luckerr 0.41.0

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.
Files changed (156) hide show
  1. package/README.md +267 -0
  2. package/README.zh-CN.md +237 -0
  3. package/dashboard/app.css +3022 -0
  4. package/dashboard/dist/app.js +30137 -0
  5. package/dashboard/dist/app.js.map +1 -0
  6. package/dashboard/dist/vendor-hljs.css +10 -0
  7. package/dashboard/dist/vendor-uplot.css +1 -0
  8. package/dashboard/index.html +19 -0
  9. package/data/deepseek-tokenizer.json.gz +0 -0
  10. package/dist/cli/acp-EOOAI4F5.js +712 -0
  11. package/dist/cli/acp-EOOAI4F5.js.map +1 -0
  12. package/dist/cli/chat-7J6GJXL2.js +51 -0
  13. package/dist/cli/chat-7J6GJXL2.js.map +1 -0
  14. package/dist/cli/chunk-2425HK6U.js +54 -0
  15. package/dist/cli/chunk-2425HK6U.js.map +1 -0
  16. package/dist/cli/chunk-25T6CVUP.js +172 -0
  17. package/dist/cli/chunk-25T6CVUP.js.map +1 -0
  18. package/dist/cli/chunk-2UQP6H6T.js +31 -0
  19. package/dist/cli/chunk-2UQP6H6T.js.map +1 -0
  20. package/dist/cli/chunk-56OAJILV.js +47 -0
  21. package/dist/cli/chunk-56OAJILV.js.map +1 -0
  22. package/dist/cli/chunk-5FTI4KXH.js +150 -0
  23. package/dist/cli/chunk-5FTI4KXH.js.map +1 -0
  24. package/dist/cli/chunk-5TWQD73O.js +2846 -0
  25. package/dist/cli/chunk-5TWQD73O.js.map +1 -0
  26. package/dist/cli/chunk-653BOCMK.js +40 -0
  27. package/dist/cli/chunk-653BOCMK.js.map +1 -0
  28. package/dist/cli/chunk-6ALJTWWQ.js +2663 -0
  29. package/dist/cli/chunk-6ALJTWWQ.js.map +1 -0
  30. package/dist/cli/chunk-6DRKA2IL.js +341 -0
  31. package/dist/cli/chunk-6DRKA2IL.js.map +1 -0
  32. package/dist/cli/chunk-6LV63NJV.js +634 -0
  33. package/dist/cli/chunk-6LV63NJV.js.map +1 -0
  34. package/dist/cli/chunk-74EX7SUH.js +25293 -0
  35. package/dist/cli/chunk-74EX7SUH.js.map +1 -0
  36. package/dist/cli/chunk-74U5RKTX.js +60611 -0
  37. package/dist/cli/chunk-74U5RKTX.js.map +1 -0
  38. package/dist/cli/chunk-ANJSUESV.js +143 -0
  39. package/dist/cli/chunk-ANJSUESV.js.map +1 -0
  40. package/dist/cli/chunk-DB2Z3DKZ.js +54 -0
  41. package/dist/cli/chunk-DB2Z3DKZ.js.map +1 -0
  42. package/dist/cli/chunk-DDIH3ZAA.js +400 -0
  43. package/dist/cli/chunk-DDIH3ZAA.js.map +1 -0
  44. package/dist/cli/chunk-ELN3Z3B2.js +621 -0
  45. package/dist/cli/chunk-ELN3Z3B2.js.map +1 -0
  46. package/dist/cli/chunk-F6BSQJGV.js +200 -0
  47. package/dist/cli/chunk-F6BSQJGV.js.map +1 -0
  48. package/dist/cli/chunk-FET2UAG5.js +246 -0
  49. package/dist/cli/chunk-FET2UAG5.js.map +1 -0
  50. package/dist/cli/chunk-FFJ342IJ.js +190 -0
  51. package/dist/cli/chunk-FFJ342IJ.js.map +1 -0
  52. package/dist/cli/chunk-GB3247B6.js +130 -0
  53. package/dist/cli/chunk-GB3247B6.js.map +1 -0
  54. package/dist/cli/chunk-HC2J4U3G.js +373 -0
  55. package/dist/cli/chunk-HC2J4U3G.js.map +1 -0
  56. package/dist/cli/chunk-HRUZAIHQ.js +42 -0
  57. package/dist/cli/chunk-HRUZAIHQ.js.map +1 -0
  58. package/dist/cli/chunk-J3ZJFUDL.js +308 -0
  59. package/dist/cli/chunk-J3ZJFUDL.js.map +1 -0
  60. package/dist/cli/chunk-J5XJHLWM.js +55 -0
  61. package/dist/cli/chunk-J5XJHLWM.js.map +1 -0
  62. package/dist/cli/chunk-JFGLMRZ6.js +160 -0
  63. package/dist/cli/chunk-JFGLMRZ6.js.map +1 -0
  64. package/dist/cli/chunk-JMBMLOBP.js +26 -0
  65. package/dist/cli/chunk-JMBMLOBP.js.map +1 -0
  66. package/dist/cli/chunk-JMWHXZEL.js +551 -0
  67. package/dist/cli/chunk-JMWHXZEL.js.map +1 -0
  68. package/dist/cli/chunk-KEQGPJBO.js +209 -0
  69. package/dist/cli/chunk-KEQGPJBO.js.map +1 -0
  70. package/dist/cli/chunk-M4K6U37F.js +232 -0
  71. package/dist/cli/chunk-M4K6U37F.js.map +1 -0
  72. package/dist/cli/chunk-MIJI2WMN.js +95 -0
  73. package/dist/cli/chunk-MIJI2WMN.js.map +1 -0
  74. package/dist/cli/chunk-MPAO3JNR.js +128 -0
  75. package/dist/cli/chunk-MPAO3JNR.js.map +1 -0
  76. package/dist/cli/chunk-PZOFBEDC.js +873 -0
  77. package/dist/cli/chunk-PZOFBEDC.js.map +1 -0
  78. package/dist/cli/chunk-RAILYQLN.js +46 -0
  79. package/dist/cli/chunk-RAILYQLN.js.map +1 -0
  80. package/dist/cli/chunk-RR35VQVT.js +90 -0
  81. package/dist/cli/chunk-RR35VQVT.js.map +1 -0
  82. package/dist/cli/chunk-RRA7VPW4.js +417 -0
  83. package/dist/cli/chunk-RRA7VPW4.js.map +1 -0
  84. package/dist/cli/chunk-RU36QVN3.js +452 -0
  85. package/dist/cli/chunk-RU36QVN3.js.map +1 -0
  86. package/dist/cli/chunk-RUBIINXR.js +1819 -0
  87. package/dist/cli/chunk-RUBIINXR.js.map +1 -0
  88. package/dist/cli/chunk-S4XVGLRW.js +499 -0
  89. package/dist/cli/chunk-S4XVGLRW.js.map +1 -0
  90. package/dist/cli/chunk-TUK7OWJA.js +51 -0
  91. package/dist/cli/chunk-TUK7OWJA.js.map +1 -0
  92. package/dist/cli/chunk-VALDDV76.js +580 -0
  93. package/dist/cli/chunk-VALDDV76.js.map +1 -0
  94. package/dist/cli/chunk-WQOGPYGN.js +11390 -0
  95. package/dist/cli/chunk-WQOGPYGN.js.map +1 -0
  96. package/dist/cli/chunk-WREKDFXT.js +34320 -0
  97. package/dist/cli/chunk-WREKDFXT.js.map +1 -0
  98. package/dist/cli/chunk-Y7XQU2EL.js +270 -0
  99. package/dist/cli/chunk-Y7XQU2EL.js.map +1 -0
  100. package/dist/cli/chunk-YBVCZJU4.js +54 -0
  101. package/dist/cli/chunk-YBVCZJU4.js.map +1 -0
  102. package/dist/cli/chunk-YLIHDXUQ.js +749 -0
  103. package/dist/cli/chunk-YLIHDXUQ.js.map +1 -0
  104. package/dist/cli/chunk-YV5XXFD7.js +767 -0
  105. package/dist/cli/chunk-YV5XXFD7.js.map +1 -0
  106. package/dist/cli/chunk-ZRCNIYRQ.js +101 -0
  107. package/dist/cli/chunk-ZRCNIYRQ.js.map +1 -0
  108. package/dist/cli/code-CRKVCMFZ.js +155 -0
  109. package/dist/cli/code-CRKVCMFZ.js.map +1 -0
  110. package/dist/cli/commands-QLMD3T7B.js +356 -0
  111. package/dist/cli/commands-QLMD3T7B.js.map +1 -0
  112. package/dist/cli/commit-53PP32NC.js +293 -0
  113. package/dist/cli/commit-53PP32NC.js.map +1 -0
  114. package/dist/cli/desktop-R6W5CLJ5.js +1046 -0
  115. package/dist/cli/desktop-R6W5CLJ5.js.map +1 -0
  116. package/dist/cli/devtools-YECO25QO.js +3719 -0
  117. package/dist/cli/devtools-YECO25QO.js.map +1 -0
  118. package/dist/cli/diff-LYNRCJZE.js +166 -0
  119. package/dist/cli/diff-LYNRCJZE.js.map +1 -0
  120. package/dist/cli/doctor-5IBP4R5J.js +28 -0
  121. package/dist/cli/doctor-5IBP4R5J.js.map +1 -0
  122. package/dist/cli/events-QN6KLN2V.js +340 -0
  123. package/dist/cli/events-QN6KLN2V.js.map +1 -0
  124. package/dist/cli/index.js +3500 -0
  125. package/dist/cli/index.js.map +1 -0
  126. package/dist/cli/mcp-FGKEH7RG.js +277 -0
  127. package/dist/cli/mcp-FGKEH7RG.js.map +1 -0
  128. package/dist/cli/mcp-browse-YCND4NWT.js +178 -0
  129. package/dist/cli/mcp-browse-YCND4NWT.js.map +1 -0
  130. package/dist/cli/mcp-inspect-V34J3VX5.js +143 -0
  131. package/dist/cli/mcp-inspect-V34J3VX5.js.map +1 -0
  132. package/dist/cli/package.json +3 -0
  133. package/dist/cli/prompt-I775PNKT.js +16 -0
  134. package/dist/cli/prompt-I775PNKT.js.map +1 -0
  135. package/dist/cli/prune-sessions-KGIIYD3P.js +44 -0
  136. package/dist/cli/prune-sessions-KGIIYD3P.js.map +1 -0
  137. package/dist/cli/replay-RDXLUAOE.js +292 -0
  138. package/dist/cli/replay-RDXLUAOE.js.map +1 -0
  139. package/dist/cli/run-RCAC2RYW.js +223 -0
  140. package/dist/cli/run-RCAC2RYW.js.map +1 -0
  141. package/dist/cli/server-FFU6TLYJ.js +3658 -0
  142. package/dist/cli/server-FFU6TLYJ.js.map +1 -0
  143. package/dist/cli/sessions-QT26MQAE.js +107 -0
  144. package/dist/cli/sessions-QT26MQAE.js.map +1 -0
  145. package/dist/cli/setup-VV4WKXHV.js +767 -0
  146. package/dist/cli/setup-VV4WKXHV.js.map +1 -0
  147. package/dist/cli/stats-JVZPQWAN.js +15 -0
  148. package/dist/cli/stats-JVZPQWAN.js.map +1 -0
  149. package/dist/cli/update-KYI3OVJP.js +15 -0
  150. package/dist/cli/update-KYI3OVJP.js.map +1 -0
  151. package/dist/cli/version-ANYORXTI.js +34 -0
  152. package/dist/cli/version-ANYORXTI.js.map +1 -0
  153. package/dist/index.d.ts +2557 -0
  154. package/dist/index.js +15000 -0
  155. package/dist/index.js.map +1 -0
  156. package/package.json +106 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/retry.ts","../src/providers/profiles/deepseek.ts","../src/providers/profiles/siliconflow.ts","../src/providers/profiles/modelscope.ts","../src/providers/profiles/zhipu.ts","../src/providers/profiles/moonshot.ts","../src/providers/profiles/qwen.ts","../src/providers/profiles/openai.ts","../src/providers/profiles/anthropic.ts","../src/providers/profiles/groq.ts","../src/providers/profiles/openrouter.ts","../src/providers/registry.ts","../src/core/pause-gate.ts","../src/hooks.ts","../src/config.ts","../src/cli/ui/theme/tokens.ts","../src/index/config.ts","../src/i18n/EN.ts","../src/i18n/zh-CN.ts","../src/i18n/index.ts","../src/tokenizer.ts","../src/repair/flatten.ts","../src/tools.ts","../src/mcp/latency.ts","../src/mcp/registry.ts","../src/memory/session.ts","../src/telemetry/stats.ts","../src/context-manager.ts","../src/core/inflight.ts","../src/loop/errors.ts","../src/loop/escalation.ts","../src/loop/thinking.ts","../src/loop/messages.ts","../src/loop/force-summary.ts","../src/loop/shrink.ts","../src/loop/healing.ts","../src/loop/hook-events.ts","../src/loop/read-only-loop-tracker.ts","../src/loop/turn-failure-tracker.ts","../src/memory/runtime.ts","../src/repair/scavenge.ts","../src/repair/storm.ts","../src/repair/truncation.ts","../src/repair/index.ts","../src/loop.ts","../src/at-mentions.ts","../src/gitignore.ts","../src/memory/project.ts","../src/memory/user.ts","../src/frontmatter.ts","../src/skills.ts","../src/prompt-fragments.ts","../src/tools/filesystem.ts","../src/tools/fs/edit.ts","../src/tools/fs/glob.ts","../src/tools/fs/outline.ts","../src/tools/fs/search.ts","../src/tools/memory.ts","../src/tools/choice.ts","../src/tools/plan-errors.ts","../src/tools/plan-core.ts","../src/tools/todo.ts","../src/tools/subagent-types.ts","../src/tools/subagent.ts","../src/tools/shell.ts","../src/tools/jobs.ts","../src/tools/shell/exec.ts","../src/tools/shell-chain.ts","../src/tools/shell/parse.ts","../src/tools/web.ts","../src/env.ts","../src/transcript/log.ts","../src/transcript/replay.ts","../src/transcript/diff.ts","../src/version.ts","../src/mcp/types.ts","../src/mcp/client.ts","../src/mcp/stdio.ts","../src/mcp/sse.ts","../src/mcp/streamable-http.ts","../src/mcp/shell-split.ts","../src/mcp/spec.ts","../src/mcp/inspect.ts","../src/code/edit-blocks.ts","../src/code/prompt.ts","../src/telemetry/usage.ts"],"sourcesContent":["import { type EventSourceMessage, createParser } from \"eventsource-parser\";\nimport { type RetryOptions, fetchWithRetry } from \"./retry.js\";\nimport { defaultProvider, resolveProvider } from \"./providers/registry.js\";\nimport type { ProviderProfile } from \"./providers/types.js\";\nimport type { ChatMessage, ChatRequestOptions, RawUsage, ToolCall, ToolSpec } from \"./types.js\";\n\nexport class Usage {\n constructor(\n public promptTokens = 0,\n public completionTokens = 0,\n public totalTokens = 0,\n public promptCacheHitTokens = 0,\n public promptCacheMissTokens = 0,\n ) {}\n\n get cacheHitRatio(): number {\n const denom = this.promptCacheHitTokens + this.promptCacheMissTokens;\n return denom > 0 ? this.promptCacheHitTokens / denom : 0;\n }\n\n static fromApi(raw: RawUsage | undefined | null): Usage {\n const u = raw ?? {};\n return new Usage(\n u.prompt_tokens ?? 0,\n u.completion_tokens ?? 0,\n u.total_tokens ?? 0,\n u.prompt_cache_hit_tokens ?? 0,\n u.prompt_cache_miss_tokens ?? 0,\n );\n }\n}\n\nexport interface ChatResponse {\n content: string;\n reasoningContent: string | null;\n toolCalls: ToolCall[];\n usage: Usage;\n raw: unknown;\n}\n\nexport interface StreamChunk {\n contentDelta?: string;\n reasoningDelta?: string;\n toolCallDelta?: { index: number; id?: string; name?: string; argumentsDelta?: string };\n usage?: Usage;\n finishReason?: string;\n raw: any;\n}\n\nexport interface BalanceInfo {\n currency: string;\n total_balance: string;\n granted_balance?: string;\n topped_up_balance?: string;\n}\n\nexport interface UserBalance {\n is_available: boolean;\n balance_infos: BalanceInfo[];\n}\n\n/** Largest `total_balance` wins — the wallet the user actually paid for and expects to see ticking down. */\nexport function pickPrimaryBalance(infos: ReadonlyArray<BalanceInfo>): BalanceInfo | null {\n if (infos.length === 0) return null;\n let best = infos[0]!;\n for (let i = 1; i < infos.length; i++) {\n if (Number(infos[i]!.total_balance) > Number(best.total_balance)) best = infos[i]!;\n }\n return best;\n}\n\nexport interface ModelInfo {\n id: string;\n object: \"model\";\n owned_by: string;\n}\n\nexport interface ModelList {\n object: \"list\";\n data: ModelInfo[];\n}\n\n// ---------------------------------------------------------------------------\n// OpenAICompatClient — provider-agnostic chat client\n// ---------------------------------------------------------------------------\n\nexport interface OpenAICompatClientOptions {\n /**\n * API key. Falls back to the provider's env var, then to\n * the legacy `DEEPSEEK_API_KEY` env var for backwards compat.\n */\n apiKey?: string;\n /**\n * Base URL override. Falls back to the provider's endpoint,\n * then to the legacy `DEEPSEEK_BASE_URL` env var.\n */\n baseUrl?: string;\n /** Request timeout in ms. Default: 660_000 (11 min). */\n timeoutMs?: number;\n /** Custom fetch implementation. */\n fetch?: typeof fetch;\n /** Retry configuration. `{ maxAttempts: 1 }` disables retries. */\n retry?: RetryOptions;\n /**\n * Provider profile. If not given, we resolve it from the\n * default model or fall back to the DeepSeek profile.\n */\n provider?: ProviderProfile;\n /**\n * If you know the model id upfront, we'll auto-resolve the\n * provider. Only used when `provider` is not passed.\n */\n model?: string;\n}\n\n/**\n * Generic OpenAI-compatible chat client.\n *\n * Works with any provider that speaks `/chat/completions` with\n * Bearer-auth and SSE streaming (DeepSeek, SiliconFlow, ModelScope,\n * Zhipu, Moonshot, Qwen, OpenAI, Groq, OpenRouter, etc.).\n *\n * For Anthropic's non-OpenAI-compatible Messages API, a separate\n * adapter will be needed.\n */\nexport class OpenAICompatClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeoutMs: number;\n readonly retry: RetryOptions;\n readonly provider: ProviderProfile;\n private readonly _fetch: typeof fetch;\n\n constructor(opts: OpenAICompatClientOptions = {}) {\n // Resolve the provider profile.\n let provider: ProviderProfile | undefined = opts.provider;\n if (!provider && opts.model) {\n provider = resolveProvider(opts.model);\n }\n if (!provider) {\n provider = defaultProvider();\n }\n this.provider = provider;\n\n // API key resolution order:\n // 1. Explicit `apiKey` option\n // 2. Provider-specific env var (e.g. SILICONFLOW_API_KEY)\n // 3. Legacy DEEPSEEK_API_KEY (backwards compat)\n const apiKey =\n opts.apiKey ??\n process.env[provider.auth.envKey] ??\n process.env.DEEPSEEK_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n `${provider.auth.envKey} is not set. Put it in .env or pass apiKey to the client.`,\n );\n }\n this.apiKey = apiKey;\n\n // Base URL resolution:\n // 1. Explicit `baseUrl` option\n // 2. Legacy DEEPSEEK_BASE_URL env var (backwards compat)\n // 3. Provider's default chat endpoint\n let url = opts.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? provider.endpoints.chat;\n while (url.endsWith(\"/\")) url = url.slice(0, -1);\n this.baseUrl = url;\n\n this.timeoutMs = opts.timeoutMs ?? 660_000;\n this._fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry ?? {};\n }\n\n // -----------------------------------------------------------------------\n // Auth header construction\n // -----------------------------------------------------------------------\n\n private authHeader(): Record<string, string> {\n const auth = this.provider.auth;\n const header = auth.header ?? \"Authorization\";\n const prefix = auth.prefix ?? \"Bearer \";\n const value = prefix ? `${prefix}${this.apiKey}` : this.apiKey;\n\n const headers: Record<string, string> = { [header]: value };\n\n // Extra provider-level headers (e.g. Anthropic version, OpenRouter referer).\n if (this.provider.extraHeaders) {\n for (const [k, v] of Object.entries(this.provider.extraHeaders)) {\n headers[k] = v;\n }\n }\n\n return headers;\n }\n\n // -----------------------------------------------------------------------\n // Payload construction — respects the provider's thinking transport\n // -----------------------------------------------------------------------\n\n private buildPayload(opts: ChatRequestOptions, stream: boolean): Record<string, unknown> {\n const payload: Record<string, unknown> = {\n model: opts.model,\n messages: opts.messages,\n stream,\n };\n if (opts.tools?.length) payload.tools = opts.tools;\n if (opts.temperature !== undefined) payload.temperature = opts.temperature;\n if (opts.maxTokens !== undefined) payload.max_tokens = opts.maxTokens;\n if (opts.responseFormat) payload.response_format = opts.responseFormat;\n\n // Thinking / reasoning mode injection.\n // Only add thinking fields when the model is known to support it,\n // OR when the toggle is explicitly \"disabled\" (deepseek-chat needs\n // thinking=disabled to opt out of the default).\n const thinking = this.provider.thinking;\n const modelSupportsThinking =\n !thinking.thinkingModels || thinking.thinkingModels.includes(opts.model);\n\n if (\n thinking.transport === \"extra_body\" &&\n opts.thinking &&\n (modelSupportsThinking || opts.thinking === \"disabled\")\n ) {\n // DeepSeek-style: extra_body.thinking.type\n payload.extra_body = {\n ...((payload.extra_body as Record<string, unknown>) ?? {}),\n thinking: { type: opts.thinking },\n };\n }\n if (thinking.transport === \"reasoning_effort\" && opts.reasoningEffort && modelSupportsThinking) {\n // OpenAI-style: top-level reasoning_effort\n payload.reasoning_effort = opts.reasoningEffort;\n }\n if (thinking.transport === \"include\" && opts.thinking === \"enabled\" && modelSupportsThinking) {\n // Anthropic-style: thinking: { type: \"enabled\", budget_tokens: 16000 }\n payload.thinking = { type: \"enabled\", budget_tokens: 16000 };\n }\n\n // DeepSeek reasoning_effort also works through extra_body.\n if (opts.reasoningEffort && thinking.transport !== \"reasoning_effort\") {\n payload.reasoning_effort = opts.reasoningEffort;\n }\n\n // Merge provider-level extra body fields.\n if (this.provider.extraBody) {\n for (const [k, v] of Object.entries(this.provider.extraBody)) {\n if (!(k in payload)) payload[k] = v;\n }\n }\n\n return payload;\n }\n\n // -----------------------------------------------------------------------\n // Balance\n // -----------------------------------------------------------------------\n\n /** Returns null on failure so callers can degrade — session must keep working without balance UI. */\n async getBalance(opts: { signal?: AbortSignal } = {}): Promise<UserBalance | null> {\n const ep = this.provider.endpoints.balance;\n if (!ep) return null;\n\n try {\n const resp = await this._fetch(`${this.baseUrl}${ep}`, {\n method: \"GET\",\n headers: this.authHeader(),\n signal: opts.signal,\n });\n if (!resp.ok) return null;\n const data = (await resp.json()) as UserBalance;\n if (!data || !Array.isArray(data.balance_infos)) return null;\n return data;\n } catch {\n return null;\n }\n }\n\n // -----------------------------------------------------------------------\n // Model listing\n // -----------------------------------------------------------------------\n\n /** Returns null on failure — callers fall back to the provider's static model list. */\n async listModels(opts: { signal?: AbortSignal } = {}): Promise<ModelList | null> {\n const ep = this.provider.endpoints.models;\n if (!ep) return null;\n\n try {\n const resp = await this._fetch(`${this.baseUrl}${ep}`, {\n method: \"GET\",\n headers: this.authHeader(),\n signal: opts.signal,\n });\n if (!resp.ok) return null;\n const data = (await resp.json()) as ModelList;\n if (!data || !Array.isArray(data.data)) return null;\n return data;\n } catch {\n return null;\n }\n }\n\n // -----------------------------------------------------------------------\n // Chat (non-streaming)\n // -----------------------------------------------------------------------\n\n async chat(opts: ChatRequestOptions): Promise<ChatResponse> {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n const signal = opts.signal ?? ctrl.signal;\n\n try {\n const resp = await fetchWithRetry(\n this._fetch,\n `${this.baseUrl}/chat/completions`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeader(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(this.buildPayload(opts, false)),\n signal,\n },\n { ...this.retry, signal },\n );\n if (!resp.ok) {\n throw new Error(\n `${this.provider.label} ${resp.status}: ${await resp.text()}`,\n );\n }\n const data: any = await resp.json();\n const choice = data.choices?.[0]?.message ?? {};\n return {\n content: choice.content ?? \"\",\n reasoningContent: choice.reasoning_content ?? null,\n toolCalls: choice.tool_calls ?? [],\n usage: Usage.fromApi(data.usage),\n raw: data,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n // -----------------------------------------------------------------------\n // Chat (streaming)\n // -----------------------------------------------------------------------\n\n async *stream(opts: ChatRequestOptions): AsyncGenerator<StreamChunk> {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n const signal = opts.signal ?? ctrl.signal;\n\n let resp: Response;\n try {\n resp = await fetchWithRetry(\n this._fetch,\n `${this.baseUrl}/chat/completions`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeader(),\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify(this.buildPayload(opts, true)),\n signal,\n },\n { ...this.retry, signal },\n );\n } catch (err) {\n clearTimeout(timer);\n throw err;\n }\n if (!resp.ok || !resp.body) {\n clearTimeout(timer);\n throw new Error(\n `${this.provider.label} ${resp.status}: ${await resp.text().catch(() => \"\")}`,\n );\n }\n\n const queue: StreamChunk[] = [];\n let done = false;\n const parser = createParser({\n onEvent: (ev: EventSourceMessage) => {\n if (!ev.data || ev.data === \"[DONE]\") {\n done = true;\n return;\n }\n try {\n const json = JSON.parse(ev.data);\n const delta = json.choices?.[0]?.delta ?? {};\n const finishReason = json.choices?.[0]?.finish_reason ?? undefined;\n const chunk: StreamChunk = { raw: json, finishReason };\n if (typeof delta.content === \"string\" && delta.content.length > 0) {\n chunk.contentDelta = delta.content;\n }\n if (\n typeof delta.reasoning_content === \"string\" &&\n delta.reasoning_content.length > 0\n ) {\n chunk.reasoningDelta = delta.reasoning_content;\n }\n if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {\n const tc = delta.tool_calls[0];\n chunk.toolCallDelta = {\n index: tc.index ?? 0,\n id: tc.id,\n name: tc.function?.name,\n argumentsDelta: tc.function?.arguments,\n };\n }\n if (json.usage) {\n chunk.usage = Usage.fromApi(json.usage);\n }\n queue.push(chunk);\n } catch {\n /* skip malformed sse frame */\n }\n },\n });\n\n const reader = resp.body.getReader();\n const decoder = new TextDecoder();\n try {\n while (true) {\n if (queue.length > 0) {\n yield queue.shift()!;\n continue;\n }\n if (done) break;\n const { value, done: streamDone } = await reader.read();\n if (streamDone) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\n while (queue.length > 0) yield queue.shift()!;\n } finally {\n clearTimeout(timer);\n reader.releaseLock();\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Backwards-compatible DeepSeekClient alias\n// ---------------------------------------------------------------------------\n\nexport interface DeepSeekClientOptions {\n apiKey?: string;\n baseUrl?: string;\n timeoutMs?: number;\n fetch?: typeof fetch;\n retry?: RetryOptions;\n}\n\n/**\n * @deprecated Use `OpenAICompatClient` with a provider profile instead.\n * Kept for backwards compatibility — all existing code that constructs\n * `new DeepSeekClient()` continues to work.\n */\nexport class DeepSeekClient extends OpenAICompatClient {\n constructor(opts: DeepSeekClientOptions = {}) {\n // Preserve the old error message for backwards compat.\n const apiKey = opts.apiKey ?? process.env.DEEPSEEK_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"DEEPSEEK_API_KEY is not set. Put it in .env or pass apiKey to DeepSeekClient.\",\n );\n }\n super({\n apiKey,\n baseUrl: opts.baseUrl,\n timeoutMs: opts.timeoutMs,\n fetch: opts.fetch,\n retry: opts.retry,\n provider: defaultProvider(),\n });\n }\n}\n\nexport type { ChatMessage, ToolCall, ToolSpec };\n","/** No retry on aborts or mid-stream body errors — re-billing the user for desynced output is worse than failing. */\n\nexport interface RetryOptions {\n /** Maximum total attempts (including the first). Default 4. */\n maxAttempts?: number;\n /** Initial backoff in ms. Doubles each retry, with jitter. Default 500. */\n initialBackoffMs?: number;\n /** Upper bound on any single backoff delay. Default 10000 (10s). */\n maxBackoffMs?: number;\n /** HTTP statuses to treat as retryable. Default [408, 429, 500, 502, 503, 504]. */\n retryableStatuses?: readonly number[];\n /** Abort signal; we do NOT retry once aborted. */\n signal?: AbortSignal;\n /** Telemetry hook — called before each wait. */\n onRetry?: (info: RetryInfo) => void;\n}\n\nexport interface RetryInfo {\n attempt: number;\n reason: string;\n waitMs: number;\n}\n\nconst DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504] as const;\n\nexport async function fetchWithRetry(\n fetchFn: typeof fetch,\n url: string,\n init: RequestInit,\n opts: RetryOptions = {},\n): Promise<Response> {\n const maxAttempts = opts.maxAttempts ?? 4;\n const initial = opts.initialBackoffMs ?? 500;\n const cap = opts.maxBackoffMs ?? 10_000;\n const retryable = new Set(opts.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES);\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (opts.signal?.aborted) throw new Error(\"aborted\");\n\n try {\n const resp = await fetchFn(url, init);\n\n // Success or non-retryable failure: return as-is.\n if (resp.ok || !retryable.has(resp.status)) return resp;\n\n // Retryable but out of attempts: return the last response so the caller\n // can surface the status to the user.\n if (attempt === maxAttempts - 1) return resp;\n\n // Drain the body so the connection can be reused on the next attempt.\n await resp.text().catch(() => undefined);\n\n const waitMs = computeWait(attempt, initial, cap, resp.headers.get(\"Retry-After\"));\n opts.onRetry?.({ attempt: attempt + 1, reason: `http ${resp.status}`, waitMs });\n await sleep(waitMs, opts.signal);\n } catch (err) {\n lastError = err;\n // Respect explicit aborts — do not retry.\n if (isAbortError(err) || opts.signal?.aborted) throw err;\n if (attempt === maxAttempts - 1) throw err;\n\n const waitMs = computeWait(attempt, initial, cap, null);\n opts.onRetry?.({\n attempt: attempt + 1,\n reason: `network: ${messageOf(err)}`,\n waitMs,\n });\n await sleep(waitMs, opts.signal);\n }\n }\n\n throw lastError ?? new Error(\"fetchWithRetry: loop exited unexpectedly\");\n}\n\nfunction computeWait(\n attempt: number,\n initial: number,\n cap: number,\n retryAfter: string | null,\n): number {\n if (retryAfter) {\n const seconds = Number.parseFloat(retryAfter);\n if (Number.isFinite(seconds) && seconds > 0) {\n return Math.min(seconds * 1000, cap);\n }\n }\n const exp = initial * 2 ** attempt;\n // Jitter range [75%, 125%] to spread retries out when many clients hit 429 together.\n const jitter = exp * (0.75 + Math.random() * 0.5);\n return Math.min(Math.max(jitter, 0), cap);\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve, reject) => {\n const timer = setTimeout(resolve, ms);\n if (signal) {\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error(\"aborted\"));\n };\n if (signal.aborted) onAbort();\n else signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n });\n}\n\nfunction isAbortError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const name = (err as { name?: unknown }).name;\n return name === \"AbortError\";\n}\n\nfunction messageOf(err: unknown): string {\n if (err instanceof Error) return err.message;\n try {\n return String(err);\n } catch {\n return \"unknown error\";\n }\n}\n","/** DeepSeek provider profile. https://api.deepseek.com */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const deepseek: ProviderProfile = {\n id: \"deepseek\",\n label: \"DeepSeek\",\n auth: {\n envKey: \"DEEPSEEK_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://api.deepseek.com\",\n balance: \"/user/balance\",\n models: \"/models\",\n },\n models: [\n \"deepseek-v4-flash\",\n \"deepseek-v4-pro\",\n \"deepseek-chat\",\n \"deepseek-reasoner\",\n ],\n defaultModel: \"deepseek-v4-flash\",\n escalationModel: \"deepseek-v4-pro\",\n pricing: {\n models: {\n \"deepseek-v4-flash\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n \"deepseek-v4-pro\": { inputCacheHit: 0.139, inputCacheMiss: 1.667, output: 3.333 },\n \"deepseek-chat\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n \"deepseek-reasoner\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n },\n defaults: { inputCacheHit: 0.139, inputCacheMiss: 1.667, output: 3.333 },\n },\n thinking: {\n transport: \"extra_body\",\n thinkingModels: [\"deepseek-v4-flash\", \"deepseek-v4-pro\", \"deepseek-reasoner\"],\n defaultEnabled: true,\n },\n contextTokens: {\n \"deepseek-v4-flash\": 1_000_000,\n \"deepseek-v4-pro\": 1_000_000,\n \"deepseek-chat\": 1_000_000,\n \"deepseek-reasoner\": 1_000_000,\n },\n defaultContextTokens: 131_072,\n};\n","/** 硅基流动 (SiliconFlow) provider profile. OpenAI-compatible API. https://docs.siliconflow.cn */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const siliconflow: ProviderProfile = {\n id: \"siliconflow\",\n label: \"硅基流动 (SiliconFlow)\",\n auth: {\n envKey: \"SILICONFLOW_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://api.siliconflow.cn/v1\",\n balance: \"/user/info\", // returns balance in user info\n models: \"/models\",\n },\n models: [\n \"deepseek-ai/DeepSeek-V3\",\n \"deepseek-ai/DeepSeek-R1\",\n \"Qwen/Qwen2.5-72B-Instruct\",\n \"Qwen/Qwen2.5-32B-Instruct\",\n \"Qwen/Qwen2.5-14B-Instruct\",\n \"Qwen/Qwen2.5-7B-Instruct\",\n \"meta-llama/Llama-3.3-70B-Instruct\",\n \"meta-llama/Llama-3.1-8B-Instruct\",\n \"Pro/Llama-3.3-70B-Instruct\",\n \"THUDM/glm-4-9b-chat\",\n ],\n defaultModel: \"deepseek-ai/DeepSeek-V3\",\n pricing: {\n models: {\n // DeepSeek models on SiliconFlow — ¥1.00 / 1M input, ¥2.00 / 1M output ≈ $0.14 / $0.28\n \"deepseek-ai/DeepSeek-V3\": { inputCacheHit: 0.139, inputCacheMiss: 0.139, output: 0.278 },\n \"deepseek-ai/DeepSeek-R1\": { inputCacheHit: 0.556, inputCacheMiss: 0.556, output: 1.111 },\n // Qwen series — ¥0.50–4.00 / 1M tokens\n \"Qwen/Qwen2.5-72B-Instruct\": { inputCacheHit: 0.556, inputCacheMiss: 0.556, output: 0.556 },\n \"Qwen/Qwen2.5-32B-Instruct\": { inputCacheHit: 0.278, inputCacheMiss: 0.278, output: 0.278 },\n \"Qwen/Qwen2.5-14B-Instruct\": { inputCacheHit: 0.139, inputCacheMiss: 0.139, output: 0.139 },\n \"Qwen/Qwen2.5-7B-Instruct\": { inputCacheHit: 0.069, inputCacheMiss: 0.069, output: 0.069 },\n // Llama\n \"meta-llama/Llama-3.3-70B-Instruct\": { inputCacheHit: 0.556, inputCacheMiss: 0.556, output: 0.556 },\n \"meta-llama/Llama-3.1-8B-Instruct\": { inputCacheHit: 0.069, inputCacheMiss: 0.069, output: 0.069 },\n \"Pro/Llama-3.3-70B-Instruct\": { inputCacheHit: 0.833, inputCacheMiss: 0.833, output: 0.833 },\n // GLM\n \"THUDM/glm-4-9b-chat\": { inputCacheHit: 0.069, inputCacheMiss: 0.069, output: 0.069 },\n },\n defaults: { inputCacheHit: 0.139, inputCacheMiss: 0.139, output: 0.278 },\n },\n thinking: {\n transport: \"none\",\n defaultEnabled: false,\n },\n defaultContextTokens: 131_072,\n};\n","/** 魔塔社区 (ModelScope) provider profile. Alibaba model hub, OpenAI-compatible. */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const modelscope: ProviderProfile = {\n id: \"modelscope\",\n label: \"魔塔社区 (ModelScope)\",\n auth: {\n envKey: \"MODELSCOPE_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://api-inference.modelscope.cn/v1\",\n balance: null,\n models: \"/models\",\n },\n models: [\n \"Qwen/Qwen2.5-72B-Instruct\",\n \"Qwen/Qwen2.5-32B-Instruct\",\n \"Qwen/Qwen2.5-14B-Instruct\",\n \"Qwen/Qwen2.5-7B-Instruct\",\n \"deepseek-ai/DeepSeek-V3\",\n \"deepseek-ai/DeepSeek-R1\",\n \"LLM-Research/Meta-Llama-3.1-70B-Instruct\",\n ],\n defaultModel: \"Qwen/Qwen2.5-72B-Instruct\",\n pricing: {\n defaults: { inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n },\n thinking: {\n transport: \"none\",\n defaultEnabled: false,\n },\n defaultContextTokens: 131_072,\n};\n","/** 智谱AI (Zhipu) provider profile. GLM series. https://open.bigmodel.cn */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const zhipu: ProviderProfile = {\n id: \"zhipu\",\n label: \"智谱AI (Zhipu)\",\n auth: {\n envKey: \"ZHIPU_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://open.bigmodel.cn/api/paas/v4\",\n balance: null,\n models: \"/models\",\n },\n models: [\n \"glm-4-plus\",\n \"glm-4-flash\",\n \"glm-4-air\",\n \"glm-4-long\",\n \"glm-4\",\n \"glm-4v-plus\",\n \"glm-4v-flash\",\n \"glm-4v\",\n ],\n defaultModel: \"glm-4-flash\",\n pricing: {\n models: {\n \"glm-4-plus\": { inputCacheHit: 6.944, inputCacheMiss: 6.944, output: 6.944 },\n \"glm-4-flash\": { inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n \"glm-4-air\": { inputCacheHit: 0.139, inputCacheMiss: 0.139, output: 0.139 },\n \"glm-4-long\": { inputCacheHit: 0.139, inputCacheMiss: 0.139, output: 0.139 },\n \"glm-4\": { inputCacheHit: 6.944, inputCacheMiss: 6.944, output: 6.944 },\n \"glm-4v-plus\": { inputCacheHit: 6.944, inputCacheMiss: 6.944, output: 6.944 },\n \"glm-4v-flash\":{ inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n \"glm-4v\": { inputCacheHit: 6.944, inputCacheMiss: 6.944, output: 6.944 },\n },\n defaults: { inputCacheHit: 0.139, inputCacheMiss: 0.139, output: 0.139 },\n },\n thinking: {\n transport: \"none\",\n defaultEnabled: false,\n },\n contextTokens: {\n \"glm-4-plus\": 128_000,\n \"glm-4-flash\": 128_000,\n \"glm-4-air\": 128_000,\n \"glm-4-long\": 1_000_000,\n \"glm-4\": 128_000,\n \"glm-4v-plus\": 128_000,\n \"glm-4v-flash\": 128_000,\n \"glm-4v\": 128_000,\n },\n defaultContextTokens: 128_000,\n};\n","/** Moonshot (Kimi) provider profile. https://platform.moonshot.cn */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const moonshot: ProviderProfile = {\n id: \"moonshot\",\n label: \"Kimi (Moonshot)\",\n auth: {\n envKey: \"MOONSHOT_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://api.moonshot.cn/v1\",\n balance: null,\n models: \"/models\",\n },\n models: [\n \"moonshot-v1-8k\",\n \"moonshot-v1-32k\",\n \"moonshot-v1-128k\",\n \"kimi-latest\",\n ],\n defaultModel: \"kimi-latest\",\n pricing: {\n models: {\n \"moonshot-v1-8k\": { inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n \"moonshot-v1-32k\": { inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n \"moonshot-v1-128k\": { inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n \"kimi-latest\": { inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n },\n defaults: { inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n },\n thinking: {\n transport: \"none\",\n defaultEnabled: false,\n },\n contextTokens: {\n \"moonshot-v1-8k\": 8_192,\n \"moonshot-v1-32k\": 32_768,\n \"moonshot-v1-128k\": 131_072,\n \"kimi-latest\": 131_072,\n },\n defaultContextTokens: 131_072,\n};\n","/** 通义千问 (Qwen/DashScope) provider profile. https://dashscope.aliyun.com */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const qwen: ProviderProfile = {\n id: \"qwen\",\n label: \"通义千问 (Qwen/DashScope)\",\n auth: {\n envKey: \"DASHSCOPE_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n balance: null,\n models: null,\n },\n models: [\n \"qwen-max\",\n \"qwen-plus\",\n \"qwen-turbo\",\n \"qwen-long\",\n \"qwen2.5-72b-instruct\",\n \"qwen2.5-32b-instruct\",\n \"qwen2.5-14b-instruct\",\n \"qwen2.5-7b-instruct\",\n \"qwq-plus\",\n ],\n defaultModel: \"qwen-plus\",\n pricing: {\n models: {\n \"qwen-max\": { inputCacheHit: 2.778, inputCacheMiss: 2.778, output: 8.333 },\n \"qwen-plus\": { inputCacheHit: 0.278, inputCacheMiss: 0.278, output: 0.833 },\n \"qwen-turbo\": { inputCacheHit: 0.042, inputCacheMiss: 0.042, output: 0.167 },\n \"qwen-long\": { inputCacheHit: 0.069, inputCacheMiss: 0.069, output: 0.278 },\n \"qwen2.5-72b-instruct\": { inputCacheHit: 0.556, inputCacheMiss: 0.556, output: 1.111 },\n \"qwen2.5-32b-instruct\": { inputCacheHit: 0.278, inputCacheMiss: 0.278, output: 0.556 },\n \"qwen2.5-14b-instruct\": { inputCacheHit: 0.139, inputCacheMiss: 0.139, output: 0.278 },\n \"qwen2.5-7b-instruct\": { inputCacheHit: 0.069, inputCacheMiss: 0.069, output: 0.139 },\n \"qwq-plus\": { inputCacheHit: 0.278, inputCacheMiss: 0.278, output: 0.833 },\n },\n defaults: { inputCacheHit: 0.278, inputCacheMiss: 0.278, output: 0.833 },\n },\n thinking: {\n transport: \"none\",\n defaultEnabled: false,\n },\n contextTokens: {\n \"qwen-max\": 32_768,\n \"qwen-plus\": 131_072,\n \"qwen-turbo\": 131_072,\n \"qwen-long\": 10_000_000,\n \"qwq-plus\": 131_072,\n },\n defaultContextTokens: 131_072,\n};\n","/** OpenAI provider profile. https://api.openai.com/v1 */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const openai: ProviderProfile = {\n id: \"openai\",\n label: \"OpenAI\",\n auth: {\n envKey: \"OPENAI_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://api.openai.com/v1\",\n balance: null,\n models: \"/models\",\n },\n models: [\n \"gpt-5\",\n \"gpt-5-mini\",\n \"gpt-5-nano\",\n \"gpt-4.1\",\n \"gpt-4.1-mini\",\n \"gpt-4.1-nano\",\n \"o4-mini\",\n \"o3\",\n \"o3-mini\",\n ],\n defaultModel: \"gpt-5-mini\",\n pricing: {\n models: {\n \"gpt-5\": { inputCacheHit: 0.625, inputCacheMiss: 2.50, output: 5.00 },\n \"gpt-5-mini\": { inputCacheHit: 0.075, inputCacheMiss: 0.30, output: 0.60 },\n \"gpt-5-nano\": { inputCacheHit: 0.025, inputCacheMiss: 0.10, output: 0.20 },\n \"gpt-4.1\": { inputCacheHit: 0.50, inputCacheMiss: 2.00, output: 8.00 },\n \"gpt-4.1-mini\":{ inputCacheHit: 0.10, inputCacheMiss: 0.40, output: 1.60 },\n \"gpt-4.1-nano\":{ inputCacheHit: 0.025, inputCacheMiss: 0.10, output: 0.40 },\n \"o4-mini\": { inputCacheHit: 0.275, inputCacheMiss: 1.10, output: 4.40 },\n \"o3\": { inputCacheHit: 1.25, inputCacheMiss: 5.00, output: 20.00 },\n \"o3-mini\": { inputCacheHit: 0.275, inputCacheMiss: 1.10, output: 4.40 },\n },\n defaults: { inputCacheHit: 0.30, inputCacheMiss: 1.20, output: 2.40 },\n },\n thinking: {\n transport: \"reasoning_effort\",\n thinkingModels: [\"o4-mini\", \"o3\", \"o3-mini\", \"gpt-5\"],\n defaultEnabled: true,\n },\n contextTokens: {\n \"gpt-5\": 400_000,\n \"gpt-5-mini\": 400_000,\n \"gpt-5-nano\": 400_000,\n \"gpt-4.1\": 1_000_000,\n \"gpt-4.1-mini\": 1_000_000,\n \"gpt-4.1-nano\": 1_000_000,\n \"o4-mini\": 200_000,\n \"o3\": 200_000,\n \"o3-mini\": 200_000,\n },\n defaultContextTokens: 131_072,\n};\n","/** Anthropic provider profile. Uses x-api-key auth. Anthropic's Messages API needs a translation adapter (TBD). */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const anthropic: ProviderProfile = {\n id: \"anthropic\",\n label: \"Anthropic\",\n auth: {\n envKey: \"ANTHROPIC_API_KEY\",\n header: \"x-api-key\",\n prefix: \"\",\n },\n endpoints: {\n chat: \"https://api.anthropic.com/v1\",\n balance: null,\n models: null,\n },\n models: [\n \"claude-sonnet-4-20250514\",\n \"claude-opus-4-20250514\",\n \"claude-3.5-haiku-20241022\",\n ],\n defaultModel: \"claude-sonnet-4-20250514\",\n pricing: {\n models: {\n \"claude-sonnet-4-20250514\": { inputCacheHit: 0.30, inputCacheMiss: 3.00, output: 15.00 },\n \"claude-opus-4-20250514\": { inputCacheHit: 1.50, inputCacheMiss: 15.00, output: 75.00 },\n \"claude-3.5-haiku-20241022\":{ inputCacheHit: 0.08, inputCacheMiss: 0.80, output: 4.00 },\n },\n defaults: { inputCacheHit: 0.30, inputCacheMiss: 3.00, output: 15.00 },\n },\n thinking: {\n transport: \"include\",\n thinkingModels: [\n \"claude-sonnet-4-20250514\",\n \"claude-opus-4-20250514\",\n ],\n defaultEnabled: true,\n },\n contextTokens: {\n \"claude-sonnet-4-20250514\": 200_000,\n \"claude-opus-4-20250514\": 200_000,\n \"claude-3.5-haiku-20241022\": 200_000,\n },\n defaultContextTokens: 200_000,\n extraHeaders: {\n \"anthropic-version\": \"2023-06-01\",\n },\n};\n","/** Groq provider profile. OpenAI-compatible. https://groq.com */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const groq: ProviderProfile = {\n id: \"groq\",\n label: \"Groq\",\n auth: {\n envKey: \"GROQ_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://api.groq.com/openai/v1\",\n balance: null,\n models: \"/models\",\n },\n models: [\n \"llama-3.3-70b-versatile\",\n \"llama-3.1-8b-instant\",\n \"mixtral-8x7b-32768\",\n \"gemma2-9b-it\",\n ],\n defaultModel: \"llama-3.3-70b-versatile\",\n pricing: {\n models: {\n \"llama-3.3-70b-versatile\": { inputCacheHit: 0, inputCacheMiss: 0.59, output: 0.79 },\n \"llama-3.1-8b-instant\": { inputCacheHit: 0, inputCacheMiss: 0.05, output: 0.08 },\n \"mixtral-8x7b-32768\": { inputCacheHit: 0, inputCacheMiss: 0.24, output: 0.24 },\n \"gemma2-9b-it\": { inputCacheHit: 0, inputCacheMiss: 0.03, output: 0.03 },\n },\n defaults: { inputCacheHit: 0, inputCacheMiss: 0.59, output: 0.79 },\n },\n thinking: {\n transport: \"none\",\n defaultEnabled: false,\n },\n defaultContextTokens: 131_072,\n};\n","/** OpenRouter provider profile. Unified API gateway. https://openrouter.ai */\nimport type { ProviderProfile } from \"../types.js\";\n\nexport const openrouter: ProviderProfile = {\n id: \"openrouter\",\n label: \"OpenRouter\",\n auth: {\n envKey: \"OPENROUTER_API_KEY\",\n header: \"Authorization\",\n prefix: \"Bearer \",\n },\n endpoints: {\n chat: \"https://openrouter.ai/api/v1\",\n balance: null,\n models: \"/models\",\n },\n models: [\n \"openai/gpt-5-mini\",\n \"openai/gpt-5\",\n \"anthropic/claude-sonnet-4-20250514\",\n \"anthropic/claude-opus-4-20250514\",\n \"google/gemini-2.5-pro\",\n \"google/gemini-2.5-flash\",\n \"meta-llama/llama-3.3-70b-instruct\",\n \"deepseek/deepseek-chat\",\n \"deepseek/deepseek-r1\",\n ],\n defaultModel: \"anthropic/claude-sonnet-4-20250514\",\n pricing: {\n // OpenRouter pricing is per-model and changes frequently.\n // Users should check https://openrouter.ai/models for current rates.\n defaults: { inputCacheHit: 0, inputCacheMiss: 0, output: 0 },\n },\n thinking: {\n transport: \"none\",\n defaultEnabled: false,\n },\n defaultContextTokens: 131_072,\n extraHeaders: {\n \"HTTP-Referer\": \"https://github.com/luckerr/luckerr\",\n \"X-Title\": \"Luckerr\",\n },\n};\n","/** Provider registry — maps model ids to provider profiles. Call registerProvider() to add custom providers. */\n\nimport type { ProviderProfile } from \"./types.js\";\n\n// Built-in profiles\nimport { deepseek } from \"./profiles/deepseek.js\";\nimport { siliconflow } from \"./profiles/siliconflow.js\";\nimport { modelscope } from \"./profiles/modelscope.js\";\nimport { zhipu } from \"./profiles/zhipu.js\";\nimport { moonshot } from \"./profiles/moonshot.js\";\nimport { qwen } from \"./profiles/qwen.js\";\nimport { openai } from \"./profiles/openai.js\";\nimport { anthropic } from \"./profiles/anthropic.js\";\nimport { groq } from \"./profiles/groq.js\";\nimport { openrouter } from \"./profiles/openrouter.js\";\n\nconst providers = new Map<string, ProviderProfile>();\n\n/** Map from model id → provider id, built lazily. */\nconst modelToProvider = new Map<string, string>();\n\nfunction register(p: ProviderProfile): void {\n if (providers.has(p.id)) {\n throw new Error(`Duplicate provider id: ${p.id}`);\n }\n providers.set(p.id, p);\n for (const mid of p.models) {\n if (modelToProvider.has(mid)) continue;\n modelToProvider.set(mid, p.id);\n }\n}\n\n// Register built-ins.\nregister(deepseek);\nregister(siliconflow);\nregister(modelscope);\nregister(zhipu);\nregister(moonshot);\nregister(qwen);\nregister(openai);\nregister(anthropic);\nregister(groq);\nregister(openrouter);\n\n/** Register a custom or third-party provider at runtime. */\nexport function registerProvider(profile: ProviderProfile): void {\n register(profile);\n}\n\n/** Unregister a provider by id. */\nexport function unregisterProvider(id: string): boolean {\n const p = providers.get(id);\n if (!p) return false;\n for (const mid of p.models) {\n if (modelToProvider.get(mid) === id) modelToProvider.delete(mid);\n }\n return providers.delete(id);\n}\n\n/** Get a provider profile by id. */\nexport function getProvider(id: string): ProviderProfile | undefined {\n return providers.get(id);\n}\n\n/** List all registered provider profiles. */\nexport function listProviders(): ProviderProfile[] {\n return [...providers.values()];\n}\n\n/**\n * Resolve which provider owns a given model id.\n * Returns undefined for unknown models — callers should fall back\n * to the default provider (\"deepseek\").\n */\nexport function resolveProvider(modelId: string): ProviderProfile | undefined {\n const pid = modelToProvider.get(modelId);\n if (pid) return providers.get(pid);\n\n // Prefix-based fallback for OpenRouter-style model ids.\n for (const [prefix, providerId] of PREFIX_MAP) {\n if (modelId.startsWith(prefix)) return providers.get(providerId);\n }\n\n return undefined;\n}\n\nconst PREFIX_MAP: [string, string][] = [\n [\"openai/\", \"openai\"],\n [\"anthropic/\", \"anthropic\"],\n [\"deepseek/\", \"deepseek\"],\n [\"deepseek-ai/\", \"siliconflow\"],\n [\"Qwen/\", \"siliconflow\"],\n [\"meta-llama/\", \"siliconflow\"],\n [\"THUDM/\", \"siliconflow\"],\n [\"Pro/\", \"siliconflow\"],\n [\"google/\", \"openrouter\"],\n [\"mistral/\", \"openrouter\"],\n [\"cohere/\", \"openrouter\"],\n [\"microsoft/\", \"openrouter\"],\n [\"amazon/\", \"openrouter\"],\n [\"nvidia/\", \"openrouter\"],\n [\"x-ai/\", \"openrouter\"],\n];\n\n/** The default provider (DeepSeek), used when no provider can be resolved. */\nexport function defaultProvider(): ProviderProfile {\n return deepseek;\n}\n\n/**\n * Pricing for a given model. Falls back: model-specific → provider\n * defaults → all-zeros for completely unknown models.\n */\nexport function pricingFor(modelId: string): {\n inputCacheHit: number;\n inputCacheMiss: number;\n output: number;\n} {\n const provider = resolveProvider(modelId);\n if (!provider) return ZERO_PRICING;\n return provider.pricing.models?.[modelId] ?? provider.pricing.defaults;\n}\n\nconst ZERO_PRICING = { inputCacheHit: 0, inputCacheMiss: 0, output: 0 };\n\n/** Context window size for a given model, resolved through the registry. */\nexport function contextTokensFor(modelId: string): number {\n const provider = resolveProvider(modelId);\n if (!provider) return 131_072;\n return provider.contextTokens?.[modelId] ?? provider.defaultContextTokens;\n}\n","/** Generic pause gate — bridges tool functions and the App's modals via Promises. */\n// Tools call gate.ask(kind, payload) and await the result; the App subscribes\n// with gate.on() to show the right modal, then calls gate.resolve() on user pick.\n\nexport type ConfirmationChoice =\n | { type: \"deny\"; denyContext?: string }\n | { type: \"run_once\" }\n | { type: \"always_allow\"; prefix: string };\n\nexport type PlanVerdict =\n | { type: \"approve\"; feedback?: string }\n | { type: \"refine\"; feedback?: string }\n | { type: \"cancel\"; feedback?: string };\n\nexport type CheckpointVerdict =\n | { type: \"continue\" }\n | { type: \"revise\"; feedback?: string }\n | { type: \"stop\" };\n\nexport type RevisionVerdict = { type: \"accepted\" } | { type: \"rejected\" } | { type: \"cancelled\" };\n\nexport type ChoiceVerdict =\n | { type: \"pick\"; optionId: string }\n | { type: \"text\"; text: string }\n | { type: \"cancel\" };\n\nexport type ToolConfirmationAuditEvent =\n | {\n type: \"tool.confirm.allow\";\n kind: \"run_command\" | \"run_background\";\n payload: { command: string };\n }\n | {\n type: \"tool.confirm.deny\";\n kind: \"run_command\" | \"run_background\";\n payload: { command: string };\n denyContext?: string;\n }\n | {\n type: \"tool.confirm.always_allow\";\n kind: \"run_command\" | \"run_background\";\n payload: { command: string };\n prefix: string;\n };\n\ninterface PauseResponseMap {\n run_command: ConfirmationChoice;\n run_background: ConfirmationChoice;\n path_access: ConfirmationChoice;\n plan_proposed: PlanVerdict;\n plan_checkpoint: CheckpointVerdict;\n plan_revision: RevisionVerdict;\n choice: ChoiceVerdict;\n}\n\ntype PauseKind = keyof PauseResponseMap;\n\ninterface PausePayloadMap {\n run_command: { command: string; cwd?: string; timeoutSec?: number };\n run_background: { command: string; cwd?: string; waitSec?: number };\n path_access: {\n /** Absolute path the tool wants to touch. */\n path: string;\n /** Why we're being asked — read leaks content, write mutates files. */\n intent: \"read\" | \"write\";\n /** The filesystem tool calling in — surfaced so users can see what's about to happen. */\n toolName: string;\n /** Sandbox root the path is escaping — surfaced for context. */\n sandboxRoot: string;\n /** Directory prefix that would be persisted if the user picks \"always allow\". */\n allowPrefix: string;\n };\n plan_proposed: { plan: string; steps?: unknown[]; summary?: string };\n plan_checkpoint: { stepId: string; title?: string; result: string; notes?: string };\n plan_revision: { reason: string; remainingSteps: unknown[]; summary?: string };\n choice: { question: string; options: unknown[]; allowCustom: boolean };\n}\n\nexport type PauseRequest = {\n id: number;\n kind: PauseKind;\n payload: unknown;\n};\n\ntype GateListener = (request: PauseRequest) => void;\ntype AuditListener = (event: ToolConfirmationAuditEvent) => void;\n\n/** Named options for PauseGate.ask() — makes it obvious which field is kind vs payload. */\nexport interface PauseAskOpts<K extends PauseKind = PauseKind> {\n kind: K;\n payload: PausePayloadMap[K];\n}\n\nexport class PauseGate {\n private _nextId = 0;\n private _pending = new Map<number, { resolve: (data: unknown) => void; request: PauseRequest }>();\n private _listeners: Set<GateListener> = new Set();\n private _auditListener: AuditListener | null = null;\n\n /** Block until the user responds. Takes a named options object so the\n * kind and payload fields don't get confused at the call site. */\n ask<K extends PauseKind>(opts: PauseAskOpts<K>): Promise<PauseResponseMap[K]> {\n const { kind, payload } = opts;\n if (this._listeners.size === 0) {\n throw new Error(\n `${kind}: no confirmation listener registered — cannot prompt the user. This tool can only be used inside an interactive Luckerr session.`,\n );\n }\n return new Promise((resolve) => {\n const id = this._nextId++;\n const request: PauseRequest = { id, kind, payload };\n this._pending.set(id, { resolve: resolve as (d: unknown) => void, request });\n for (const fn of this._listeners) {\n try {\n fn(request);\n } catch {\n /* listener error shouldn't break the gate */\n }\n }\n });\n }\n\n /** Resolve a pending request. Called by the App's modal callback. */\n resolve(id: number, data: unknown): void {\n const p = this._pending.get(id);\n if (!p) return;\n this._pending.delete(id);\n this.emitAuditEvent(p.request, data);\n p.resolve(data);\n }\n\n /** Safe-cancel every outstanding request — frees stranded tool fns on Esc / /new. */\n cancelAll(): void {\n const ids = [...this._pending.keys()];\n for (const id of ids) {\n const p = this._pending.get(id);\n if (!p) continue;\n this._pending.delete(id);\n p.resolve(safeCancelVerdict(p.request.kind));\n }\n }\n\n /** Cancel one pending request — used by multi-tab hosts that need per-scope abort. */\n cancel(id: number): boolean {\n const p = this._pending.get(id);\n if (!p) return false;\n this._pending.delete(id);\n p.resolve(safeCancelVerdict(p.request.kind));\n return true;\n }\n\n setAuditListener(fn: AuditListener | null): void {\n this._auditListener = fn;\n }\n\n /** Subscribe to new pause requests. Returns an unsubscribe function. */\n on(fn: GateListener): () => void {\n this._listeners.add(fn);\n return () => {\n this._listeners.delete(fn);\n };\n }\n\n /** Current pending request, if any (polling fallback). */\n get current(): PauseRequest | null {\n for (const [, p] of this._pending) return p.request;\n return null;\n }\n\n private emitAuditEvent(request: PauseRequest, data: unknown): void {\n if (!this._auditListener) return;\n if (request.kind !== \"run_command\" && request.kind !== \"run_background\") return;\n if (!data || typeof data !== \"object\") return;\n const choice = data as Partial<ConfirmationChoice>;\n try {\n switch (choice.type) {\n case \"run_once\":\n this._auditListener({\n type: \"tool.confirm.allow\",\n kind: request.kind,\n payload: request.payload as { command: string },\n });\n break;\n case \"deny\":\n this._auditListener({\n type: \"tool.confirm.deny\",\n kind: request.kind,\n payload: request.payload as { command: string },\n denyContext: choice.denyContext,\n });\n break;\n case \"always_allow\":\n if (typeof choice.prefix !== \"string\") return;\n this._auditListener({\n type: \"tool.confirm.always_allow\",\n kind: request.kind,\n payload: request.payload as { command: string },\n prefix: choice.prefix,\n });\n break;\n default:\n break;\n }\n } catch {\n /* audit path must never break the gate */\n }\n }\n}\n\nfunction safeCancelVerdict(kind: PauseKind): unknown {\n switch (kind) {\n case \"run_command\":\n case \"run_background\":\n case \"path_access\":\n return { type: \"deny\" };\n case \"plan_proposed\":\n return { type: \"cancel\" };\n case \"plan_checkpoint\":\n return { type: \"stop\" };\n case \"plan_revision\":\n return { type: \"cancelled\" };\n case \"choice\":\n return { type: \"cancel\" };\n }\n}\n\n/** Singleton shared between tools and the App. */\nexport const pauseGate = new PauseGate();\n","/** Shell-command hooks; project scope first, then global. Exit 0=pass, 2=block on Pre*, other=warn. */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { t } from \"./i18n/index.js\";\n\nexport type HookEvent = \"PreToolUse\" | \"PostToolUse\" | \"UserPromptSubmit\" | \"Stop\";\n\n/** All four events as a const array — drives slash listing + validation. */\nexport const HOOK_EVENTS: readonly HookEvent[] = [\n \"PreToolUse\",\n \"PostToolUse\",\n \"UserPromptSubmit\",\n \"Stop\",\n] as const;\n\n/** Only the gating events can block the loop. */\nconst BLOCKING_EVENTS: ReadonlySet<HookEvent> = new Set([\"PreToolUse\", \"UserPromptSubmit\"]);\n\n/** Per-event default timeout. Tool/prompt hooks gate progress, so they're tight. */\nconst DEFAULT_TIMEOUTS_MS: Record<HookEvent, number> = {\n PreToolUse: 5_000,\n UserPromptSubmit: 5_000,\n PostToolUse: 30_000,\n Stop: 30_000,\n};\n\nexport type HookScope = \"project\" | \"global\";\n\nexport interface HookConfig {\n /** Anchored regex; `\"*\"` / omitted = every tool. Pre/PostToolUse only. */\n match?: string;\n /** Shell command to run. Spawned through the platform shell. */\n command: string;\n /** Optional human description — surfaced in `/hooks`. */\n description?: string;\n /** Per-hook timeout override in ms. */\n timeout?: number;\n /** Defaults: project scope → project root; global scope → process.cwd(). */\n cwd?: string;\n}\n\n/** Shape of `<scope>/.luckerr/settings.json` — only `hooks` for now. */\nexport interface HookSettings {\n hooks?: Partial<Record<HookEvent, HookConfig[]>>;\n}\n\n/** A loaded hook with its origin scope baked in (used for ordering and `/hooks`). */\nexport interface ResolvedHook extends HookConfig {\n event: HookEvent;\n scope: HookScope;\n /** Absolute path to the settings.json the hook came from. */\n source: string;\n}\n\n/** Outcome of a single hook invocation. */\nexport interface HookOutcome {\n /** Which hook fired. */\n hook: ResolvedHook;\n /** pass=exit 0; block=exit 2 on blocking event; warn=other non-zero; timeout=killed; error=spawn failed. */\n decision: \"pass\" | \"block\" | \"warn\" | \"timeout\" | \"error\";\n exitCode: number | null;\n /** Captured stdout (trimmed). May be empty. */\n stdout: string;\n /** Captured stderr (trimmed). The block / warn message comes from here. */\n stderr: string;\n durationMs: number;\n /** Output crossed the per-stream byte cap; surfaced so user knows we kept less than the script wrote. */\n truncated?: boolean;\n}\n\n/** Aggregate report for `runHooks`. */\nexport interface HookReport {\n event: HookEvent;\n outcomes: HookOutcome[];\n /** True iff at least one outcome was a `block` — only meaningful for blocking events. */\n blocked: boolean;\n}\n\nexport const HOOK_SETTINGS_FILENAME = \"settings.json\";\nexport const HOOK_SETTINGS_DIRNAME = \".luckerr\";\n\n/** Where the global settings.json lives. Equivalent to `~/.luckerr/settings.json`. */\nexport function globalSettingsPath(homeDirOverride?: string): string {\n return join(homeDirOverride ?? homedir(), HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME);\n}\n\n/** Where the project settings.json lives for a given root. */\nexport function projectSettingsPath(projectRoot: string): string {\n return join(projectRoot, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME);\n}\n\nfunction readSettingsFile(path: string): HookSettings | null {\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") return parsed as HookSettings;\n } catch {\n /* malformed JSON → treat as no hooks; do NOT throw, the user\n * shouldn't lose the whole CLI to a typo in their settings */\n }\n return null;\n}\n\n/** Project hooks fire before global; within a scope, array order. */\nexport interface LoadHookSettingsOptions {\n /** Absolute project root, if any. Without it, only global hooks load. */\n projectRoot?: string;\n /** Override `~` for tests. */\n homeDir?: string;\n}\n\nexport function loadHooks(opts: LoadHookSettingsOptions = {}): ResolvedHook[] {\n const out: ResolvedHook[] = [];\n if (opts.projectRoot) {\n const projPath = projectSettingsPath(opts.projectRoot);\n const settings = readSettingsFile(projPath);\n if (settings) appendResolved(out, settings, \"project\", projPath);\n }\n const globalPath = globalSettingsPath(opts.homeDir);\n const settings = readSettingsFile(globalPath);\n if (settings) appendResolved(out, settings, \"global\", globalPath);\n return out;\n}\n\nfunction appendResolved(\n out: ResolvedHook[],\n settings: HookSettings,\n scope: HookScope,\n source: string,\n): void {\n if (!settings.hooks) return;\n for (const event of HOOK_EVENTS) {\n const list = settings.hooks[event];\n if (!Array.isArray(list)) continue;\n for (const cfg of list) {\n if (!cfg || typeof cfg.command !== \"string\" || cfg.command.trim() === \"\") continue;\n out.push({ ...cfg, event, scope, source });\n }\n }\n}\n\n/** Match field is an ANCHORED regex — `\"file\"` won't trigger on `read_file`; use `\".*file\"`. */\nexport function matchesTool(hook: ResolvedHook, toolName: string): boolean {\n if (hook.event !== \"PreToolUse\" && hook.event !== \"PostToolUse\") return true;\n const m = hook.match;\n if (!m || m === \"*\") return true;\n try {\n const re = new RegExp(`^(?:${m})$`);\n return re.test(toolName);\n } catch {\n /* malformed regex → don't fire (safer than firing on every tool) */\n return false;\n }\n}\n\n/** Payload envelope passed to hook stdin. */\nexport interface HookPayload {\n event: HookEvent;\n cwd: string;\n toolName?: string;\n toolArgs?: unknown;\n toolResult?: string;\n prompt?: string;\n lastAssistantText?: string;\n turn?: number;\n}\n\n/** Test seam — same shape as Node's spawn but returns a Promise of the raw outcome bits. */\nexport interface HookSpawnInput {\n command: string;\n cwd: string;\n stdin: string;\n timeoutMs: number;\n}\n\nexport interface HookSpawnResult {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n timedOut: boolean;\n /** True iff spawn() itself failed (ENOENT, EACCES, …). */\n spawnError?: Error;\n /** Output capped at byte limit — hook ran to completion but consumers see clipped view. */\n truncated?: boolean;\n}\n\n/** Per-stream cap — bounds heap exposure to a runaway child between spawn and timeout. */\nconst HOOK_OUTPUT_CAP_BYTES = 256 * 1024;\n\nexport type HookSpawner = (input: HookSpawnInput) => Promise<HookSpawnResult>;\n\n/** `shell: true` — hook is a shell command by contract; pipes / `&&` / env expansion must work. */\nfunction defaultSpawner(input: HookSpawnInput): Promise<HookSpawnResult> {\n return new Promise<HookSpawnResult>((resolve) => {\n const child = spawn(input.command, {\n cwd: input.cwd,\n shell: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n // Collect raw bytes per stream and decode once at close so a\n // multi-byte UTF-8 sequence split across data chunks doesn't\n // corrupt — same approach shell.ts uses for run_command output.\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let stdoutBytes = 0;\n let stderrBytes = 0;\n let truncated = false;\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGTERM\");\n // SIGTERM may not land on Windows for shell children — followed\n // by a hard kill a moment later if the process is still around.\n setTimeout(() => {\n try {\n child.kill(\"SIGKILL\");\n } catch {\n /* already gone */\n }\n }, 500);\n }, input.timeoutMs);\n\n const onChunk = (kind: \"stdout\" | \"stderr\", chunk: Buffer) => {\n const target = kind === \"stdout\" ? stdoutChunks : stderrChunks;\n const seen = kind === \"stdout\" ? stdoutBytes : stderrBytes;\n if (seen >= HOOK_OUTPUT_CAP_BYTES) {\n truncated = true;\n return;\n }\n const remaining = HOOK_OUTPUT_CAP_BYTES - seen;\n if (chunk.length > remaining) {\n target.push(chunk.subarray(0, remaining));\n if (kind === \"stdout\") stdoutBytes = HOOK_OUTPUT_CAP_BYTES;\n else stderrBytes = HOOK_OUTPUT_CAP_BYTES;\n truncated = true;\n } else {\n target.push(chunk);\n if (kind === \"stdout\") stdoutBytes += chunk.length;\n else stderrBytes += chunk.length;\n }\n };\n child.stdout.on(\"data\", (chunk: Buffer) => onChunk(\"stdout\", chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => onChunk(\"stderr\", chunk));\n child.once(\"error\", (err) => {\n clearTimeout(timer);\n resolve({\n exitCode: null,\n stdout: Buffer.concat(stdoutChunks).toString(\"utf8\"),\n stderr: Buffer.concat(stderrChunks).toString(\"utf8\"),\n timedOut: false,\n spawnError: err,\n truncated: truncated || undefined,\n });\n });\n child.once(\"close\", (code) => {\n clearTimeout(timer);\n resolve({\n exitCode: code,\n stdout: Buffer.concat(stdoutChunks).toString(\"utf8\").trim(),\n stderr: Buffer.concat(stderrChunks).toString(\"utf8\").trim(),\n timedOut,\n truncated: truncated || undefined,\n });\n });\n\n try {\n child.stdin.write(input.stdin);\n child.stdin.end();\n } catch {\n /* stdin write can race with spawn errors; the close handler\n * still fires with exit 0/null */\n }\n });\n}\n\nexport function formatHookOutcomeMessage(outcome: HookOutcome): string {\n if (outcome.decision === \"pass\") return \"\";\n const detail = (outcome.stderr || outcome.stdout || \"\").trim();\n const tag = `${outcome.hook.scope}/${outcome.hook.event}`;\n const cmd =\n outcome.hook.command.length > 60\n ? `${outcome.hook.command.slice(0, 60)}…`\n : outcome.hook.command;\n const truncTag = outcome.truncated ? t(\"hooks.truncated\") : \"\";\n const decision = t(`hooks.decision${capitalize(outcome.decision)}`);\n return detail\n ? t(\"hooks.headWithDetail\", { tag, cmd, decision, truncTag, detail })\n : t(\"hooks.head\", { tag, cmd, decision, truncTag });\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nexport function decideOutcome(\n event: HookEvent,\n raw: HookSpawnResult,\n): \"pass\" | \"block\" | \"warn\" | \"timeout\" | \"error\" {\n if (raw.spawnError) return \"error\";\n if (raw.timedOut) return BLOCKING_EVENTS.has(event) ? \"block\" : \"warn\";\n if (raw.exitCode === 0) return \"pass\";\n if (raw.exitCode === 2 && BLOCKING_EVENTS.has(event)) return \"block\";\n return \"warn\";\n}\n\nexport interface RunHooksOptions {\n payload: HookPayload;\n hooks: ResolvedHook[];\n /** Test seam — defaults to a real `spawn`. */\n spawner?: HookSpawner;\n}\n\n/** Stops at first `block` so a gating hook can prevent later hooks running against a phantom success. */\nexport async function runHooks(opts: RunHooksOptions): Promise<HookReport> {\n const spawner = opts.spawner ?? defaultSpawner;\n const event = opts.payload.event;\n const toolName = opts.payload.toolName ?? \"\";\n const matching = opts.hooks.filter((h) => h.event === event && matchesTool(h, toolName));\n\n const outcomes: HookOutcome[] = [];\n let blocked = false;\n const stdin = `${JSON.stringify(opts.payload)}\\n`;\n\n for (const hook of matching) {\n const start = Date.now();\n const timeoutMs = hook.timeout ?? DEFAULT_TIMEOUTS_MS[event];\n const cwd = hook.cwd ?? opts.payload.cwd;\n const raw = await spawner({ command: hook.command, cwd, stdin, timeoutMs });\n const decision = decideOutcome(event, raw);\n outcomes.push({\n hook,\n decision,\n exitCode: raw.exitCode,\n stdout: raw.stdout,\n stderr:\n raw.stderr ||\n (raw.spawnError ? raw.spawnError.message : \"\") ||\n (raw.timedOut ? `hook timed out after ${timeoutMs}ms` : \"\"),\n durationMs: Date.now() - start,\n truncated: raw.truncated,\n });\n if (decision === \"block\") {\n blocked = true;\n break;\n }\n }\n\n return { event, outcomes, blocked };\n}\n","/** Library reads only DEEPSEEK_API_KEY from env; the CLI bridges config.json → env var. */\n\nimport { chmodSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { type ThemeName, isThemeName, resolveThemeName } from \"./cli/ui/theme/tokens.js\";\nimport type { LanguageCode } from \"./i18n/types.js\";\nimport {\n type IndexUserConfig,\n type ResolvedIndexConfig,\n resolveIndexConfig,\n} from \"./index/config.js\";\n\n/** Legacy `fast|smart|max` kept for back-compat with existing config.json files. */\nexport type PresetName = \"auto\" | \"flash\" | \"pro\" | \"fast\" | \"smart\" | \"max\";\n\n/** Single trust dial: review queues edits + gates shell; auto applies + gates shell; yolo skips both gates. */\nexport type EditMode = \"review\" | \"auto\" | \"yolo\";\n\nexport type ReasoningEffort = \"high\" | \"max\";\n\nexport type EmbeddingProvider = \"ollama\" | \"openai-compat\";\n\nexport interface OllamaEmbeddingUserConfig {\n baseUrl?: string;\n model?: string;\n}\n\nexport interface OpenAICompatEmbeddingUserConfig {\n baseUrl?: string;\n apiKey?: string;\n model?: string;\n extraBody?: Record<string, unknown>;\n}\n\nexport interface SemanticEmbeddingUserConfig {\n provider?: EmbeddingProvider;\n ollama?: OllamaEmbeddingUserConfig;\n openaiCompat?: OpenAICompatEmbeddingUserConfig;\n}\n\nexport interface ResolvedOllamaEmbeddingConfig {\n provider: \"ollama\";\n baseUrl: string;\n model: string;\n timeoutMs: number;\n}\n\nexport interface ResolvedOpenAICompatEmbeddingConfig {\n provider: \"openai-compat\";\n baseUrl: string;\n apiKey: string;\n model: string;\n extraBody: Record<string, unknown>;\n timeoutMs: number;\n}\n\nexport type ResolvedEmbeddingConfig =\n | ResolvedOllamaEmbeddingConfig\n | ResolvedOpenAICompatEmbeddingConfig;\n\nexport interface SemanticEmbeddingConfigView {\n provider: EmbeddingProvider;\n ollama: {\n baseUrl: string;\n model: string;\n };\n openaiCompat: {\n baseUrl: string;\n apiKey: string;\n apiKeySet: boolean;\n model: string;\n extraBody: Record<string, unknown>;\n };\n}\n\nexport interface LuckerrConfig {\n apiKey?: string;\n baseUrl?: string;\n /**\n * Active AI provider id (e.g. \"deepseek\", \"siliconflow\", \"zhipu\").\n * If unset, the system auto-resolves from the model id or falls\n * back to \"deepseek\".\n */\n provider?: string;\n /** Per-provider API keys, keyed by provider id. */\n providerKeys?: Record<string, string>;\n /** Per-provider base URL overrides, keyed by provider id. */\n providerBaseUrls?: Record<string, string>;\n lang?: LanguageCode;\n preset?: PresetName;\n editMode?: EditMode;\n editModeHintShown?: boolean;\n mouseClipboardHintShown?: boolean;\n reasoningEffort?: ReasoningEffort;\n /** Default workspace root for the desktop client. CLI uses cwd. */\n workspaceDir?: string;\n /** Last N workspace paths the desktop client has opened, most recent first. */\n recentWorkspaces?: string[];\n /** Desktop only — `openWith` value for clicking file links. Empty/undefined = OS default app. Examples: \"code\", \"cursor\", \"C:\\\\path\\\\to\\\\editor.exe\". */\n editor?: string;\n theme?: ThemeName | \"auto\";\n /** Stored as `--mcp`-format strings so one parser handles both flag and config. */\n mcp?: string[];\n /** Names of servers in `mcp` to skip on bridge — see `/mcp disable <name>`. */\n mcpDisabled?: string[];\n /** Env overlay per MCP server name (matches the `name=` prefix of the spec). Stdio transports merge this over process.env; SSE/HTTP ignore it. */\n mcpEnv?: Record<string, Record<string, string>>;\n session?: string | null;\n setupCompleted?: boolean;\n search?: boolean;\n /** Web search engine backend: \"mojeek\" (default, scrapes Mojeek) or \"searxng\" (self-hosted SearXNG). */\n webSearchEngine?: \"mojeek\" | \"searxng\";\n /** Base URL for SearXNG instance (default http://localhost:8080). */\n webSearchEndpoint?: string;\n dashboard?: {\n /** Pin the embedded dashboard to a fixed port — required for stable SSH tunnels. 0/absent → ephemeral. */\n port?: number;\n };\n escalation?: {\n /** Per-turn repair/error signal count required to escalate flash→pro. Defaults to 3. Out-of-range → default. */\n failureThreshold?: number;\n };\n /** Per-field visibility toggles for the bottom status row. All default to true (visible). */\n statusBar?: {\n showBalance?: boolean;\n showSessionCost?: boolean;\n showTurnCost?: boolean;\n showCacheHit?: boolean;\n showVersion?: boolean;\n showFeedbackHint?: boolean;\n };\n projects?: {\n [absoluteRootDir: string]: {\n shellAllowed?: string[];\n /** Absolute directory prefixes the user pre-approved for outside-sandbox file access (#684). */\n pathAllowed?: string[];\n };\n };\n index?: IndexUserConfig;\n semantic?: SemanticEmbeddingUserConfig;\n /** User-declared extensions to the built-in memory types (#709). Unknown types round-trip even without a declaration; declaring one lets you attach a default priority + lifecycle. */\n memory?: {\n customTypes?: CustomMemoryTypeConfig[];\n };\n}\n\nexport interface CustomMemoryTypeConfig {\n name: string;\n description?: string;\n priority?: \"low\" | \"medium\" | \"high\";\n expires?: \"project_end\";\n}\n\nexport interface MemoryTypeRegistryEntry {\n name: string;\n builtin: boolean;\n description?: string;\n priority?: \"low\" | \"medium\" | \"high\";\n expires?: \"project_end\";\n}\n\nconst BUILTIN_TYPE_DOCS: Record<string, string> = {\n user: \"role / skills / preferences\",\n feedback: \"corrections or confirmed approaches\",\n project: \"facts / decisions about the current work\",\n reference: \"pointers to external systems the user uses\",\n};\n\n/** Resolve the merged registry of memory types — built-ins, overlaid by anything in `config.memory.customTypes`. */\nexport function loadMemoryTypeRegistry(\n cfg: LuckerrConfig = readConfig(),\n): MemoryTypeRegistryEntry[] {\n const out: MemoryTypeRegistryEntry[] = [];\n for (const name of [\"user\", \"feedback\", \"project\", \"reference\"]) {\n out.push({ name, builtin: true, description: BUILTIN_TYPE_DOCS[name] });\n }\n const seen = new Set(out.map((e) => e.name));\n for (const raw of cfg.memory?.customTypes ?? []) {\n if (!raw || typeof raw.name !== \"string\") continue;\n const name = raw.name.trim();\n if (!name || !/^[a-zA-Z][a-zA-Z0-9_-]{0,31}$/.test(name)) continue;\n if (seen.has(name)) continue;\n seen.add(name);\n const entry: MemoryTypeRegistryEntry = { name, builtin: false };\n if (typeof raw.description === \"string\") entry.description = raw.description;\n if (raw.priority === \"low\" || raw.priority === \"medium\" || raw.priority === \"high\") {\n entry.priority = raw.priority;\n }\n if (raw.expires === \"project_end\") entry.expires = raw.expires;\n out.push(entry);\n }\n return out;\n}\n\nexport function memoryTypeDefaults(\n typeName: string,\n cfg: LuckerrConfig = readConfig(),\n): { priority?: \"low\" | \"medium\" | \"high\"; expires?: \"project_end\" } {\n const found = loadMemoryTypeRegistry(cfg).find((e) => e.name === typeName);\n if (!found) return {};\n const out: { priority?: \"low\" | \"medium\" | \"high\"; expires?: \"project_end\" } = {};\n if (found.priority) out.priority = found.priority;\n if (found.expires) out.expires = found.expires;\n return out;\n}\n\nconst DEFAULT_OLLAMA_URL = \"http://localhost:11434\";\nconst DEFAULT_EMBED_MODEL = \"nomic-embed-text\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport function defaultConfigPath(): string {\n return join(homedir(), \".luckerr\", \"config.json\");\n}\n\nexport function readConfig(path: string = defaultConfigPath()): LuckerrConfig {\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") return parsed as LuckerrConfig;\n } catch {\n /* missing or malformed → empty config */\n }\n return {};\n}\n\nexport function writeConfig(cfg: LuckerrConfig, path: string = defaultConfigPath()): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(cfg, null, 2), \"utf8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n /* ignore on platforms without chmod */\n }\n}\n\n/** Resolve the language from config file. */\nexport function loadLanguage(path: string = defaultConfigPath()): LanguageCode | undefined {\n return readConfig(path).lang;\n}\n\nexport function mcpEnvFor(\n serverName: string | null | undefined,\n cfg: LuckerrConfig,\n): Record<string, string> | undefined {\n if (!serverName) return undefined;\n const entry = cfg.mcpEnv?.[serverName];\n if (!entry) return undefined;\n // Coerce to string and drop empty values — JSON config could be sloppy.\n const filtered: Record<string, string> = {};\n for (const [k, v] of Object.entries(entry)) {\n if (typeof v === \"string\" && v.length > 0) filtered[k] = v;\n }\n return Object.keys(filtered).length > 0 ? filtered : undefined;\n}\n\n/** Persist the language so it survives a relaunch. */\nexport function saveLanguage(lang: LanguageCode, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.lang = lang;\n writeConfig(cfg, path);\n}\n\n/** Resolve the API key from env var first, then the config file. */\n/**\n * Load the API key for the default provider (DeepSeek).\n * Checks DEEPSEEK_API_KEY env var first, then config.json apiKey field.\n */\nexport function loadApiKey(path: string = defaultConfigPath()): string | undefined {\n if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;\n return readConfig(path).apiKey;\n}\n\n/**\n * Load the API key for a specific provider.\n *\n * Resolution order:\n * 1. Provider-specific env var (e.g. SILICONFLOW_API_KEY)\n * 2. config.json providerKeys[providerId]\n * 3. Legacy DEEPSEEK_API_KEY env var\n * 4. config.json apiKey field\n */\nexport function loadProviderApiKey(\n providerId: string,\n envKey: string,\n path: string = defaultConfigPath(),\n): string | undefined {\n // 1. Provider-specific env var\n if (process.env[envKey]) return process.env[envKey];\n // 2. config.json per-provider key\n const cfg = readConfig(path);\n if (cfg.providerKeys?.[providerId]) return cfg.providerKeys[providerId];\n // 3. Legacy DEEPSEEK_API_KEY\n if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;\n // 4. config.json apiKey\n return cfg.apiKey;\n}\n\n/** env > config > undefined. Client falls back to api.deepseek.com when undefined. */\nexport function loadBaseUrl(path: string = defaultConfigPath()): string | undefined {\n if (process.env.DEEPSEEK_BASE_URL) return process.env.DEEPSEEK_BASE_URL;\n return readConfig(path).baseUrl;\n}\n\n/**\n * Load a provider-specific base URL override.\n *\n * Resolution order:\n * 1. Legacy DEEPSEEK_BASE_URL env var\n * 2. config.json providerBaseUrls[providerId]\n * 3. config.json baseUrl\n */\nexport function loadProviderBaseUrl(\n providerId: string,\n path: string = defaultConfigPath(),\n): string | undefined {\n if (process.env.DEEPSEEK_BASE_URL) return process.env.DEEPSEEK_BASE_URL;\n const cfg = readConfig(path);\n if (cfg.providerBaseUrls?.[providerId]) return cfg.providerBaseUrls[providerId];\n return cfg.baseUrl;\n}\n\n/**\n * Load the active provider id from config.\n * Falls back to \"deepseek\" if unset.\n */\nexport function loadProvider(path: string = defaultConfigPath()): string {\n return readConfig(path).provider ?? \"deepseek\";\n}\n\nexport function saveBaseUrl(url: string, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n const trimmed = url.trim();\n if (trimmed) {\n cfg.baseUrl = trimmed;\n } else {\n cfg.baseUrl = undefined;\n }\n writeConfig(cfg, path);\n}\n\nexport function searchEnabled(path: string = defaultConfigPath()): boolean {\n const env = process.env.LUCKERR_SEARCH;\n if (env === \"off\" || env === \"false\" || env === \"0\") return false;\n const cfg = readConfig(path).search;\n if (cfg === false) return false;\n return true;\n}\n\nexport function webSearchEngine(path: string = defaultConfigPath()): \"mojeek\" | \"searxng\" {\n const cfg = readConfig(path).webSearchEngine;\n if (cfg === \"searxng\") return \"searxng\";\n return \"mojeek\";\n}\n\nexport function webSearchEndpoint(path: string = defaultConfigPath()): string {\n const cfg = readConfig(path).webSearchEndpoint;\n if (cfg && typeof cfg === \"string\") return cfg;\n return \"http://localhost:8080\";\n}\n\nexport function saveApiKey(key: string, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.apiKey = key.trim();\n writeConfig(cfg, path);\n}\n\n/** Windows: case-insensitive — NTFS treats `F:\\Foo` and `f:\\foo` as one directory (#402). */\nfunction findProjectKey(cfg: LuckerrConfig, rootDir: string): string | undefined {\n const projects = cfg.projects;\n if (!projects) return undefined;\n if (Object.hasOwn(projects, rootDir)) return rootDir;\n if (process.platform !== \"win32\") return undefined;\n const lower = rootDir.toLowerCase();\n for (const k of Object.keys(projects)) {\n if (k.toLowerCase() === lower) return k;\n }\n return undefined;\n}\n\nexport function loadProjectShellAllowed(\n rootDir: string,\n path: string = defaultConfigPath(),\n): string[] {\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return [];\n return cfg.projects?.[key]?.shellAllowed ?? [];\n}\n\nexport function addProjectShellAllowed(\n rootDir: string,\n prefix: string,\n path: string = defaultConfigPath(),\n): void {\n const trimmed = prefix.trim();\n if (!trimmed) return;\n const cfg = readConfig(path);\n if (!cfg.projects) cfg.projects = {};\n const key = findProjectKey(cfg, rootDir) ?? rootDir;\n if (!cfg.projects[key]) cfg.projects[key] = {};\n const existing = cfg.projects[key].shellAllowed ?? [];\n if (existing.includes(trimmed)) return;\n cfg.projects[key].shellAllowed = [...existing, trimmed];\n writeConfig(cfg, path);\n}\n\n/** Match is exact after trim — NOT prefix-match: removing `git` MUST NOT drop `git push origin main`. */\nexport function removeProjectShellAllowed(\n rootDir: string,\n prefix: string,\n path: string = defaultConfigPath(),\n): boolean {\n const trimmed = prefix.trim();\n if (!trimmed) return false;\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return false;\n const existing = cfg.projects?.[key]?.shellAllowed ?? [];\n if (!existing.includes(trimmed)) return false;\n const next = existing.filter((p) => p !== trimmed);\n if (!cfg.projects) cfg.projects = {};\n if (!cfg.projects[key]) cfg.projects[key] = {};\n cfg.projects[key].shellAllowed = next;\n writeConfig(cfg, path);\n return true;\n}\n\nexport function clearProjectShellAllowed(\n rootDir: string,\n path: string = defaultConfigPath(),\n): number {\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return 0;\n const existing = cfg.projects?.[key]?.shellAllowed ?? [];\n if (existing.length === 0) return 0;\n if (!cfg.projects) cfg.projects = {};\n if (!cfg.projects[key]) cfg.projects[key] = {};\n cfg.projects[key].shellAllowed = [];\n writeConfig(cfg, path);\n return existing.length;\n}\n\nexport function loadProjectPathAllowed(\n rootDir: string,\n path: string = defaultConfigPath(),\n): string[] {\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return [];\n return cfg.projects?.[key]?.pathAllowed ?? [];\n}\n\nexport function addProjectPathAllowed(\n rootDir: string,\n prefix: string,\n path: string = defaultConfigPath(),\n): void {\n const trimmed = prefix.trim();\n if (!trimmed) return;\n const cfg = readConfig(path);\n if (!cfg.projects) cfg.projects = {};\n const key = findProjectKey(cfg, rootDir) ?? rootDir;\n if (!cfg.projects[key]) cfg.projects[key] = {};\n const existing = cfg.projects[key].pathAllowed ?? [];\n if (existing.includes(trimmed)) return;\n cfg.projects[key].pathAllowed = [...existing, trimmed];\n writeConfig(cfg, path);\n}\n\nexport function removeProjectPathAllowed(\n rootDir: string,\n prefix: string,\n path: string = defaultConfigPath(),\n): boolean {\n const trimmed = prefix.trim();\n if (!trimmed) return false;\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return false;\n const existing = cfg.projects?.[key]?.pathAllowed ?? [];\n if (!existing.includes(trimmed)) return false;\n const next = existing.filter((p) => p !== trimmed);\n if (!cfg.projects) cfg.projects = {};\n if (!cfg.projects[key]) cfg.projects[key] = {};\n cfg.projects[key].pathAllowed = next;\n writeConfig(cfg, path);\n return true;\n}\n\nexport function clearProjectPathAllowed(\n rootDir: string,\n path: string = defaultConfigPath(),\n): number {\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return 0;\n const existing = cfg.projects?.[key]?.pathAllowed ?? [];\n if (existing.length === 0) return 0;\n if (!cfg.projects) cfg.projects = {};\n if (!cfg.projects[key]) cfg.projects[key] = {};\n cfg.projects[key].pathAllowed = [];\n writeConfig(cfg, path);\n return existing.length;\n}\n\n/** Unknown values fall back to \"review\" so hand-edited bad config gets the safe default. */\nexport function loadEditMode(path: string = defaultConfigPath()): EditMode {\n const v = readConfig(path).editMode;\n if (v === \"auto\" || v === \"yolo\") return v;\n return \"review\";\n}\n\n/** Persist the edit mode so `/mode auto` survives a relaunch. */\nexport function saveEditMode(mode: EditMode, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.editMode = mode;\n writeConfig(cfg, path);\n}\n\n/** True when the onboarding tip for the review/AUTO gate has been shown. */\nexport function editModeHintShown(path: string = defaultConfigPath()): boolean {\n return readConfig(path).editModeHintShown === true;\n}\n\n/** True when the mouse-tracking + clipboard tip has been shown. */\nexport function mouseClipboardHintShown(path: string = defaultConfigPath()): boolean {\n return readConfig(path).mouseClipboardHintShown === true;\n}\n\n/** Unknown / missing fall back to \"max\" so hand-edited bad config can't silently override the default. */\nexport function loadReasoningEffort(path: string = defaultConfigPath()): ReasoningEffort {\n const v = readConfig(path).reasoningEffort;\n return v === \"high\" ? \"high\" : \"max\";\n}\n\nexport function loadTheme(path: string = defaultConfigPath()): ThemeName | \"auto\" | undefined {\n const value = readConfig(path).theme;\n if (value === \"auto\") return \"auto\";\n if (typeof value === \"string\" && isThemeName(value)) return value;\n return undefined;\n}\n\nexport function resolveThemePreference(\n configTheme: ThemeName | \"auto\" | undefined,\n envTheme?: string | null,\n): ThemeName {\n if (configTheme && configTheme !== \"auto\") return configTheme;\n return resolveThemeName(envTheme);\n}\n\nexport function saveTheme(theme: ThemeName | \"auto\", path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.theme = theme;\n writeConfig(cfg, path);\n}\n\n/** Persist the reasoning_effort cap so `/effort high` survives a relaunch. */\nexport function saveReasoningEffort(\n effort: ReasoningEffort,\n path: string = defaultConfigPath(),\n): void {\n const cfg = readConfig(path);\n cfg.reasoningEffort = effort;\n writeConfig(cfg, path);\n}\n\nexport function loadWorkspaceDir(path: string = defaultConfigPath()): string | undefined {\n const v = readConfig(path).workspaceDir;\n return typeof v === \"string\" && v.trim() ? v : undefined;\n}\n\nexport function saveWorkspaceDir(dir: string, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n const trimmed = dir.trim();\n if (trimmed) cfg.workspaceDir = trimmed;\n else cfg.workspaceDir = undefined;\n writeConfig(cfg, path);\n}\n\nexport function loadEditor(path: string = defaultConfigPath()): string | undefined {\n const v = readConfig(path).editor;\n return typeof v === \"string\" && v.trim() ? v : undefined;\n}\n\nexport function saveEditor(editor: string, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n const trimmed = editor.trim();\n if (trimmed) cfg.editor = trimmed;\n else cfg.editor = undefined;\n writeConfig(cfg, path);\n}\n\nexport function loadRecentWorkspaces(path: string = defaultConfigPath()): string[] {\n const v = readConfig(path).recentWorkspaces;\n return Array.isArray(v) ? v.filter((s): s is string => typeof s === \"string\") : [];\n}\n\nconst MAX_RECENT_WORKSPACES = 8;\nexport function pushRecentWorkspace(dir: string, path: string = defaultConfigPath()): void {\n const trimmed = dir.trim();\n if (!trimmed) return;\n const cfg = readConfig(path);\n const list = (cfg.recentWorkspaces ?? []).filter((s) => s !== trimmed);\n list.unshift(trimmed);\n cfg.recentWorkspaces = list.slice(0, MAX_RECENT_WORKSPACES);\n writeConfig(cfg, path);\n}\n\nexport function loadPreset(path: string = defaultConfigPath()): PresetName | undefined {\n return readConfig(path).preset;\n}\n\n/** Persist preset so `/preset pro` (or `/model deepseek-v4-pro`) sticks across relaunches. */\nexport function savePreset(preset: PresetName, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.preset = preset;\n writeConfig(cfg, path);\n}\n\nexport function loadIndexUserConfig(path: string = defaultConfigPath()): IndexUserConfig {\n return readConfig(path).index ?? {};\n}\n\nexport function loadIndexConfig(path: string = defaultConfigPath()): ResolvedIndexConfig {\n return resolveIndexConfig(readConfig(path).index);\n}\n\nexport function saveIndexConfig(user: IndexUserConfig, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.index = user;\n writeConfig(cfg, path);\n}\n\nexport function loadSemanticEmbeddingUserConfig(\n path: string = defaultConfigPath(),\n): SemanticEmbeddingUserConfig {\n return normalizeSemanticEmbeddingUserConfig(readConfig(path).semantic);\n}\n\nexport function saveSemanticEmbeddingConfig(\n user: SemanticEmbeddingUserConfig,\n path: string = defaultConfigPath(),\n): void {\n const cfg = readConfig(path);\n cfg.semantic = normalizeSemanticEmbeddingUserConfig(user);\n writeConfig(cfg, path);\n}\n\nexport function resolveSemanticEmbeddingConfig(\n path: string = defaultConfigPath(),\n): ResolvedEmbeddingConfig {\n const user = loadSemanticEmbeddingUserConfig(path);\n const provider = user.provider ?? \"ollama\";\n if (provider === \"openai-compat\") {\n const baseUrl = user.openaiCompat?.baseUrl?.trim() ?? \"\";\n const apiKey = user.openaiCompat?.apiKey?.trim() ?? \"\";\n const model = user.openaiCompat?.model?.trim() ?? \"\";\n if (!baseUrl) throw new Error(\"OpenAI-compatible embeddings require an API URL.\");\n requireValidUrl(baseUrl, \"OpenAI-compatible API URL\");\n if (!apiKey) throw new Error(\"OpenAI-compatible embeddings require an API key.\");\n if (!model) throw new Error(\"OpenAI-compatible embeddings require a model.\");\n return {\n provider,\n baseUrl,\n apiKey,\n model,\n extraBody: normalizeExtraBody(user.openaiCompat?.extraBody),\n timeoutMs: DEFAULT_TIMEOUT_MS,\n };\n }\n return {\n provider: \"ollama\",\n baseUrl: user.ollama?.baseUrl?.trim() || process.env.OLLAMA_URL || DEFAULT_OLLAMA_URL,\n model: user.ollama?.model?.trim() || process.env.LUCKERR_EMBED_MODEL || DEFAULT_EMBED_MODEL,\n timeoutMs: DEFAULT_TIMEOUT_MS,\n };\n}\n\nexport function redactSemanticEmbeddingConfig(\n user: SemanticEmbeddingUserConfig,\n): SemanticEmbeddingConfigView {\n const normalized = normalizeSemanticEmbeddingUserConfig(user);\n return {\n provider: normalized.provider ?? \"ollama\",\n ollama: {\n baseUrl: normalized.ollama?.baseUrl?.trim() || process.env.OLLAMA_URL || DEFAULT_OLLAMA_URL,\n model:\n normalized.ollama?.model?.trim() || process.env.LUCKERR_EMBED_MODEL || DEFAULT_EMBED_MODEL,\n },\n openaiCompat: {\n baseUrl: normalized.openaiCompat?.baseUrl?.trim() ?? \"\",\n apiKey: normalized.openaiCompat?.apiKey ? redactKey(normalized.openaiCompat.apiKey) : \"\",\n apiKeySet: Boolean(normalized.openaiCompat?.apiKey?.trim()),\n model: normalized.openaiCompat?.model?.trim() ?? \"\",\n extraBody: normalizeExtraBody(normalized.openaiCompat?.extraBody),\n },\n };\n}\n\n/** Mark the onboarding tip as shown so subsequent launches skip it. */\nexport function markEditModeHintShown(path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n if (cfg.editModeHintShown === true) return;\n cfg.editModeHintShown = true;\n writeConfig(cfg, path);\n}\n\n/** Mark the mouse + clipboard tip as shown. */\nexport function markMouseClipboardHintShown(path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n if (cfg.mouseClipboardHintShown === true) return;\n cfg.mouseClipboardHintShown = true;\n writeConfig(cfg, path);\n}\n\n/** Self-hosted DeepSeek-compatible endpoints may issue any token shape, so we only typo-guard here — the real auth check is the first API call against `baseUrl`. */\nexport function isPlausibleKey(key: string): boolean {\n const trimmed = key.trim();\n if (trimmed.length < 16) return false;\n return !/\\s/.test(trimmed);\n}\n\n/** Mask a key for display: `sk-abcd...wxyz`. */\nexport function redactKey(key: string): string {\n if (!key) return \"\";\n if (key.length <= 12) return \"****\";\n return `${key.slice(0, 6)}…${key.slice(-4)}`;\n}\n\nfunction normalizeSemanticEmbeddingUserConfig(\n cfg: SemanticEmbeddingUserConfig | undefined,\n): SemanticEmbeddingUserConfig {\n return {\n provider: cfg?.provider === \"openai-compat\" ? \"openai-compat\" : \"ollama\",\n ollama: {\n baseUrl: normalizeOptionalString(cfg?.ollama?.baseUrl),\n model: normalizeOptionalString(cfg?.ollama?.model),\n },\n openaiCompat: {\n baseUrl: normalizeOptionalString(cfg?.openaiCompat?.baseUrl),\n apiKey: normalizeOptionalString(cfg?.openaiCompat?.apiKey),\n model: normalizeOptionalString(cfg?.openaiCompat?.model),\n extraBody: normalizeExtraBody(cfg?.openaiCompat?.extraBody),\n },\n };\n}\n\nfunction normalizeOptionalString(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction normalizeExtraBody(value: Record<string, unknown> | undefined): Record<string, unknown> {\n if (value === undefined) return {};\n if (!isPlainObject(value)) {\n throw new Error(\"Semantic embedding extraBody must be a JSON object.\");\n }\n return { ...value };\n}\n\nfunction requireValidUrl(value: string, label: string): void {\n try {\n new URL(value);\n } catch {\n throw new Error(`${label} must be a valid URL.`);\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n","export type ThemeName =\n | \"default\"\n | \"dark\"\n | \"light\"\n | \"tokyo-night\"\n | \"github-dark\"\n | \"github-light\"\n | \"high-contrast\";\n\nexport interface ThemeTokens {\n fg: {\n strong: string;\n body: string;\n sub: string;\n meta: string;\n faint: string;\n };\n tone: {\n brand: string;\n accent: string;\n violet: string;\n ok: string;\n warn: string;\n err: string;\n info: string;\n };\n toneActive: ThemeTokens[\"tone\"];\n surface: {\n bg: string;\n bgInput: string;\n bgCode: string;\n bgElev: string;\n };\n card: Record<\n | \"user\"\n | \"reasoning\"\n | \"streaming\"\n | \"task\"\n | \"tool\"\n | \"plan\"\n | \"diff\"\n | \"error\"\n | \"warn\"\n | \"usage\"\n | \"subagent\"\n | \"approval\"\n | \"search\"\n | \"memory\"\n | \"ctx\"\n | \"doctor\"\n | \"branch\",\n { color: string; glyph: string }\n >;\n}\n\ntype ThemeBase = Omit<ThemeTokens, \"card\">;\n\nfunction card(fg: ThemeTokens[\"fg\"], tone: ThemeTokens[\"tone\"]): ThemeTokens[\"card\"] {\n return {\n user: { color: fg.meta, glyph: \"◇\" },\n reasoning: { color: tone.accent, glyph: \"◆\" },\n streaming: { color: tone.brand, glyph: \"◈\" },\n task: { color: tone.warn, glyph: \"▶\" },\n tool: { color: tone.info, glyph: \"▣\" },\n plan: { color: tone.accent, glyph: \"⊞\" },\n diff: { color: tone.ok, glyph: \"±\" },\n error: { color: tone.err, glyph: \"✖\" },\n warn: { color: tone.warn, glyph: \"⚠\" },\n usage: { color: fg.meta, glyph: \"Σ\" },\n subagent: { color: tone.violet, glyph: \"⌬\" },\n approval: { color: tone.warn, glyph: \"?\" },\n search: { color: tone.info, glyph: \"⊙\" },\n memory: { color: fg.meta, glyph: \"⌑\" },\n ctx: { color: tone.brand, glyph: \"◔\" },\n doctor: { color: fg.meta, glyph: \"⚕\" },\n branch: { color: tone.violet, glyph: \"⎇\" },\n };\n}\n\nfunction defineTheme(base: ThemeBase): ThemeTokens {\n return { ...base, card: card(base.fg, base.tone) };\n}\n\nconst githubDark = defineTheme({\n fg: {\n strong: \"#e6edf3\",\n body: \"#c9d1d9\",\n sub: \"#8b949e\",\n meta: \"#6e7681\",\n faint: \"#484f58\",\n },\n tone: {\n brand: \"#79c0ff\",\n accent: \"#d2a8ff\",\n violet: \"#b395f5\",\n ok: \"#7ee787\",\n warn: \"#f0b07d\",\n err: \"#ff8b81\",\n info: \"#79c0ff\",\n },\n toneActive: {\n brand: \"#a5d6ff\",\n accent: \"#e2c5ff\",\n violet: \"#c8aaff\",\n ok: \"#a8f5ad\",\n warn: \"#ffc99e\",\n err: \"#ffaba3\",\n info: \"#a5d6ff\",\n },\n surface: {\n bg: \"#0a0c10\",\n bgInput: \"#0d1015\",\n bgCode: \"#06080c\",\n bgElev: \"#11141a\",\n },\n});\n\nconst dark = defineTheme({\n fg: {\n strong: \"#f4f7fb\",\n body: \"#d8dee9\",\n sub: \"#a7b1c2\",\n meta: \"#778294\",\n faint: \"#4d5666\",\n },\n tone: {\n brand: \"#7dd3fc\",\n accent: \"#c084fc\",\n violet: \"#a78bfa\",\n ok: \"#86efac\",\n warn: \"#fbbf24\",\n err: \"#f87171\",\n info: \"#60a5fa\",\n },\n toneActive: {\n brand: \"#bae6fd\",\n accent: \"#e9d5ff\",\n violet: \"#ddd6fe\",\n ok: \"#bbf7d0\",\n warn: \"#fde68a\",\n err: \"#fecaca\",\n info: \"#bfdbfe\",\n },\n surface: {\n bg: \"#0b1020\",\n bgInput: \"#111827\",\n bgCode: \"#080c16\",\n bgElev: \"#151d2f\",\n },\n});\n\nconst light = defineTheme({\n fg: {\n strong: \"#111827\",\n body: \"#1f2937\",\n sub: \"#4b5563\",\n meta: \"#6b7280\",\n faint: \"#9ca3af\",\n },\n tone: {\n brand: \"#2563eb\",\n accent: \"#7c3aed\",\n violet: \"#6d28d9\",\n ok: \"#15803d\",\n warn: \"#b45309\",\n err: \"#dc2626\",\n info: \"#0369a1\",\n },\n toneActive: {\n brand: \"#1d4ed8\",\n accent: \"#6d28d9\",\n violet: \"#5b21b6\",\n ok: \"#166534\",\n warn: \"#92400e\",\n err: \"#b91c1c\",\n info: \"#075985\",\n },\n surface: {\n bg: \"#ffffff\",\n bgInput: \"#f8fafc\",\n bgCode: \"#f3f4f6\",\n bgElev: \"#eef2f7\",\n },\n});\n\nconst tokyoNight = defineTheme({\n fg: {\n strong: \"#c0caf5\",\n body: \"#a9b1d6\",\n sub: \"#9aa5ce\",\n meta: \"#565f89\",\n faint: \"#414868\",\n },\n tone: {\n brand: \"#7aa2f7\",\n accent: \"#bb9af7\",\n violet: \"#9d7cd8\",\n ok: \"#9ece6a\",\n warn: \"#e0af68\",\n err: \"#f7768e\",\n info: \"#2ac3de\",\n },\n toneActive: {\n brand: \"#a9c7ff\",\n accent: \"#d7b9ff\",\n violet: \"#c6a0f6\",\n ok: \"#b9f27c\",\n warn: \"#ffd089\",\n err: \"#ff9cac\",\n info: \"#7dcfff\",\n },\n surface: {\n bg: \"#1a1b26\",\n bgInput: \"#1f2335\",\n bgCode: \"#16161e\",\n bgElev: \"#24283b\",\n },\n});\n\nconst githubLight = defineTheme({\n fg: {\n strong: \"#1f2328\",\n body: \"#24292f\",\n sub: \"#57606a\",\n meta: \"#6e7781\",\n faint: \"#8c959f\",\n },\n tone: {\n brand: \"#0969da\",\n accent: \"#8250df\",\n violet: \"#6639ba\",\n ok: \"#1a7f37\",\n warn: \"#9a6700\",\n err: \"#cf222e\",\n info: \"#0969da\",\n },\n toneActive: {\n brand: \"#0550ae\",\n accent: \"#6639ba\",\n violet: \"#512a97\",\n ok: \"#116329\",\n warn: \"#7d4e00\",\n err: \"#a40e26\",\n info: \"#0550ae\",\n },\n surface: {\n bg: \"#ffffff\",\n bgInput: \"#f6f8fa\",\n bgCode: \"#f6f8fa\",\n bgElev: \"#eaeef2\",\n },\n});\n\nconst highContrast = defineTheme({\n fg: {\n strong: \"#ffffff\",\n body: \"#f5f5f5\",\n sub: \"#d4d4d4\",\n meta: \"#bdbdbd\",\n faint: \"#8a8a8a\",\n },\n tone: {\n brand: \"#00e5ff\",\n accent: \"#ff4dff\",\n violet: \"#b388ff\",\n ok: \"#00ff66\",\n warn: \"#ffdd00\",\n err: \"#ff4d4d\",\n info: \"#4da3ff\",\n },\n toneActive: {\n brand: \"#80f2ff\",\n accent: \"#ff99ff\",\n violet: \"#d0b3ff\",\n ok: \"#80ffb3\",\n warn: \"#ffee80\",\n err: \"#ff9999\",\n info: \"#99c9ff\",\n },\n surface: {\n bg: \"#000000\",\n bgInput: \"#0a0a0a\",\n bgCode: \"#050505\",\n bgElev: \"#141414\",\n },\n});\n\nexport const THEMES = {\n default: githubDark,\n dark,\n light,\n \"tokyo-night\": tokyoNight,\n \"github-dark\": githubDark,\n \"github-light\": githubLight,\n \"high-contrast\": highContrast,\n} as const satisfies Record<ThemeName, ThemeTokens>;\n\nexport const DEFAULT_THEME_NAME: ThemeName = \"default\";\n\nexport function isThemeName(value: string): value is ThemeName {\n return Object.prototype.hasOwnProperty.call(THEMES, value);\n}\n\nexport function resolveThemeName(value?: string | null): ThemeName {\n if (!value || value === \"auto\") return DEFAULT_THEME_NAME;\n return isThemeName(value) ? value : DEFAULT_THEME_NAME;\n}\n\nexport function listThemeNames(): ThemeName[] {\n return Object.keys(THEMES) as ThemeName[];\n}\n\nexport function themeTokens(name?: string | null): ThemeTokens {\n return THEMES[resolveThemeName(name)];\n}\n\nexport const DEFAULT_THEME = THEMES[DEFAULT_THEME_NAME];\n\nlet activeTheme: ThemeTokens = DEFAULT_THEME;\nlet activeThemeVersion = 0;\n\nexport function setActiveTheme(theme: ThemeTokens): () => void {\n const previousTheme = activeTheme;\n activeTheme = theme;\n activeThemeVersion += 1;\n const version = activeThemeVersion;\n return () => {\n if (activeThemeVersion !== version || activeTheme !== theme) return;\n activeTheme = previousTheme;\n activeThemeVersion += 1;\n };\n}\n\nfunction proxyTokens<T extends object>(select: (theme: ThemeTokens) => T): T {\n const target = select(DEFAULT_THEME);\n return new Proxy(target, {\n get(_target, prop: string | symbol) {\n return select(activeTheme)[prop as keyof T];\n },\n getOwnPropertyDescriptor(_target, prop: string | symbol) {\n return Reflect.getOwnPropertyDescriptor(select(activeTheme), prop);\n },\n has(_target, prop: string | symbol) {\n return prop in select(activeTheme);\n },\n ownKeys() {\n return Reflect.ownKeys(select(activeTheme));\n },\n });\n}\n\nexport const FG = proxyTokens((theme) => theme.fg);\nexport const TONE = proxyTokens((theme) => theme.tone);\nexport const TONE_ACTIVE = proxyTokens((theme) => theme.toneActive);\nexport const SURFACE = proxyTokens((theme) => theme.surface);\nexport const CARD = proxyTokens((theme) => theme.card);\n\nexport type CardTone = keyof ThemeTokens[\"card\"];\n\n/** DeepSeek prices in CNY; our internal table is USD divided by 7.2. Multiply back for display. */\nexport const USD_TO_CNY = 7.2;\n\nconst SYMBOL: Record<string, string> = { USD: \"$\", CNY: \"¥\" };\n\n/** Format an amount already in `currency`. Undefined currency → CNY (matches pre-fix behavior). */\nexport function formatBalance(\n amount: number,\n currency?: string,\n opts?: { fractionDigits?: number; label?: boolean },\n): string {\n const cur = currency ?? \"CNY\";\n const sym = SYMBOL[cur];\n const digits = opts?.fractionDigits ?? 2;\n const body = sym ? `${sym}${amount.toFixed(digits)}` : `${cur} ${amount.toFixed(digits)}`;\n return opts?.label ? `w ${body}` : body;\n}\n\n/** Format an internal USD cost in the wallet's display currency. Undefined currency → CNY. */\nexport function formatCost(costUsd: number, currency?: string, fractionDigits = 4): string {\n const cur = currency ?? \"CNY\";\n const amount = cur === \"CNY\" ? costUsd * USD_TO_CNY : costUsd;\n return formatBalance(amount, cur, { fractionDigits });\n}\n\n/** Threshold color for a wallet balance. USD is converted to CNY before the threshold check. */\nexport function balanceColor(amount: number, currency?: string): string {\n const cny = (currency ?? \"CNY\") === \"USD\" ? amount * USD_TO_CNY : amount;\n if (cny < 5) return TONE.err;\n if (cny < 20) return TONE.warn;\n return TONE.brand;\n}\n","/** Shared exclude defaults + resolver — chunker, directory_tree, and dashboard read from here. */\n\nimport picomatch from \"picomatch\";\n\nexport interface IndexUserConfig {\n excludeDirs?: string[];\n excludeFiles?: string[];\n excludeExts?: string[];\n excludePatterns?: string[];\n respectGitignore?: boolean;\n maxFileBytes?: number;\n}\n\n/** Plain-data shape — JSON-safe so the dashboard endpoint can serialize. */\nexport interface ResolvedIndexConfig {\n excludeDirs: readonly string[];\n excludeFiles: readonly string[];\n excludeExts: readonly string[];\n excludePatterns: readonly string[];\n respectGitignore: boolean;\n maxFileBytes: number;\n}\n\n/** Hot-path lookup wrapper — built once per indexer run, never serialized. */\nexport interface IndexFilters {\n dirSet: ReadonlySet<string>;\n fileSet: ReadonlySet<string>;\n extSet: ReadonlySet<string>;\n patternMatch: (relPath: string) => boolean;\n respectGitignore: boolean;\n maxFileBytes: number;\n}\n\nexport const DEFAULT_INDEX_EXCLUDES = {\n dirs: [\n \"node_modules\",\n \".git\",\n \".hg\",\n \".svn\",\n \"dist\",\n \"build\",\n \"out\",\n \".next\",\n \".nuxt\",\n \"target\",\n \".venv\",\n \"venv\",\n \"__pycache__\",\n \".pytest_cache\",\n \".mypy_cache\",\n \".cache\",\n \"coverage\",\n \".turbo\",\n \".vercel\",\n \".luckerr\",\n ] as const,\n files: [\n \"package-lock.json\",\n \"yarn.lock\",\n \"pnpm-lock.yaml\",\n \"Cargo.lock\",\n \"poetry.lock\",\n \"Pipfile.lock\",\n \"go.sum\",\n \".DS_Store\",\n ] as const,\n exts: [\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".webp\",\n \".bmp\",\n \".ico\",\n \".tiff\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".otf\",\n \".eot\",\n \".zip\",\n \".tar\",\n \".gz\",\n \".bz2\",\n \".xz\",\n \".rar\",\n \".7z\",\n \".exe\",\n \".dll\",\n \".so\",\n \".dylib\",\n \".bin\",\n \".class\",\n \".jar\",\n \".war\",\n \".wasm\",\n \".o\",\n \".obj\",\n \".lib\",\n \".a\",\n \".pyc\",\n \".pyo\",\n \".mp3\",\n \".mp4\",\n \".wav\",\n \".ogg\",\n \".webm\",\n \".mov\",\n \".avi\",\n \".pdf\",\n \".sqlite\",\n \".db\",\n ] as const,\n} as const;\n\nexport const DEFAULT_MAX_FILE_BYTES = 256 * 1024;\nexport const DEFAULT_RESPECT_GITIGNORE = true;\n\nexport function defaultIndexConfig(): ResolvedIndexConfig {\n return {\n excludeDirs: [...DEFAULT_INDEX_EXCLUDES.dirs],\n excludeFiles: [...DEFAULT_INDEX_EXCLUDES.files],\n excludeExts: [...DEFAULT_INDEX_EXCLUDES.exts],\n excludePatterns: [],\n respectGitignore: DEFAULT_RESPECT_GITIGNORE,\n maxFileBytes: DEFAULT_MAX_FILE_BYTES,\n };\n}\n\n/** A field present in user config fully replaces the default for that field. Absent → default. */\nexport function resolveIndexConfig(user?: IndexUserConfig | null): ResolvedIndexConfig {\n const d = defaultIndexConfig();\n if (!user) return d;\n return {\n excludeDirs: Array.isArray(user.excludeDirs) ? [...user.excludeDirs] : d.excludeDirs,\n excludeFiles: Array.isArray(user.excludeFiles) ? [...user.excludeFiles] : d.excludeFiles,\n excludeExts: Array.isArray(user.excludeExts)\n ? user.excludeExts.map((e) => e.toLowerCase())\n : d.excludeExts,\n excludePatterns: Array.isArray(user.excludePatterns) ? [...user.excludePatterns] : [],\n respectGitignore:\n typeof user.respectGitignore === \"boolean\" ? user.respectGitignore : d.respectGitignore,\n maxFileBytes:\n typeof user.maxFileBytes === \"number\" && user.maxFileBytes > 0\n ? user.maxFileBytes\n : d.maxFileBytes,\n };\n}\n\nexport function compileFilters(cfg: ResolvedIndexConfig): IndexFilters {\n const matcher =\n cfg.excludePatterns.length === 0\n ? () => false\n : picomatch(cfg.excludePatterns as string[], { dot: true });\n return {\n dirSet: new Set(cfg.excludeDirs),\n fileSet: new Set(cfg.excludeFiles),\n extSet: new Set(cfg.excludeExts.map((e) => e.toLowerCase())),\n patternMatch: matcher as (p: string) => boolean,\n respectGitignore: cfg.respectGitignore,\n maxFileBytes: cfg.maxFileBytes,\n };\n}\n","import type { TranslationSchema } from \"./types.js\";\n\nexport const EN: TranslationSchema = {\n common: {\n error: \"Error\",\n warning: \"Warning\",\n loading: \"Loading...\",\n done: \"Done\",\n cancel: \"Cancel\",\n confirm: \"Confirm\",\n back: \"Back\",\n next: \"Next\",\n tool: \"tool\",\n running: \"running\",\n noTurns: \"(no turns yet)\",\n },\n cli: {\n description: \"DeepSeek-native agent framework — built for cache hits and cheap tokens.\",\n continue: \"Resume the most recently used chat session without showing the picker.\",\n setup: \"Interactive wizard — API key, preset, MCP servers. Re-run any time to reconfigure.\",\n code: \"Code-editing chat — filesystem tools rooted at <dir> (default: cwd), coding system prompt, v4-flash baseline.\",\n chat: \"Interactive Ink TUI with live cache/cost panel.\",\n run: \"Run a single task non-interactively, streaming output.\",\n stats: \"Show usage dashboard.\",\n doctor: \"One-command health check.\",\n commit: \"Draft a commit message from the staged diff.\",\n sessions: \"List saved chat sessions, or inspect one by name.\",\n pruneSessions: \"Delete saved sessions idle ≥N days (default 90). Use --dry-run to preview.\",\n events: \"Pretty-print the kernel event-log sidecar.\",\n replay: \"Interactive Ink TUI to scrub through a transcript.\",\n diff: \"Compare two transcripts in a split-pane Ink TUI.\",\n mcp: \"Model Context Protocol helpers — discover servers, test your setup.\",\n version: \"Print Luckerr version.\",\n update: \"Check for a newer Luckerr and install it.\",\n index: \"Build (or incrementally refresh) a local semantic search index.\",\n },\n ui: {\n welcome: \"Run `luckerr` any time to start chatting — your settings are remembered.\",\n taglineChat: \"DeepSeek-native agent\",\n taglineCode: \"DeepSeek-native coding agent\",\n taglineSub: \"cache-first · flash-first\",\n startSessionHint: \"type a message to start your session\",\n inputPlaceholder: \"Ask anything... (type / for commands, @ for files)\",\n busy: \"Thinking...\",\n thinking: \"▸ thinking...\",\n undo: \"Undo\",\n undoHint: \"press u within 5s to undo\",\n applied: \"applied\",\n rejected: \"rejected\",\n noDashboard: \"Suppress the auto-launched embedded web dashboard.\",\n dashboardPortHint:\n \"Pin the dashboard to a fixed port (1–65535). Stable across restarts — required for SSH tunnels. Default: ephemeral.\",\n dashboardPortInvalid:\n \"▲ ignoring --dashboard-port={value} (must be an integer 1–65535) — falling back to ephemeral\",\n dashboardAutoStartFailed:\n \"▲ dashboard auto-start failed ({reason}) — try /dashboard, or pass --no-dashboard to silence\",\n systemAppendHint:\n \"Append instructions to the code system prompt. Does NOT replace the default prompt — adds after it.\",\n systemAppendFileHint:\n \"Append file contents to the code system prompt. Does NOT replace the default prompt. UTF-8, relative to cwd or absolute.\",\n resumedSession:\n '▸ resumed session \"{name}\" with {count} prior messages · /new to start fresh · /sessions to manage',\n newSession: '▸ session \"{name}\" (new) — auto-saved as you chat · /sessions to rename or delete',\n ephemeralSession: \"▸ ephemeral chat (no session persistence) — drop --no-session to enable\",\n restoredEdits:\n \"▸ restored {count} pending edit block(s) from an interrupted prior run — /apply to commit or /discard to drop.\",\n resumedPlan: \"Resumed plan · {when}{summary}\",\n tipEditBindings: {\n topic: \"edit-gate keybindings\",\n sections: [\n {\n rows: [\n { key: \"y / n\", text: \"accept or drop pending edits\" },\n {\n key: \"Shift+Tab\",\n text: \"switch review ↔ AUTO (persisted; AUTO applies instantly)\",\n },\n { key: \"u\", text: \"undo the last auto-applied batch (within the 5s banner)\" },\n ],\n },\n ],\n footer: \"Current mode shown in the bottom status bar · /keys for the full reference\",\n },\n tipMouseClipboard: {\n topic: \"mouse + clipboard\",\n sections: [\n {\n rows: [\n { key: \"drag\", text: \"select text — terminal-native, no modifier needed\" },\n {\n key: \"right-click\",\n text: \"your terminal's native menu (paste / copy on Windows Terminal etc.)\",\n },\n { key: \"wheel\", text: \"scrolls chat history (works on web/cloud/SSH terminals too)\" },\n {\n key: \"↑ / ↓\",\n text: \"scroll chat · use Ctrl+P / Ctrl+N for prompt history + multi-line cursor\",\n },\n ],\n },\n ],\n footer: \"Run /keys for the full keyboard + mouse reference\",\n },\n keysReference: {\n topic: \"Luckerr keys + mouse reference\",\n sections: [\n {\n title: \"keyboard\",\n rows: [\n { key: \"Enter\", text: \"submit the prompt\" },\n { key: \"Shift+Enter\", text: \"insert a newline in the prompt\" },\n { key: \"↑ / ↓\", text: \"scroll chat history (mouse wheel routes here too)\" },\n {\n key: \"Ctrl+P / Ctrl+N\",\n text: \"previous / next prompt history · cursor up / down in a multi-line draft\",\n },\n { key: \"Ctrl+A / Ctrl+E\", text: \"jump to start / end of the current line\" },\n { key: \"Ctrl+W\", text: \"delete the word before the cursor\" },\n { key: \"Ctrl+U\", text: \"clear the entire prompt buffer\" },\n { key: \"Tab\", text: \"complete @-mention · drill folder · accept slash command\" },\n { key: \"Shift+Tab\", text: \"edit-gate: toggle review ↔ AUTO mode\" },\n { key: \"Esc\", text: \"dismiss picker · abort the running model turn\" },\n { key: \"Ctrl+C\", text: \"abort the running model turn (NOT copy — see clipboard)\" },\n { key: \"PgUp / PgDn\", text: \"scroll chat history a page at a time\" },\n { key: \"End\", text: \"jump chat to the most recent line\" },\n ],\n },\n {\n title: \"mouse\",\n rows: [\n { key: \"wheel\", text: \"scrolls chat history (works on web/cloud/SSH terminals too)\" },\n { key: \"drag\", text: \"selects text natively — direct copy works, no modifier\" },\n { key: \"right-click\", text: \"terminal-native (paste menu on Windows Terminal etc.)\" },\n ],\n },\n {\n title: \"copy / paste\",\n rows: [\n { key: \"select text\", text: \"drag to select — terminal-native (no modifier needed)\" },\n {\n key: \"/copy\",\n text: \"vim/tmux-style copy mode — works in SSH/mosh/tmux where drag-select can't extend past the viewport\",\n },\n {\n key: \"copy\",\n text: \"Ctrl+Shift+C (Win/Linux) · Cmd+C (macOS) — or auto-copy-on-select if your terminal does it\",\n },\n { key: \"paste\", text: \"Ctrl+V or Ctrl+Shift+V (Win/Linux) · Cmd+V (macOS)\" },\n {\n key: \"bracketed paste\",\n text: \"multi-line pastes stay one block — no auto-submit on intermediate newlines\",\n },\n ],\n },\n {\n title: \"edit-gate (code mode)\",\n rows: [\n { key: \"y / n\", text: \"accept or drop pending edits in the review modal\" },\n { key: \"Shift+Tab\", text: \"toggle review ↔ AUTO (persisted across sessions)\" },\n { key: \"u\", text: \"undo the last auto-applied batch (within the 5s banner)\" },\n ],\n },\n ],\n footer:\n \"Wheel→↑/↓ via DECSET 1007 (alternate-scroll) — wheel scrolls chat on most terminals (web/cloud/SSH included) without disturbing native selection. Drag to select stays modifier-free. Pass --no-mouse to opt out.\",\n },\n tipShownOnce: \"shown once\",\n modelOverride: \"override the default model\",\n noSession: \"disable session persistence for this run\",\n resumeHint: \"force-resume the named session (even if idle)\",\n newHint: \"force a fresh session (ignore --session / --continue)\",\n transcriptHint: \"path to write the JSONL transcript\",\n budgetHint: \"session USD cap — warns at 80%, refuses next turn at 100%\",\n modelIdHint: \"DeepSeek model id (e.g. deepseek-v4-flash)\",\n systemPromptHint: \"override the default system prompt\",\n presetHint: \"model bundle — auto|flash|pro\",\n sessionNameHint: \"session name (default: 'default')\",\n ephemeralHint: \"disable session persistence for this run\",\n mcpSpecHint: \"MCP server spec (repeatable)\",\n mcpPrefixHint: \"prefix MCP tool names with this string\",\n noConfigHint: \"ignore ~/.luckerr/config.json for this run\",\n presetHintShort: \"model bundle — auto|flash|pro\",\n budgetHintShort: \"session USD cap\",\n transcriptHintShort: \"JSONL transcript path\",\n mcpSpecHintShort: \"MCP server spec (repeatable)\",\n mcpPrefixHintShort: \"MCP tool name prefix\",\n dryRunHint: \"show what would be installed without actually installing\",\n rebuildHint: \"rebuild the index from scratch\",\n embedModelHint: \"embedding model name\",\n projectDirHint: \"project root directory\",\n ollamaUrlHint: \"Ollama server URL\",\n skipPromptsHint: \"skip confirmation prompts\",\n verboseHint: \"show full session metadata\",\n pruneDaysHint: \"delete sessions idle this many days or more (default 90)\",\n pruneDryRunHint: \"list what would be deleted without removing anything\",\n eventTypeHint: \"filter by event type\",\n eventSinceHint: \"start from this event id\",\n eventTailHint: \"show only the last N events\",\n jsonHint: \"output as JSON\",\n projectionHint: \"show projected state at each event\",\n printHint: \"print to stdout instead of TUI\",\n headHint: \"show only the first N events\",\n tailHint: \"show only the last N events\",\n mdReportHint: \"write a markdown diff report to this path\",\n printHintTable: \"print a table to stdout\",\n tuiHint: \"open the interactive TUI\",\n labelAHint: \"label for the left pane\",\n labelBHint: \"label for the right pane\",\n mcpListDescription: \"browse the MCP registry (official → smithery → local fallback)\",\n mcpInspectDescription: \"inspect an MCP server spec (tools, resources, prompts)\",\n mcpSearchDescription: \"search the MCP registry for servers matching a query\",\n mcpInstallDescription: \"install an MCP server by name (writes its spec to your config)\",\n mcpBrowseDescription: \"interactive marketplace browser — type to filter, enter to install\",\n mcpLocalHint: \"show only the bundled offline catalog\",\n mcpRefreshHint: \"bypass the 24h cache and refetch\",\n mcpLimitHint: \"max entries to show\",\n mcpPagesHint: \"eagerly load this many pages (default 1)\",\n mcpAllHint: \"load every page (slow on first run)\",\n mcpMaxPagesHint: \"cap how many pages to walk while searching (default 20)\",\n jsonHintCatalog: \"output as JSON\",\n jsonHintReport: \"output the inspection report as JSON\",\n modelOverrideFlash: \"override the model (default: deepseek-v4-flash)\",\n skipConfirmHint: \"skip the confirmation prompt\",\n },\n slash: {\n help: { description: \"show the full command reference\" },\n copy: {\n description: \"open vim/tmux-style copy mode — j/k navigate, v select, y yank to clipboard\",\n },\n status: { description: \"current model, flags, context, session\" },\n preset: {\n description: \"model bundle — auto escalates flash → pro, flash/pro lock\",\n argsHint: \"<auto|flash|pro>\",\n },\n model: { description: \"switch DeepSeek model id\", argsHint: \"<id>\" },\n models: { description: \"list available models fetched from DeepSeek /models\" },\n theme: {\n description: \"show or persist the terminal theme preference. Bare opens picker.\",\n argsHint: \"[auto|default|dark|light|tokyo-night|github-dark|github-light|high-contrast]\",\n },\n language: {\n description: \"switch the runtime language\",\n argsHint: \"<EN|zh-CN>\",\n success: \"Language switched to English.\",\n unsupported: \"Unsupported language code: {code}. Supported: {supported}.\",\n },\n pro: {\n description: \"arm v4-pro for the NEXT turn only (one-shot · auto-disarms after turn)\",\n argsHint: \"[off]\",\n },\n budget: {\n description:\n \"session USD cap — warns at 80%, refuses next turn at 100%. Off by default. /budget alone shows status\",\n argsHint: \"[usd|off]\",\n },\n mcp: { description: \"list MCP servers + tools attached to this session\" },\n resource: {\n description: \"browse + read MCP resources (no arg → list URIs; <uri> → fetch contents)\",\n argsHint: \"[uri]\",\n },\n prompt: {\n description: \"browse + fetch MCP prompts (no arg → list names; <name> → render prompt)\",\n argsHint: \"[name]\",\n },\n memory: {\n description: \"show / manage pinned memory (LUCKERR.md + ~/.luckerr/memory)\",\n argsHint: \"[list|show <name>|forget <name>|clear <scope> confirm]\",\n },\n skill: {\n description: \"list / run user skills (<project>/.luckerr/skills + ~/.luckerr/skills)\",\n argsHint: \"[list|show <name>|<name> [args]]\",\n },\n hooks: {\n description: \"list active hooks (settings.json under .luckerr/) · reload re-reads from disk\",\n argsHint: \"[reload]\",\n },\n permissions: {\n description:\n \"show / edit shell allowlist (builtin read-only · per-project: ~/.luckerr/config.json)\",\n argsHint: \"[list|add <prefix>|remove <prefix|N>|clear confirm]\",\n },\n dashboard: {\n description: \"launch the embedded web dashboard (127.0.0.1, token-gated)\",\n argsHint: \"[stop]\",\n },\n update: { description: \"show current vs latest version + the shell command to upgrade\" },\n stats: {\n description:\n \"cross-session cost dashboard (today / week / month / all-time · cache hit · vs Claude)\",\n },\n cost: {\n description:\n \"bare → last turn's spend (Usage card); with text → estimate cost of sending it next (worst-case + likely-cache)\",\n argsHint: \"[text]\",\n },\n doctor: { description: \"health check (api / config / api-reach / index / hooks / project)\" },\n context: { description: \"show context-window breakdown (system / tools / log / input)\" },\n retry: { description: \"truncate & resend your last message (fresh sample)\" },\n compact: {\n description:\n \"narrow oversized tool results + tool-call args in the log; cap at tokens, default 4000\",\n argsHint: \"[tokens]\",\n },\n cwd: {\n description:\n \"switch the workspace root mid-session — re-points fs / shell / memory tools, reloads project hooks, refreshes the at-mention walker\",\n argsHint: \"<path>\",\n },\n stop: { description: \"abort the current model turn (typed alternative to Esc)\" },\n feedback: { description: \"open a GitHub issue with diagnostic info copied to clipboard\" },\n keys: { description: \"keyboard + mouse + copy/paste reference\" },\n plans: { description: \"list this session's active + archived plans, newest first\" },\n replay: {\n description: \"load an archived plan as a read-only Time Travel snapshot (default: newest)\",\n argsHint: \"[N]\",\n },\n sessions: { description: \"list saved sessions (current marked with ▸)\" },\n setup: { description: \"reminds you to exit and run `luckerr setup`\" },\n semantic: {\n description: \"show semantic_search status — built? Ollama installed? how to enable\",\n },\n clear: { description: \"clear visible scrollback only (log/context kept)\" },\n new: { description: \"start a fresh conversation (clear context + scrollback)\" },\n loop: {\n description:\n \"auto-resubmit <prompt> every <interval> until you type something / Esc / /loop stop\",\n argsHint: \"<5s..6h> <prompt> · stop · (no args = status)\",\n },\n exit: { description: \"quit the TUI\" },\n init: {\n description:\n \"scan the project and synthesize a baseline LUCKERR.md (model writes; review with /apply). `force` overwrites an existing file.\",\n argsHint: \"[force]\",\n },\n apply: {\n description:\n \"commit pending edit blocks to disk (no arg → all; `1`, `1,3`, or `1-4` → that subset, rest stay pending)\",\n argsHint: \"[N|N,M|N-M]\",\n },\n discard: {\n description: \"drop pending edit blocks without writing (no arg → all; indices → that subset)\",\n argsHint: \"[N|N,M|N-M]\",\n },\n walk: {\n description:\n \"step through pending edits one block at a time (git-add-p style: y/n per block, a apply rest, A flip AUTO)\",\n },\n undo: { description: \"roll back the last applied edit batch\" },\n history: { description: \"list every edit batch this session (ids for /show, undone markers)\" },\n show: {\n description: \"dump a stored edit diff (omit id for newest non-undone)\",\n argsHint: \"[id]\",\n },\n commit: { description: \"git add -A && git commit -m ...\", argsHint: '\"msg\"' },\n checkpoint: {\n description:\n \"snapshot every file the session has touched (Cursor-style internal store, not git). /checkpoint alone lists.\",\n argsHint: \"[name|list|forget <id>]\",\n },\n restore: {\n description: \"roll back files to a named checkpoint (see /checkpoint list)\",\n argsHint: \"<name|id>\",\n },\n plan: {\n description: \"toggle read-only plan mode (writes bounced until submit_plan + approval)\",\n argsHint: \"[on|off]\",\n },\n mode: {\n description:\n \"edit-gate: review (queue) · auto (apply+undo) · yolo (apply+auto-shell). Shift+Tab cycles.\",\n argsHint: \"[review|auto|yolo]\",\n },\n jobs: { description: \"list background jobs started by run_background\" },\n kill: {\n description: \"stop a background job by id (SIGTERM → SIGKILL after grace)\",\n argsHint: \"<id>\",\n },\n logs: {\n description: \"tail a background job's output (default last 80 lines)\",\n argsHint: \"<id> [lines]\",\n },\n },\n wizard: {\n languageTitle: \"Choose your language\",\n languageSubtitle: \"Detected from your system locale. Switch later via /language.\",\n welcomeTitle: \"Welcome to Luckerr.\",\n apiKeyPrompt: \"Paste your DeepSeek API key to get started.\",\n apiKeyGetOne: \"Get one at: https://platform.deepseek.com/api_keys\",\n apiKeySavedLocally: \"Saved locally to {path}\",\n apiKeyInputLabel: \"key › \",\n apiKeyInvalid: \"Key looks too short — paste the full token (16+ chars, no spaces).\",\n apiKeyChecking: \"Checking API key…\",\n apiKeyRejected:\n \"DeepSeek rejected this API key. Paste a valid key, or press Esc to cancel setup.\",\n apiKeyCheckFailed:\n \"Could not verify this API key right now ({message}). Check your network or try again.\",\n apiKeyPreview: \"preview: {redacted}\",\n themeTitle: \"Choose a theme\",\n themeSubtitle: \"Preview updates live as you navigate. Change later with /theme.\",\n themeSampleHeading: \"Sample\",\n themeFooter: \"[↑↓] navigate · [Enter] confirm · [Esc] cancel\",\n themeCaption: {\n default: \"GitHub dark (default)\",\n dark: \"Cool dark tones\",\n light: \"Clean light mode\",\n \"tokyo-night\": \"Tokyo Night palette\",\n \"github-dark\": \"GitHub dark\",\n \"github-light\": \"GitHub light\",\n \"high-contrast\": \"Accessibility\",\n },\n reviewLabelTheme: \"Theme\",\n presetTitle: \"Pick a preset\",\n mcpTitle: \"Which MCP servers should Luckerr wire up for you?\",\n mcpUserArgsHint: \"(you'll provide {arg})\",\n mcpFooterMulti:\n \"[↑↓] navigate · [Space] toggle · [Enter] confirm · [Esc] cancel · empty = skip\",\n mcpArgsTitle: \"Configure {name}\",\n mcpArgsDirMissing: \"Directory {path} doesn't exist.\",\n mcpArgsDirCreateHint: \"[Y/Enter] create it (mkdir -p) · [N/Esc] enter a different path\",\n mcpArgsDirCreateFailed: \"Couldn't create {path}: {message}\",\n mcpArgsRequiredParam: \"Required parameter: \",\n mcpArgsEmpty: \"{name} needs a value — got an empty string.\",\n mcpArgsNotADir: \"{path} exists but is not a directory.\",\n reviewTitle: \"Ready to save\",\n reviewLabelApiKey: \"API key\",\n reviewLabelLanguage: \"Language\",\n reviewLabelPreset: \"Preset\",\n reviewLabelMcp: \"MCP\",\n reviewMcpNone: \"(none)\",\n reviewMcpServers: \"{count} server(s)\",\n reviewSavesTo: \"Saves to {path}\",\n reviewSaveError: \"Could not save config: {message}\",\n reviewFooter: \"[Enter] save · [Esc] cancel\",\n savedTitle: \"▸ Saved.\",\n savedFooter: \"[Enter] to exit\",\n selectFooter: \"[↑↓] navigate · [Enter] confirm · [Esc] cancel\",\n stepCounter: \"Step {step}/{total} · \",\n exitHint: \"/exit to abort\",\n apiKeyPlaceholder: \"sk-...\",\n themeSampleReasoning: \"Reasoning\",\n providerKeysTitle: \"Configure your AI providers\",\n providerKeysSubtitle: \"Enter API keys for the providers you want to use. At least one is required.\",\n providerKeysConfigured: \"{count} provider(s) configured\",\n providerKeysNone: \"(none — at least one required)\",\n providerKeysFooter: \"[↑↓] navigate · [Enter] configure · [Esc] cancel · [Space] confirm all\",\n providerKeysInputFor: \"API key for {label}:\",\n providerKeysGetOne: \"Get one at: {url}\",\n providerKeysDetected: \"(detected from env)\",\n providerKeysFromConfig: \"(saved in config)\",\n providerKeysNeeded: \"At least one API key is required to continue.\",\n reviewLabelProviders: \"AI providers\",\n },\n themePicker: {\n header: \"Theme\",\n footer: \"↑↓ pick · ⏎ confirm · esc cancel\",\n currentPref: \"current preference\",\n activeNow: \"active now\",\n autoDesc: \"use LUCKERR_THEME or default\",\n },\n planFlow: {\n approveCardTitle: \"Approve plan\",\n approveCardMetaRight: \"awaiting\",\n openQuestionsBanner:\n \"▲ the plan flags open questions or risks — pick {refine} to write concrete answers before the model moves on.\",\n openQuestionsHeader: \"Open questions / risks\",\n truncatedBodyMore: \"… {n} more line above in scrollback\",\n truncatedBodyMorePlural: \"… {n} more lines above in scrollback\",\n picker: {\n accept: \"accept\",\n acceptHint: \"run it now, in order\",\n refine: \"refine\",\n refineHint: \"give the agent more guidance, draft a new plan\",\n revise: \"revise\",\n reviseHint: \"edit the plan inline before running (skip / reorder steps)\",\n reject: \"reject\",\n rejectHint: \"discard, agent will retry from scratch\",\n },\n refineFooter: \"⏎ send · esc return to picker\",\n refineQuestionsHeading: \"Answer these or describe the change you want:\",\n modes: {\n approve: {\n title: \"approving — any last instructions?\",\n hint: \"Answer questions the plan raised, add constraints, or just press Enter to approve as-is.\",\n blankHint: \" (Enter with blank = approve without extra instructions.)\",\n },\n refine: {\n title: \"refining — what should the model change?\",\n hint: \"Describe what's wrong or missing, or answer questions the plan raised.\",\n blankHint: \" (Enter with blank = let the model pick safe defaults for any open questions.)\",\n },\n reject: {\n title: \"rejecting — tell the model why (optional)\",\n hint: \"Say what the model got wrong about your goal, or what you actually want instead.\",\n blankHint:\n \" (Enter with blank = cancel without explanation; the model will ask what you want.)\",\n },\n \"checkpoint-revise\": {\n title: \"revising — what should change before the next step?\",\n hint: \"Scope change, skip steps, alternative approach — the model adjusts the remaining plan.\",\n blankHint: \" (Enter with blank = continue with the current plan.)\",\n },\n \"choice-custom\": {\n title: \"custom answer — type whatever fits\",\n hint: \"Free-form reply. The model reads it verbatim and proceeds — no need to match the listed options.\",\n blankHint: \" (Enter with blank = ask the model what you actually want.)\",\n },\n },\n checkpoint: {\n title: \"Checkpoint — step done\",\n continue: \"Continue — run the next step\",\n continueHint: \"Model resumes with the next step.\",\n revise: \"Revise — give feedback before the next step\",\n reviseHint: \"Stay paused, type guidance; model adjusts the remaining plan.\",\n stop: \"Stop — end the plan here\",\n stopHint: \"Model summarizes what was done and ends.\",\n },\n stepList: {\n counter: \"{total} steps\",\n counterSingular: \"{total} step\",\n counterDone: \"{done}/{total} done ({pct}%) · {total} steps\",\n counterDoneSingular: \"{done}/{total} done ({pct}%) · {total} step\",\n },\n noPlanSummary: \"No plan body submitted yet.\",\n detailCollapsedHint: \"Ctrl+P expands full plan details.\",\n detailExpandedHint: \"Ctrl+P collapses details.\",\n detailHeader: \"Plan details\",\n detailWindow: \"showing lines {start}-{end} of {total}\",\n detailScrollHint: \"PgUp/PgDn scroll details · Home/End jump\",\n reviseTitle: \"Revise plan\",\n reviseSteps: \"{count} steps\",\n reviseFooter:\n \"\\u2191\\u2193 focus \\u00b7 space toggle skip \\u00b7 k/j move \\u00b7 \\u23ce accept \\u00b7 esc cancel\",\n riskMed: \" med\",\n riskHigh: \" high\",\n completeMsg: \"\\u25b8 plan complete \\u2014 all {total} step{s} done \\u00b7 archived\",\n },\n app: {\n walkCancelledRemaining: \"▸ walk cancelled — {count} block(s) still pending.\",\n walkCancelled: \"▸ walk cancelled.\",\n editModeYolo:\n \"▸ edit mode: YOLO — edits AND shell commands auto-run. /undo still rolls back edits. Use carefully.\",\n editModeAuto:\n \"▸ edit mode: AUTO — edits apply immediately; press u within 5s to undo (space pauses the timer). Shell commands still ask.\",\n editModeReview: \"▸ edit mode: review — edits queue for /apply (or y) / /discard (or n)\",\n rejectedEdit: \"▸ rejected edit to {path}{context}\",\n autoApprovingRest: \"▸ auto-approving remaining edits for this turn\",\n flippedAutoSession: \"▸ flipped to AUTO mode for the rest of the session (persisted)\",\n flippedAutoWalk: \"▸ flipped to AUTO mode — future edits will apply immediately. Walk exited.\",\n dashboardStopped: \"▸ dashboard stopped.\",\n notedMemory: \"▸ noted ({scope}) — {verb} {path}\",\n notedScopeProject: \"project\",\n notedScopeGlobal: \"global\",\n notedVerbCreated: \"created\",\n notedVerbAppended: \"appended to\",\n memoryWriteFailed: \"# memory write failed\",\n commandFailed: \"! command failed\",\n btwUsage: \"▸ /btw <question> — ask a side question without polluting the conversation context.\",\n btwHeader: \"≫ btw\",\n btwFailed: \"/btw failed\",\n restoreCodeOnly: \"▸ /restore is code-mode only\",\n hookUserPromptSubmit: \"UserPromptSubmit hook\",\n hookStop: \"Stop hook\",\n atMentions: \"▸ @mentions: {parts}\",\n atUrl: \"▸ @url: {parts}\",\n atUrlFailed: \"@url expansion failed\",\n denied: \"▸ denied: {cmd}{context}\",\n alwaysAllowed: '▸ always allowed \"{prefix}\" for {dir}',\n runningCommand: \"▸ running: {cmd}\",\n startingBackground: \"▸ starting (background): {cmd}\",\n checkpointSaved:\n \"⛁ checkpoint saved · {id} · {count} file{s} · /restore {id} to roll back this step\",\n continuingAfter: \"▸ continuing after {label}{counter}\",\n planStoppedAt: \"▸ plan stopped at {label}{counter}\",\n revisingAfter: \"▸ revising after {label} — {feedback}\",\n },\n hooks: {\n head: \"hook {tag} `{cmd}` {decision}{truncTag}\",\n headWithDetail: \"hook {tag} `{cmd}` {decision}{truncTag}: {detail}\",\n truncated: \" (output truncated at 256KB)\",\n decisionBlock: \"block\",\n decisionWarn: \"warn\",\n decisionTimeout: \"timeout\",\n decisionError: \"error\",\n },\n summary: {\n status: \"summarizing what was gathered…\",\n hallucinatedFallback:\n \"(model emitted fake tool-call markup instead of a prose summary — try /retry with a narrower question, or /think to inspect R1's reasoning)\",\n failedAfterReason:\n \"{label} and the fallback summary call failed: {message}. Run /clear and retry with a narrower question, or raise --max-tool-iters.\",\n },\n loop: {\n budgetExhausted:\n \"session budget exhausted — spent ${spent} ≥ cap ${cap}. Bump the cap with /budget <usd>, clear it with /budget off, or end the session.\",\n budget80Pct: \"▲ budget 80% used — ${spent} of ${cap}. Next turn or two likely trips the cap.\",\n proArmed: \"⇧ /pro armed — this turn runs on deepseek-v4-pro (one-shot · disarms after turn)\",\n abortedAtIter:\n \"aborted at iter {iter}/{cap} — stopped without producing a summary (press ↑ + Enter or /retry to resume)\",\n toolUploadStatus: \"tool result uploaded · model thinking before next response…\",\n toolBudgetWarning:\n \"{iter}/{cap} tool calls used — approaching budget. Press Esc to force a summary now.\",\n preflightFoldStatus: \"preflight: context near full, attempting fold…\",\n preflightFolded:\n \"preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) — folded {beforeMessages} messages → {afterMessages} (summary {summaryChars} chars). Sending.\",\n preflightNoFold:\n \"preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) and nothing left to fold — DeepSeek will likely 400. Run /clear or /new to start fresh.\",\n flashEscalation: \"⇧ flash requested escalation — retrying this turn on {model}{reasonSuffix}\",\n harvestStatus: \"extracting plan state from reasoning…\",\n autoEscalation:\n \"⇧ auto-escalating to {model} for the rest of this turn — flash hit {breakdown}. Next turn falls back to {fallback} unless /pro is armed.\",\n readOnlyLoopEscalation:\n \"⇧ auto-escalating to {model} — flash made {n} consecutive read-only calls without producing an edit or final answer. Next turn falls back to {fallback} unless /pro is armed.\",\n repeatToolCallWarning:\n \"Caught a repeated tool call — let the model see the issue and retry with a different approach.\",\n stormStuck:\n \"Stopped a stuck retry loop — the model kept calling the same tool with identical args after a self-correction nudge. Try /retry, rephrase, or rule out the underlying blocker.\",\n stormSuppressed: \"Suppressed {count} repeated tool call(s) — same name + args fired 3+ times.\",\n compactingHistoryStatus: \"compacting history{aggressiveTag}…\",\n aggressiveTag: \" (aggressive)\",\n foldedHistory:\n \"context {before}/{ctxMax} ({pct}%) — folded {beforeMessages} messages → {afterMessages} (summary {summaryChars} chars). Continuing.\",\n aggressivelyFoldedHistory:\n \"context {before}/{ctxMax} ({pct}%) — aggressively folded {beforeMessages} messages → {afterMessages} (summary {summaryChars} chars). Continuing.\",\n forcingSummary:\n \"context {before}/{ctxMax} ({pct}%) — forcing summary from what was gathered. Run /compact, /clear, or /new to reset.\",\n },\n errors: {\n contextOverflow:\n \"Context overflow (DeepSeek 400): session history is {requested}, past the model's prompt limit (V4: 1M tokens; legacy chat/reasoner: 131k). Usually a single tool result grew too big. Luckerr caps new tool results at 8k tokens and auto-heals oversized history on session load — a restart often clears it. If it still overflows, run /new to start fresh, or open /sessions and press [d] to delete this session.\",\n contextOverflowTooMany: \"too many tokens\",\n auth401:\n \"Authentication failed (DeepSeek 401): {inner}. Your API key is rejected. Fix with `luckerr setup` or `export DEEPSEEK_API_KEY=sk-...`. Get one at https://platform.deepseek.com/api_keys.\",\n balance402:\n \"Out of balance (DeepSeek 402): {inner}. Top up at https://platform.deepseek.com/top_up — the panel header shows your balance once it's non-zero.\",\n badparam422: \"Invalid parameter (DeepSeek 422): {inner}\",\n badrequest400: \"Bad request (DeepSeek 400): {inner}\",\n deepseek5xxHead:\n \"DeepSeek service unavailable ({status}) — this is a DeepSeek-side problem, not Luckerr. Already retried 4× with backoff.\",\n deepseek5xxReachable:\n \" DeepSeek's main API answered our health check, but /chat/completions is failing — partial outage on their side.\",\n deepseek5xxUnreachable:\n \" DeepSeek API is unreachable from your network — could be a wider DS outage or a local network issue.\",\n deepseek5xxActionNetwork:\n \" Try: (1) check your network, (2) wait 30s and retry, (3) status page: https://status.deepseek.com.\",\n deepseek5xxActionRetry:\n \" Try: (1) wait 30s and retry, (2) /preset to switch model, (3) status page: https://status.deepseek.com.\",\n innerNoMessage: \"(no message)\",\n reasonAborted: \"[aborted by user (Esc) — summarizing what I found so far]\",\n reasonContextGuard:\n \"[context budget running low — summarizing before the next call would overflow]\",\n reasonStuck:\n \"[stuck on a repeated tool call — explaining what was tried and what's blocking progress]\",\n reasonBudget: \"[tool-call budget ({iterCap}) reached — forcing summary from what I found]\",\n labelAborted: \"aborted by user\",\n labelContextGuard: \"context-guard triggered (prompt > 80% of window)\",\n labelStuck: \"stuck (repeated tool call suppressed by storm-breaker)\",\n labelBudget: \"tool-call budget ({iterCap}) reached\",\n },\n handlers: {\n basic: {\n newInfo:\n \"▸ new conversation — dropped {count} message(s) from context. Same session, fresh slate.\",\n newInfoArchived:\n '▸ new conversation — dropped {count} message(s) from context. Prior transcript archived as \"{archived}\" (visible under Sessions).',\n newInfoSystemReloaded:\n \" · LUCKERR.md / project memory reloaded (next turn pays one cache miss)\",\n helpTitle: \"Commands:\",\n helpShellTitle: \"Shell shortcut:\",\n helpShell: \" !<cmd> run <cmd> in the sandbox root; output goes into\",\n helpShellDetail:\n \" the conversation so the model sees it next turn.\",\n helpShellConsent:\n \" No allowlist gate — user-typed = explicit consent.\",\n helpShellExample: \" Example: !git status !ls src/ !npm test\",\n helpMemoryTitle: \"Quick memory:\",\n helpMemoryPin:\n \" #<note> append <note> to <project>/LUCKERR.md (committable).\",\n helpMemoryPinEx:\n \" Example: #findByEmail must be case-insensitive\",\n helpMemoryGlobal:\n \" #g <note> append <note> to ~/.luckerr/LUCKERR.md (global, never committed).\",\n helpMemoryGlobalEx: \" Example: #g always run pnpm not npm\",\n helpMemoryPinBoth:\n \" Both pin into every future session's prefix. Faster than /memory.\",\n helpMemoryEscape:\n \" Use `\\\\#text` to send a literal `#text` to the model.\",\n helpFileTitle: \"File references (code mode):\",\n helpFile: \" @path/to/file inline file content under [Referenced files] on send.\",\n helpFilePicker:\n \" Type `@` to open the picker (↑↓ navigate, Tab/Enter pick).\",\n helpUrlTitle: \"URL references:\",\n helpUrl:\n \" @https://example.com fetch the URL, strip HTML, inline under [Referenced URLs].\",\n helpUrlCache:\n \" Same URL twice in one session fetches once (in-mem cache).\",\n helpUrlPunct:\n \" Trailing sentence punctuation (./,/)) is stripped automatically.\",\n helpPresetsTitle: \"Presets (branch + harvest are NEVER auto-enabled — opt-in only):\",\n helpPresetAuto:\n \" auto v4-flash → v4-pro on hard turns ← default · cheap when easy, smart when hard\",\n helpPresetFlash:\n \" flash v4-flash always cheapest · predictable per-turn cost\",\n helpPresetPro:\n \" pro v4-pro always ~3× flash (5/31) · hard multi-turn work\",\n helpSessionsTitle: \"Sessions (auto-enabled by default, named 'default'):\",\n helpSessionCustom: \" luckerr chat --session <name> use a different named session\",\n helpSessionNone: \" luckerr chat --no-session disable persistence for this run\",\n retryNone: \"nothing to retry — no prior user message in this session's log.\",\n retryInfo: '▸ retrying: \"{preview}\"',\n loopTuiOnly: \"/loop is only available in the interactive TUI (not in run/replay).\",\n loopStopped: \"▸ loop stopped.\",\n loopNoActive: \"no active loop to stop.\",\n loopNoActiveHint:\n \"no active loop. Start one with `/loop <interval> <prompt>` (e.g. /loop 30s npm test).\\nCancels on: /loop stop · Esc · /clear /new · any user-typed prompt.\",\n loopStarted:\n '▸ loop started — re-submitting \"{prompt}\" every {duration}. Type anything (or /loop stop) to cancel.',\n keysNeedsTui: \"/keys needs a TUI context (postKeys wired).\",\n unknownCommand: \"unknown command: /{cmd} — did you mean {list}?\",\n unknownCommandShort: \"unknown command: /{cmd} (try /help)\",\n },\n admin: {\n doctorNeedsTui: \"/doctor needs a TUI context (postDoctor wired).\",\n doctorRunning: \"⚕ Doctor — running health checks…\",\n hooksReloadUnavailable:\n \"/hooks reload is not available in this context (no reload callback wired).\",\n hooksReloaded: \"▸ reloaded hooks · {count} active\",\n hooksUsage:\n \"usage: /hooks list active hooks\\n /hooks reload re-read settings.json files\",\n hooksNone: \"no hooks configured.\",\n hooksDropHint: \"drop a settings.json with a `hooks` key into either of:\",\n hooksProject: \" · {path} (project)\",\n hooksProjectFallback: \" · <project>/.luckerr/settings.json (project)\",\n hooksGlobal: \" · {path} (global)\",\n hooksEvents: \"events: PreToolUse, PostToolUse, UserPromptSubmit, Stop\",\n hooksExitCodes: \"exit 0 = pass · exit 2 = block (Pre*) · other = warn\",\n hooksLoaded: \"▸ {count} hook(s) loaded\",\n hooksSources: \"sources: project={project} · global={global}\",\n updateCurrent: \"current: luckerr {version}\",\n updateLatestPending: \"latest: (not yet resolved — background check in flight or offline)\",\n updateRetryHint: \"triggered a fresh registry fetch — retry `/update` in a few seconds,\",\n updateRetryHint2: \"or run `luckerr update` in another terminal to force it synchronously.\",\n updateLatest: \"latest: luckerr {version}\",\n updateUpToDate: \"you're on the latest. nothing to do.\",\n updateNpxHint: \"you're running via npx — the next `npx luckerr ...` launch will auto-fetch.\",\n updateNpxForce: \"to force a refresh sooner: `npm cache clean --force`.\",\n updateUpgradeHint: \"to upgrade, exit this session and run:\",\n updateUpgradeCmd1:\n \" luckerr update (interactive, dry-run supported via --dry-run)\",\n updateUpgradeCmd2: \" {command} (direct)\",\n updateInSessionDisabled:\n \"in-session install is deliberately disabled — the install spawn would\",\n updateInSessionDisabled2:\n \"corrupt this TUI's rendering and Windows can lock the running binary.\",\n statsNoData: \"no usage data yet.\",\n statsEveryTurn: \"every turn you run here appends one record — this session's turns\",\n statsWillAppear: \"will show up in the dashboard once you send a message.\",\n },\n edits: {\n undoCodeOnly:\n \"/undo is only available inside `luckerr code` — chat mode doesn't apply edits.\",\n historyCodeOnly: \"/history is only available inside `luckerr code`.\",\n showCodeOnly: \"/show is only available inside `luckerr code`.\",\n applyCodeOnly: \"/apply is only available inside `luckerr code` (nothing to apply here).\",\n discardCodeOnly: \"/discard is only available inside `luckerr code`.\",\n planCodeOnly:\n \"/plan is only available inside `luckerr code` — chat mode doesn't gate tool writes.\",\n planOn:\n \"▸ plan mode ON — write tools are gated; the model MUST call `submit_plan` before anything executes. (The model can also call submit_plan on its own for big tasks even when plan mode is off — this toggle is the stronger, explicit constraint.) Type /plan off to leave.\",\n planOff:\n \"▸ plan mode OFF — write tools are live again. Model can still propose plans autonomously for large tasks.\",\n modeCodeOnly: \"/mode is only available inside `luckerr code`.\",\n modeUsage: \"usage: /mode <review|auto|yolo> (Shift+Tab also cycles)\",\n modeYolo:\n \"▸ edit mode: YOLO — edits AND shell commands auto-run with no prompt. /undo still rolls back edits. Use carefully.\",\n modeAuto:\n \"▸ edit mode: AUTO — edits apply immediately; press u within 5s to undo, or /undo later. Shell commands still ask.\",\n modeReview: \"▸ edit mode: review — edits queue for /apply (or y) / /discard (or n)\",\n commitCodeOnly: \"/commit is only available inside `luckerr code` (needs a rooted git repo).\",\n commitUsage:\n 'usage: /commit \"your commit message\" — runs `git add -A && git commit -m \"…\"` in {root}',\n walkCodeOnly: \"/walk is only available inside `luckerr code`.\",\n checkpointCodeOnly:\n \"/checkpoint is only available inside `luckerr code` — chat mode doesn't apply edits.\",\n checkpointNone:\n \"no checkpoints yet — `/checkpoint <name>` snapshots every file the session has touched. Restore later with `/restore <name>`.\",\n checkpointHeader: \"◈ checkpoints · {count} stored\",\n checkpointRestoreHint:\n \" /restore <name|id> · /checkpoint forget <id> · /checkpoint <name> to add\",\n checkpointForgetUsage: \"usage: /checkpoint forget <id|name>\",\n checkpointNoMatch: '▸ no checkpoint matching \"{name}\" — see /checkpoint list',\n checkpointDeleted: \"▸ deleted checkpoint {id} ({name})\",\n checkpointDeleteFailed: \"▸ failed to delete {id} (already gone?)\",\n checkpointSaveUsage: \"usage: /checkpoint <name> (or /checkpoint list to see existing)\",\n checkpointSavedEmpty:\n '▸ checkpoint \"{name}\" saved ({id}) — but no files have been touched yet, so it\\'s an empty baseline. Edits made after this point will be revertable.',\n checkpointSaved:\n '▸ checkpoint \"{name}\" saved ({id}) — {files} file{s}, {size} KB. Restore: /restore {name}',\n restoreCodeOnly: \"/restore is only available inside `luckerr code`.\",\n restoreUsage: \"usage: /restore <name|id> (see /checkpoint list for ids)\",\n restoreNoMatch: '▸ no checkpoint matching \"{target}\" — try /checkpoint list',\n restoreInfo: '▸ restored \"{name}\" ({id}) from {when}',\n restoreWrote: \" · wrote back {count} file{s}\",\n restoreRemoved: \" · removed {count} file{s} (didn't exist at checkpoint time)\",\n restoreSkipped: \" ✗ {count} file{s} skipped:\",\n cwdCodeOnly: \"/cwd is only available inside `luckerr code`.\",\n cwdUsage:\n \"usage: /cwd <path> (current root: {current}). Re-points filesystem / shell / memory tools to <path>.\",\n cwdUsageNoCurrent: \"usage: /cwd <path> re-points the workspace root to <path>.\",\n },\n model: {\n modelHint: \"try deepseek-v4-flash or deepseek-v4-pro — run /models to fetch the live list\",\n modelUsage: \"usage: /model <id> ({hint})\",\n modelNotInCatalog:\n \"model → {id} (⚠ not in the fetched catalog: {list}. If this is wrong the next call will 400 — run /models to refresh.)\",\n modelSet: \"model → {id}\",\n presetAuto: \"preset → auto (v4-flash → v4-pro on hard turns · default)\",\n presetFlash: \"preset → flash (v4-flash always · cheapest · /pro still bumps one turn)\",\n presetPro: \"preset → pro (v4-pro always · ~3× flash · for hard multi-turn work)\",\n presetUsage: \"usage: /preset <auto|flash|pro>\",\n proNothingArmed: \"nothing armed — /pro with no args will arm pro for your next turn\",\n proDisarmed: \"▸ /pro disarmed — next turn falls back to the current preset\",\n proUsage:\n \"usage: /pro arm pro for the next turn (one-shot, auto-disarms after)\\n /pro off cancel armed state before the next turn\",\n proArmed:\n \"▸ /pro armed — your NEXT message runs on {model} regardless of preset. Auto-disarms after one turn. Use /preset max for a persistent switch.\",\n budgetNoCap:\n \"no session budget set — Luckerr will keep going until you stop it. Set one with: /budget <usd> (e.g. /budget 5)\",\n budgetStatus:\n \"budget: ${spent} of ${cap} ({pct}%) · /budget off to clear, /budget <usd> to change\",\n budgetOff: \"budget → off (no cap)\",\n budgetUsage:\n 'usage: /budget <usd> (got \"{arg}\" — must be a positive number, e.g. /budget 5 or /budget 12.50)',\n budgetExhausted:\n \"▲ budget → ${cap} but already spent ${spent}. Next turn will be refused — bump the cap higher to keep going, or end the session.\",\n budgetSet:\n \"budget → ${cap} (so far: ${spent} · warns at 80%, refuses next turn at 100% · /budget off to clear)\",\n },\n permissions: {\n mutateCodeOnly:\n \"/permissions add / remove / clear are only available inside `luckerr code` — they edit the project-scoped allowlist (`~/.luckerr/config.json` projects[<root>].shellAllowed).\",\n addUsage:\n 'usage: /permissions add <prefix> (multi-token OK: /permissions add \"git push origin\")',\n addAlready: \"▸ already allowed: {prefix}\",\n addBuiltin:\n \"▸ `{prefix}` is already in the builtin allowlist — no per-project entry needed. (Builtin entries are always on.)\",\n addInfo:\n \"▸ added: {prefix}\\n → next `{prefix}` invocation runs without prompting in this project.\",\n removeUsage:\n \"usage: /permissions remove <prefix-or-index> (e.g. /permissions remove 3, or /permissions remove npm)\",\n removeEmpty: \"▸ no project allowlist entries to remove.\",\n removeIndexOob: \"▸ index out of range: {idx} (project list has {count} entries)\",\n removeNothing: \"▸ nothing to remove.\",\n removeBuiltin:\n \"▸ `{prefix}` is in the builtin allowlist (read-only). Builtin entries can't be removed at runtime — they're baked into the binary.\",\n removeInfo: \"▸ removed: {prefix}\",\n removeNotFound:\n \"▸ no such project entry: {prefix} (try /permissions list to see what's stored)\",\n clearAlready: \"▸ project allowlist is already empty.\",\n clearConfirm:\n \"about to drop {count} project allowlist entr{plural} for {root}. Re-run with the word 'confirm' to proceed: /permissions clear confirm\",\n clearedNone: \"▸ project allowlist was already empty — nothing changed.\",\n cleared: \"▸ cleared {count} project allowlist entr{plural}.\",\n usage:\n 'usage: /permissions [list] show current state\\n /permissions add <prefix> persist (e.g. \"npm run build\")\\n /permissions remove <prefix-or-N> drop one entry\\n /permissions clear confirm wipe every project entry',\n modeYolo:\n \"▸ edit mode: YOLO — every shell command auto-runs, allowlist is bypassed. /mode review to re-enable prompts.\",\n modeAuto:\n \"▸ edit mode: auto — edits auto-apply, shell still gated by allowlist (or ShellConfirm prompt for non-allowlisted).\",\n modeReview:\n \"▸ edit mode: review — both edits and non-allowlisted shell commands ask before running.\",\n projectHeader: \"Project allowlist ({count}) — {root}\",\n projectNone1: ' (none — pick \"always allow\" on a ShellConfirm prompt to add one,',\n projectNone2: \" or `/permissions add <prefix>` directly.)\",\n projectNoRoot: \"Project allowlist — (no project root; chat mode shows builtin entries only)\",\n builtinHeader: \"Builtin allowlist ({count}) — read-only, baked in\",\n subcommands:\n \"Subcommands: /permissions add <prefix> · /permissions remove <prefix-or-N> · /permissions clear confirm\",\n },\n dashboard: {\n notAvailable:\n \"/dashboard is not available in this context (no startDashboard callback wired).\",\n stopNoCallback: \"/dashboard stop: no stop callback wired.\",\n notRunning: \"▸ dashboard is not running.\",\n stopping: \"▸ dashboard stopping…\",\n alreadyRunning: \"▸ dashboard is already running:\",\n alreadyRunningHint: \"Open it in any browser. Type `/dashboard stop` to tear it down.\",\n ready: \"▸ dashboard ready:\",\n readyHint: \"127.0.0.1 only · token-gated. Type `/dashboard stop` to shut down.\",\n failed: \"▸ dashboard failed to start: {reason}\",\n starting: \"▸ starting dashboard server…\",\n },\n observability: {\n contextInfo: \"context: ~{total} of {max} ({pct}%) · system {sys} · tools {tools} · log {log}\",\n compactStarting: \"▸ folding older turns into a summary…\",\n compactNoop: \"▸ nothing to fold — log already small or recent turns alone exceed the budget.\",\n compactDone: \"▸ folded {before} messages → {after} (summary {chars} chars). Continuing.\",\n compactFailed: \"▸ fold failed: {reason}\",\n costNoTurn: \"no turn yet — `/cost` shows the most recent turn's token + spend breakdown.\",\n costNeedsTui: \"/cost needs a TUI context (postUsage wired).\",\n costNoPricing:\n '▸ /cost: no pricing table for model \"{model}\". Add one to telemetry/stats.ts.',\n costEstimate:\n \"▸ /cost estimate · {model} · {prompt} prompt tokens (sys {sys} + tools {tools} + log {log} + msg {msg})\",\n costWorstCase:\n \" worst case (full miss): {input} input + ~{output} output ({avg} avg) ≈ {total}\",\n costLikely: \" likely ({pct}% session cache hit): {input} input + ~{output} output ≈ {total}\",\n costLikelyCold: \" likely: matches worst case until cache fills (no completed turns yet)\",\n statusModel: \" model {model}\",\n statusFlags: \" flags stream={stream} · effort={effort}\",\n statusCtx: \" ctx {bar} {used}/{max} ({pct}%)\",\n statusCtxNone: \" ctx no turns yet\",\n statusCost: \" cost ${cost} · cache {bar} {pct}% · turns {turns}\",\n statusCostCold: \" cost ${cost} · turns {turns} (cache warming up)\",\n statusBudget: \" budget ${spent} / ${cap} ({pct}%){tag}\",\n statusSession: ' session \"{name}\" · {count} messages in log (resumed {resumed})',\n statusSessionEphemeral: \" session (ephemeral — no persistence)\",\n statusWorkspace:\n \" workspace {path} · pinned at launch (relaunch with --dir <path> to switch)\",\n statusMcp: \" mcp {servers} server(s), {tools} tool(s) in registry\",\n statusEdits: \" edits {count} pending (/apply to commit, /discard to drop)\",\n statusPlan: \" plan ON — writes gated (submit_plan + approval)\",\n statusModeYolo:\n \" mode YOLO — edits + shell auto-run with no prompt (/undo still rolls back · Shift+Tab to flip)\",\n statusModeAuto:\n \" mode AUTO — edits apply immediately (u to undo within 5s · Shift+Tab to flip)\",\n statusModeReview: \" mode review — edits queue for /apply or y (Shift+Tab to flip)\",\n statusDash: \" dash {url} (open in browser · /dashboard stop)\",\n },\n plans: {\n noSession:\n \"no session attached — `/plans` is per-session. Run `luckerr code` in a project to get a session.\",\n activePlan: \"▸ active plan{label} — {done}/{total} step{s} done · last touched {when}\",\n activeNone: \"▸ active plan: (none)\",\n noArchives:\n \"no archived plans yet for this session — they auto-archive when every step is done\",\n archivedHeader: \"Archived ({count}):\",\n replayNoSession:\n \"no session attached — `/replay` is per-session. Run `luckerr code` in a project to get a session.\",\n replayNoArchives:\n \"no archived plans yet for this session — `/replay` lights up once a plan completes (auto-archives when every step is done).\",\n replayInvalidIndex:\n \"invalid index — `/replay` takes 1..{max} (newest = 1). Use `/plans` to see the list.\",\n archivedRow: \" ✓ {when} {total} step{s} · {completion} {label}\",\n completionComplete: \"complete\",\n stopAborted:\n \"▸ plan stopped — model aborted; type a follow-up to continue or start a new task.\",\n doneUsage:\n \"usage: /plans done <stepId> · /plans done all — manual override when the model forgot to call mark_step_complete\",\n doneUnavailable: \"/plans done is only available inside an active session.\",\n doneNoPlan: \"no active plan — nothing to mark done.\",\n doneNotInPlan: \"step `{id}` is not in the active plan. Run /plans to see the step ids.\",\n doneAlready: \"step `{id}` was already marked done.\",\n doneOk: \"▸ marked step `{id}` done.\",\n doneAllNoop: \"every step is already done.\",\n doneAllOk: \"▸ marked {count} step(s) done.\",\n },\n jobs: {\n codeOnly: \"/jobs is only available inside `luckerr code`.\",\n killCodeOnly: \"/kill is only available inside `luckerr code`.\",\n logsCodeOnly: \"/logs is only available inside `luckerr code`.\",\n empty:\n \"◈ jobs · 0 running · 0 total\\n (run_background spawns one — dev servers, watchers, long-running scripts)\",\n header: \"◈ jobs · {running} running · {total} total\",\n footer: \" /logs <id> tail · /kill <id> SIGTERM → SIGKILL\",\n killUsage: \"usage: /kill <id> (see /jobs for ids)\",\n killNotFound: \"job {id}: not found\",\n killAlreadyExited: \"job {id} already exited ({code})\",\n killStopping:\n \"▸ stopping job {id} (tree kill: SIGTERM → SIGKILL after 2s grace; Windows: taskkill /T /F)\",\n killStatus: \"▸ job {id} {status}\",\n killStillAlive: \"still alive after SIGKILL (!) — report this as a bug\",\n logsUsage: \"usage: /logs <id> [lines] (default last 80 lines)\",\n logsNotFound: \"job {id}: not found\",\n logsStatus: \"[job {id} · {status}]\\n$ {command}\",\n logsRunning: \"running · pid {pid}\",\n logsExited: \"exited {code}\",\n logsFailed: \"failed ({reason})\",\n logsStopped: \"stopped\",\n },\n memory: {\n disabled:\n \"memory is disabled (LUCKERR_MEMORY=off in env). Unset the var to re-enable — no LUCKERR.md or ~/.luckerr/memory content will be pinned in the meantime.\",\n noRoot:\n \"no working directory on this session — `/memory` needs a root to resolve LUCKERR.md from. (Running in a test harness?)\",\n listEmpty:\n \"no user memories yet. The model can call `remember` to save one, or you can create files by hand in ~/.luckerr/memory/global/ or the per-project subdir.\",\n listHeader: \"User memories ({count}):\",\n listFooter: \"View body: /memory show <name> Delete: /memory forget <name>\",\n showUsage: \"usage: /memory show <name> or /memory show <scope>/<name>\",\n showNotFound: \"no memory found: {target}\",\n showFailed: \"show failed: {reason}\",\n forgetUsage: \"usage: /memory forget <name> or /memory forget <scope>/<name>\",\n forgetNotFound: \"no memory found: {target}\",\n forgetInfo: \"▸ forgot {scope}/{name}. Next /new or launch won't see it.\",\n forgetFailed: \"could not forget {scope}/{name} (already gone?)\",\n forgetError: \"forget failed: {reason}\",\n clearUsage: \"usage: /memory clear <global|project> confirm\",\n clearConfirm:\n \"about to delete every memory in scope={scope}. Re-run with the word 'confirm' to proceed: /memory clear {scope} confirm\",\n cleared: \"▸ cleared scope={scope} — deleted {count} memory file(s).\",\n noMemory: \"no memory pinned in {root}.\",\n layers: \"Three layers are available:\",\n layerProject: \" 1. {file} — committable team memory (in the repo).\",\n layerGlobal: \" 2. ~/.luckerr/memory/global/ — your cross-project private memory.\",\n layerProjectHash: \" 3. ~/.luckerr/memory/<project-hash>/ — this project's private memory.\",\n askModel: \"Ask the model to `remember` something, or hand-edit files directly.\",\n changesNote:\n \"Changes take effect on next /new or launch — the system prompt is hashed once per session to keep the prefix cache warm.\",\n subcommands:\n \"Subcommands: /memory list | /memory show <name> | /memory forget <name> | /memory clear <scope> confirm\",\n changesNoteShort:\n \"Changes take effect on next /new or launch. Subcommands: /memory list | show | forget | clear\",\n },\n mcp: {\n noServers:\n 'no MCP servers attached. Run `luckerr setup` to pick some, or launch with --mcp \"<spec>\". `luckerr mcp list` shows the catalog.',\n toolsLabel: \" tools {count}\",\n resourcesHint: \"`/resource` to browse+read\",\n promptsHint: \"`/prompt` to browse+fetch\",\n awarenessOnly:\n \"Chat mode consumes tools today; resources+prompts are surfaced here for awareness.\",\n catalogHint:\n \"Full catalog: `luckerr mcp list` · deeper diagnosis: `luckerr mcp inspect <spec>`.\",\n fallbackServers: \"MCP servers ({count}):\",\n fallbackTools: \"Tools in registry ({count}):\",\n fallbackChange: \"To change this set, exit and run `luckerr setup`.\",\n usageDisableEnable:\n \"usage: /mcp {action} <name> · pick a name shown in /mcp (anonymous servers can't be named-toggled).\",\n usageReconnect: \"usage: /mcp reconnect <name> · pick a name shown in /mcp.\",\n unknownServer: 'unknown MCP server \"{name}\". Known: {list}.',\n noneList: \"(none)\",\n reconnectNoTui: \"/mcp reconnect requires the interactive TUI (postInfo not wired).\",\n liveTab: \"Live\",\n marketplaceTab: \"Marketplace\",\n tabHint: \"tab to switch\",\n },\n init: {\n codeOnly:\n \"/init only works in code mode (it needs filesystem tools).\\nRun `luckerr code [path]` to start a session rooted at the\\nproject you want to initialize, then run /init.\",\n exists: \"▸ LUCKERR.md already exists at {path}\",\n existsForce: \" /init force regenerate from scratch (overwrites)\",\n existsEdit: \" Or edit it by hand — it's just markdown. The current file is\",\n existsPinned: \" pinned into the system prompt every launch as-is.\",\n info: \"▸ /init — model will scan the project and synthesize LUCKERR.md.\\n The result lands as a pending edit; review with /apply or /walk.\",\n },\n webSearchEngine: {\n currentEngine: \"Current web search engine: {engine}\",\n endpoint: \"SearXNG endpoint: {url}\",\n usageHeader: \"Usage:\",\n usageMojeek: \" /search-engine mojeek use Mojeek (default, no external deps)\",\n usageSearxng: \" /search-engine searxng use SearXNG at default endpoint\",\n usageSearxngUrl: \" /search-engine searxng <url> use SearXNG at custom endpoint\",\n alias: \"Alias: /se\",\n searxngInfo:\n \"SearXNG is a self-hosted metasearch engine (https://github.com/searxng/searxng).\",\n searxngInstall: \"Install it with: docker run -d -p 8080:8080 searxng/searxng\",\n switched: 'Switched web search engine to \"{engine}\".{note}',\n switchedSearxngNote: \" Make sure SearXNG is running at {endpoint}.\",\n confirmed:\n '✓ Web search engine set to \"{engine}\"{detail}. Next assistant turn will pick up the change.',\n confirmedDetail: \" ({endpoint})\",\n },\n skill: {\n listEmpty: \"no skills found. Luckerr reads skills from:\",\n listProjectScope:\n \" · <project>/.luckerr/skills/<name>/SKILL.md (or <name>.md) — project scope\",\n listGlobalScope: \" · ~/.luckerr/skills/<name>/SKILL.md (or <name>.md) — global scope\",\n listProjectOnly: \" (project scope is only active in `luckerr code`)\",\n listFrontmatter: \"Each file's frontmatter needs at least `name` and `description`.\",\n listInvoke:\n \"Invoke a skill with `/skill <name> [args]` or by asking the model to call `run_skill`.\",\n listHeader: \"User skills ({count}):\",\n listFooter: \"View: /skill show <name> Run: /skill <name> [args] New: /skill new <name>\",\n listEmptyNewHint:\n \"Scaffold one with: /skill new <name> (project scope) — there's no remote registry yet; you author skills directly.\",\n showUsage: \"usage: /skill show <name>\",\n showNotFound: \"no skill found: {name}\",\n runNotFound: \"no skill found: {name} (try /skill list)\",\n runInfo: \"▸ running skill: {name}{args}\",\n newUsage: \"usage: /skill new <name> [--global]\",\n newCreated: \"▸ created skill: {name}\\n {path}\\n edit it, then `/skill {name}` to invoke\",\n newError: \"▲ /skill new failed: {reason}\",\n },\n },\n statusBar: {\n turn: \"turn\",\n cache: \"cache\",\n spent: \"spent\",\n left: \" left\",\n slow: \"slow\",\n disconnect: \"disconnect\",\n reconnecting: \"reconnecting\\u2026\",\n approvingIn: \"approving in \",\n escToInterrupt: \"s \\u00b7 esc to interrupt\",\n recordingGlyph: \"\\u25CFREC\",\n mb: \" MB\",\n evt: \" evt\",\n editsLabel: \"edits:\",\n mcpLoading: \"MCP\",\n },\n editMode: {\n plan: \"PLAN MODE\",\n yolo: \"YOLO\",\n auto: \"AUTO\",\n review: \"REVIEW\",\n writesGated: \" writes gated \\u00b7 /plan off to leave\",\n editsShellAuto: \"edits + shell auto \\u00b7 /undo to roll back\",\n editsLandNow: \"edits land now \\u00b7 u to undo\",\n queuedApplyDiscard: \"{count} queued \\u00b7 y apply \\u00b7 n discard\",\n editsQueued: \"edits queued \\u00b7 y apply \\u00b7 n discard\",\n shiftTabFlip: \" {mid} \\u00b7 Shift+Tab to flip\",\n queuedDots: \"queued\\u2026\",\n },\n composer: {\n placeholder: \"ask anything \\u00b7 slash for commands \\u00b7 at-sign for files\",\n waitingForResponse: \"\\u2026waiting for response\\u2026\",\n hintSend: \"send\",\n hintNewline: \"newline\",\n hintClear: \"clear\",\n hintScroll: \"scroll\",\n hintHistory: \"history\",\n hintAbort: \"abort\",\n hintQuit: \"quit\",\n abortedHint: \"turn aborted by user \\u00b7 esc again to clear \\u00b7 \\u23ce to ask a follow-up\",\n editorNoRawMode:\n \"external editor unavailable \\u2014 stdin doesn't support raw-mode toggling on this terminal\",\n editorFailed: \"external editor:\",\n editorMissing:\n \"no $EDITOR / $VISUAL / $GIT_EDITOR set \\u2014 export one (e.g. `export EDITOR=nano`) and retry\",\n editorExited: \"editor exited with code {code}\",\n },\n pathConfirm: {\n title: \"Outside-sandbox path\",\n subtitleRead: \"{tool} wants to READ a file outside the project sandbox\",\n subtitleWrite: \"{tool} wants to WRITE a file outside the project sandbox\",\n awaiting: \"awaiting\",\n denyTitle: \"Deny \\u2014 provide context\",\n optional: \"optional\",\n denyFooter:\n \"type context \\u00b7 \\u23ce submit with reason \\u00b7 esc skip (deny without reason)\",\n pickFooter:\n \"\\u2191\\u2193 pick \\u00b7 \\u23ce confirm \\u00b7 Tab add context \\u00b7 esc cancel\",\n allowOnce: \"allow once\",\n allowOnceDesc: \"permit this access; remember the directory for the rest of this session\",\n allowAlways: \"allow always\",\n allowAlwaysDesc: \"remember `{prefix}` for this project (persisted in ~/.luckerr/config.json)\",\n deny: \"deny\",\n denyDesc: \"press Tab to add context telling the model why\",\n pathLabel: \"path\",\n sandboxLabel: \"sandbox\",\n allowPrefixLabel: \"prefix\",\n },\n shellConfirm: {\n title: \"Shell command\",\n bgTitle: \"Background process\",\n subtitle: \"model wants to run a shell command\",\n bgSubtitle: \"long-running process \\u2014 keeps running after approval, /kill to stop\",\n denyTitle: \"Deny \\u2014 provide context\",\n optional: \"optional\",\n denyFooter:\n \"type context \\u00b7 \\u23ce submit with reason \\u00b7 esc skip (deny without reason)\",\n awaiting: \"awaiting\",\n pickFooter:\n \"\\u2191\\u2193 pick \\u00b7 \\u23ce confirm \\u00b7 Tab add context \\u00b7 esc cancel\",\n allowOnce: \"allow once\",\n allowOnceDesc: \"run this command, ask again next time\",\n allowAlways: \"allow always\",\n allowAlwaysDesc: \"remember `{prefix}` for this project\",\n deny: \"deny\",\n denyDesc: \"press Tab to add context telling the model why\",\n cwdLabel: \"cwd\",\n timeoutLabel: \"timeout\",\n waitLabel: \"wait\",\n previewMore: \"… {n} more line hidden — press esc, ask the model to split it\",\n previewMorePlural: \"… {n} more lines hidden — press esc, ask the model to split it\",\n },\n editConfirm: {\n footer:\n \"[y/Enter] apply \\u00b7 [n] reject with reason \\u00b7 [a] apply rest \\u00b7 [A] flip AUTO \\u00b7 [\\u2191\\u2193/Space] scroll \\u00b7 [Esc] abort\",\n newTag: \"NEW\",\n editTag: \"EDIT\",\n linesCount: \"-{removed} +{added} lines\",\n viewingRange: \"viewing {start}-{end}/{total}\",\n denyFooter: \"\\u23ce submit \\u00b7 esc skip (deny without reason)\",\n oldLabel: \" - old\",\n newLabel: \" + new\",\n sideBySide:\n \" side-by-side \\u00b7 removed lines on the left, added on the right \\u00b7 paired by offset\",\n linesAbove: \" \\u2191 {count} line above (\\u2191/k or PgUp)\",\n linesAbovePlural: \" \\u2191 {count} lines above (\\u2191/k or PgUp)\",\n linesBelow: \" \\u2193 {count} line below (\\u2193/j or Space/PgDn)\",\n linesBelowPlural: \" \\u2193 {count} lines below (\\u2193/j or Space/PgDn)\",\n },\n sessionPicker: {\n header: \" \\u25c8 LUCKERR \\u00b7 pick a session \",\n title: \"pick a session \\u2014 {workspace}\",\n messages: \"{count} message\",\n messagesPlural: \"{count} messages\",\n turns: \"{count} turns\",\n pickerHint:\n \"\\u2191\\u2193 pick \\u00b7 \\u23ce open \\u00b7 [n] new \\u00b7 [d] delete \\u00b7 [r] rename \\u00b7 esc quit\",\n empty: \" no saved sessions in this workspace yet \\u2014 press \",\n emptyNew: \" to start a new one\",\n renamePrompt: ' rename \"{from}\" \\u2192 ',\n renameHint: \" \\u23ce confirm rename \\u00b7 esc cancel\",\n emptyHint: \" \\u23ce new session \\u00b7 esc quit\",\n justNow: \"just now\",\n minAgo: \"{count} min ago\",\n yesterday: \"yesterday\",\n hoursAgo: \"{count}h ago\",\n daysAgo: \"{count} days ago\",\n },\n modelPicker: {\n header: \" \\u25c8 LUCKERR \\u00b7 pick a setup \",\n loading: \" \\u00b7 loading catalog\\u2026\",\n catalogEmpty: \" \\u00b7 catalog empty \\u2014 using known fallbacks\",\n modelsAvailable: \" \\u00b7 {count} models available\",\n presetsHeader: \" PRESETS \\u00b7 recommended \\u2014 model + effort + auto-escalate\",\n modelsHeader: \" MODELS \\u00b7 raw pick \\u2014 auto-escalate stays as-is\",\n pickerFooter:\n \" \\u2191\\u2193 pick \\u00b7 \\u23ce confirm \\u00b7 [r] refresh \\u00b7 esc cancel\",\n currentLabel: \" \\u00b7 current\",\n },\n slashSuggestions: {\n noMatch: \"no slash command matches that prefix\",\n backspaceHint: \" \\u2014 Backspace to edit, or /help for the full list\",\n commandCount: \"{count} command\",\n commandCountPlural: \"{count} commands\",\n aboveLabel: \" \\u2191 {count} above\",\n belowLabel: \" \\u2193 {count} below\",\n advancedHint: \" + {count} advanced \\u00b7 type a letter to search\",\n footerHint: \" \\u2191\\u2193 navigate \\u00b7 Tab / \\u23ce pick \\u00b7 esc cancel\",\n groupChat: \"CHAT\",\n groupSetup: \"SETUP\",\n groupInfo: \"INFO\",\n groupSession: \"SESSION\",\n groupExtend: \"EXTEND\",\n groupCode: \"CODE\",\n groupJobs: \"JOBS\",\n groupAdvanced: \"ADVANCED\",\n groupDetailSetup: \"model + cost\",\n groupDetailInfo: \"current state\",\n groupDetailChat: \"daily turn ops\",\n groupDetailExtend: \"MCP, memory, skills\",\n groupDetailSession: \"saved sessions\",\n groupDetailCode: \"edits + plans (code mode)\",\n groupDetailJobs: \"background processes (code mode)\",\n groupDetailAdvanced: \"rare or set-and-forget\",\n },\n atMentions: {\n loading: \"loading\\u2026\",\n entrySingular: \"{count} entry\",\n entryPlural: \"{count} entries\",\n searching: \"searching\\u2026\",\n scanned: \"scanned\",\n match: \"match\",\n matches: \"matches\",\n forFilter: 'for \"{filter}\"',\n noMatch: 'no files match \"{filter}\"',\n emptyDir: \"empty directory\",\n scanning: \"scanning the tree\\u2026\",\n footerBrowse:\n \"\\u2191\\u2193 navigate \\u00b7 Tab drill into folder \\u00b7 \\u23ce insert \\u00b7 esc cancel\",\n footerBrowseSearch:\n \"\\u2191\\u2193 navigate \\u00b7 Tab / \\u23ce insert as @path \\u00b7 esc cancel\",\n footerInsert: \"\\u2191\\u2193 navigate \\u00b7 Tab / \\u23ce insert as @path \\u00b7 esc cancel\",\n },\n statsPanel: {\n modePlan: \"PLAN\",\n modeYolo: \"yolo\",\n modeAuto: \"auto\",\n modeReview: \"review\",\n pro: \"\\u21e7 pro\",\n budget: \" budget \",\n },\n welcomeBanner: {\n workspace: \"\\u25b8 workspace\",\n relaunchHint: \" (relaunch with --dir <path> to switch)\",\n dashboard: \"\\u25b8 web\",\n },\n ctxBreakdown: {\n title: \"\\u25a3 context\",\n compactHint: \" /compact folds (auto at 50%) \\u00b7 /new wipes log\",\n topTools: \" top tool results by cost ({count}):\",\n msg: \"msg\",\n turnLabel: \"turn\",\n },\n startup: {\n codeRooted:\n '\\u25b8 luckerr code: rooted at {rootDir}, session \"{session}\" \\u00b7 {tools} native tool(s){semantic}',\n ephemeral: \"(ephemeral)\",\n semanticOn: \" \\u00b7 semantic_search on\",\n },\n doctorErrors: {\n unreadable: \"{path} unreadable \\u2014 {message}\",\n cannotList: \"cannot list \\u2014 {message}\",\n parseFailed: \"couldn't parse settings.json \\u2014 {message}\",\n probeFailed: \"probe failed \\u2014 {message}\",\n },\n webErrors: {\n status:\n \"web_search {status} \\u2014 try: the search backend returned an error; rephrase the query, or switch engine with /search-engine mojeek|searxng\",\n rateLimit429:\n \"web_search 429 \\u2014 try: wait 10s before retrying, or rephrase the query; the search backend is rate-limiting this client\",\n forbidden403:\n \"web_search 403 \\u2014 try: the search backend is blocking this client; switch engine with /search-engine mojeek|searxng, or wait and retry later\",\n serverError5xx:\n \"web_search {status} \\u2014 try: open the search URL in a browser; if it loads this is transient and a retry in 30s may help\",\n mojeekBlocked:\n \"web_search: Mojeek anti-bot page \\u2014 rate-limited or blocked \\u2014 try: wait 30s and retry, or switch engine with /search-engine searxng\",\n mojeekNoResults:\n \"web_search: 0 results but response doesn't look like a real empty page ({chars} chars, first 120: {preview}) \\u2014 try: rephrase the query with simpler terms, or switch engine with /search-engine searxng\",\n invalidEndpoint:\n 'web_search: invalid SearXNG endpoint \"{endpoint}\" \\u2014 try: set a valid URL with /search-endpoint http://host:port',\n endpointMustBeHttp:\n \"web_search: SearXNG endpoint must be http(s), got {protocol} \\u2014 try: set a valid URL with /search-endpoint http://host:port\",\n cannotReach:\n \"web_search: Cannot reach SearXNG server at {endpoint} \\u2014 try: install and start SearXNG (https://github.com/searxng/searxng, e.g. `docker run -d -p 8080:8080 searxng/searxng`), or switch to the default engine with /search-engine mojeek\",\n searxngNoResults:\n \"web_search: 0 results but SearXNG response doesn't look like an empty results page ({chars} chars) \\u2014 try: rephrase the query with simpler terms, or switch engine with /search-engine mojeek\",\n fetchStatus:\n \"web_fetch {status} for {url} \\u2014 try: confirm the URL resolves in a browser; status suggests the host returned an error page\",\n fetchRateLimit429:\n \"web_fetch 429 for {url} \\u2014 try: wait 10s before retrying; the host is rate-limiting this client\",\n fetchForbidden403:\n \"web_fetch 403 for {url} \\u2014 try: the host is blocking this client; the page may require login or block bots \\u2014 use web_search snippets instead\",\n fetchServerError5xx:\n \"web_fetch {status} for {url} \\u2014 try: open the URL in a browser; if it loads this is transient and a retry in 30s may help\",\n fetchTimeout:\n \"web_fetch: timed out after {ms}ms for {url} \\u2014 try: a shorter URL or smaller content; this may be a slow CDN, or retry once\",\n fetchTooLarge:\n \"web_fetch refused: content-length {len} bytes exceeds {cap}-byte cap ({url}) \\u2014 try: a different URL with smaller content; this page is too large to fetch\",\n fetchBodyTooLarge:\n \"web_fetch refused: response body exceeded {cap}-byte cap ({seen} bytes seen) \\u2014 try: a different URL with smaller content; this page streamed past the size cap\",\n fetchInvalidUrl:\n \"web_fetch: url must start with http:// or https:// \\u2014 try: pass an absolute http(s) URL (the URL is malformed or uses an unsupported scheme)\",\n },\n choiceConfirm: {\n customLabel: \"Let me type my own answer\",\n customDesc:\n \"None of the above fits \\u2014 type a free-form reply. The model reads it verbatim.\",\n cancelLabel: \"Cancel \\u2014 drop the question\",\n cancelDesc: \"Model stops and asks what you want instead.\",\n },\n cardTitles: {\n usage: \"usage\",\n context: \"context\",\n search: \"search\",\n subagent: \"subagent\",\n reply: \"reply\",\n reasoning: \"reasoning\",\n reasoningAborted: \"reasoning (aborted)\",\n reasoningEllipsis: \"reasoning\\u2026\",\n error: \"error\",\n doctor: \"doctor\",\n you: \"you\",\n task: \"task\",\n },\n cardLabels: {\n prompt: \"prompt\",\n reason: \"reason\",\n output: \"output\",\n cache: \"cache\",\n session: \"session\",\n balance: \"balance\",\n turn: \"turn\",\n system: \"system\",\n tools: \"tools\",\n log: \"log\",\n input: \"input\",\n topTools: \"top tools\",\n logMsgs: \"log msgs\",\n hitSingular: \"{count} hit \\u00b7 {files} file\",\n hitsPlural: \"{count} hits \\u00b7 {files} files\",\n moreHitSingular: \"\\u22ee +{count} more hit\",\n moreHitsPlural: \"\\u22ee +{count} more hits\",\n earlierLine: \"\\u22ee {count} earlier line (use /tool to read full)\",\n earlierLines: \"\\u22ee {count} earlier lines (use /tool to read full)\",\n earlierStackLine: \"\\u22ee {count} earlier stack line hidden\",\n earlierStackLines: \"\\u22ee {count} earlier stack lines hidden\",\n agent: \"agent \\u00b7 {name}\",\n response: \"response\",\n writing: \"writing \\u2026\",\n tok: \"tok\",\n pilcrow: \"\\u00b6\",\n aborted: \"aborted\",\n truncatedByEsc: \"[truncated by esc]\",\n rejected: \"rejected\",\n exit: \"exit {code}\",\n bytesIn: \"{bytes} in\",\n elapsedSec: \"{secs}s\",\n stackTrace: \"stack trace\",\n retries: \"retries\",\n reasoningLabel: \"reasoning \\u00b7 {count} \\u00b6\",\n runningLabel: \"running\",\n workingLabel: \"working\",\n defaultFooter: \"\\u2191\\u2193 pick \\u00b7 \\u23ce confirm \\u00b7 esc cancel\",\n applyAction: \"[a] apply\",\n skipAction: \"[s] skip\",\n rejectAction: \"[r] reject\",\n levelOk: \"OK\",\n levelWarn: \"warn\",\n levelFail: \"FAIL\",\n checksLabel: \"checks\",\n passed: \"passed\",\n warnTag: \"warn\",\n failTag: \"fail\",\n stepLabel: \"step\",\n done: \"done\",\n inProgress: \"\\u2190 in progress\",\n upcoming: \"upcoming\",\n resumed: \"resumed \\u00b7 \",\n archive: \"\\u23ea archive \\u00b7 \",\n more: \"\\u22ee +{count} more\",\n categoryUser: \"user\",\n categoryFeedback: \"feedback\",\n categoryProject: \"project\",\n categoryReference: \"reference\",\n },\n copyMode: {\n title: \"── COPY MODE ──\",\n help: \"j/k or ↑/↓ move · v select · y yank · g/G top/bottom · q quit\",\n statusBar: \"line {cur}/{total} · selection: {sel}\",\n statusYanked: \"yanked {size} chars (osc52={osc52})\",\n statusEmpty: \"nothing selected\",\n empty: \"(no chat content yet — say something to the model first)\",\n labelUser: \"you\",\n labelAssistant: \"assistant\",\n labelReasoning: \"reasoning\",\n yankedToast: \"▸ copied {size} chars to clipboard (osc52)\",\n yankedToastFile: \"▸ copied {size} chars · file: {path}\",\n },\n mcpHealth: {\n noData: \"no inspect data\",\n healthy: \"healthy \\u00b7 {ms}ms\",\n slow: \"slow \\u00b7 {ms}ms\",\n verySlow: \"very slow \\u00b7 {ms}ms\",\n slowToast: \"\\u26a0 MCP `{name}` slow \\u00b7 {seconds}s p95 over the last {sampleSize} calls\",\n emptyHint:\n \"\\u2139 no MCP servers configured \\u2014 try: `luckerr setup` to re-pick, or `luckerr mcp install filesystem`\",\n },\n denyContextInput: {\n description:\n \"Tell the agent why you denied this. The next attempt will see your reason as additional context.\",\n },\n cardStream: {\n scrollAbove: \" \\u2191 {scroll} / {max} row above\",\n scrollAbovePlural: \" \\u2191 {scroll} / {max} rows above\",\n scrollMore: \" \\u2014 {remaining} more\",\n scrollPgUp: \" \\u00b7 PgUp / wheel / \\u2191\",\n },\n slashArgPicker: {\n noMatch: 'no match for \"{partial}\"',\n keepTyping: \" \\u2014 keep typing, or Backspace to edit\",\n above: \" \\u2191 {hidden} above\",\n below: \" \\u2193 {hidden} below\",\n footer: \" \\u2191\\u2193 navigate \\u00b7 Tab / \\u23ce pick \\u00b7 esc cancel\",\n },\n mcpMarketplace: {\n title: \"MCP marketplace\",\n filter: \"filter: \",\n filterPlaceholder: \"(type to filter)\",\n matchSingular: \"{n} match\",\n matchPlural: \"{n} matches\",\n loading: \"loading\\u2026\",\n noEntries: \"no entries\",\n opening: \"opening registry\\u2026\",\n cached: \"\\u00b7 cached\",\n exhausted: \"\\u00b7 exhausted\",\n loadingMore: \"loading more\\u2026\",\n allLoaded: \"all pages loaded\",\n fetchingDetail: \"fetching smithery detail\\u2026\",\n noInstallInfo: \"no install info for {name} - try `npx -y @smithery/cli install {name}`\",\n alreadyInstalled: \"already installed: {spec}\",\n installed: \"installed \\u2192 {spec}\",\n uninstalled: \"uninstalled {name}\",\n installFailed: \"install failed: {message}\",\n notInstalled: \"not installed: {name}\",\n bridged: \"\\u2713 installed {name} - bridged\",\n bridgeFailed: \"\\u25b2 installed {name} - bridge failed: {reason}\",\n bridgeReloadFailed:\n \"\\u2713 installed {name} - restart `luckerr code` to bridge (reload failed: {message})\",\n restartBridge: \"\\u2713 installed {name} - restart `luckerr code` to bridge\",\n needsEnv: \" \\u00b7 needs env: {env}\",\n badgeOfficial: \"[off]\",\n badgeSmithery: \"[smt]\",\n badgeLocal: \"[loc]\",\n footerHint:\n \"type filter \\u00b7 \\u2191\\u2193 pick \\u00b7 \\u23ce install/toggle \\u00b7 PgDn load more \\u00b7 esc close\",\n specLine: \"spec: {runtime} {id} \\u00b7 {transport}\",\n smitheryDetail: \"(smithery listing \\u2014 install detail fetched on Enter)\",\n statusError: \"error: {message}\",\n },\n mcpBrowser: {\n title: \"\\u25c8 MCP browser\",\n empty: \"No MCP servers attached. Run `luckerr setup` to pick some, or launch with --mcp.\",\n serverCount: \"{count} server{s}\",\n footer: \"\\u2191\\u2193 pick \\u00b7 [r] reconnect \\u00b7 [d] disable \\u00b7 esc quit\",\n },\n mcpLifecycle: {\n handshake: \"handshake\\u2026\",\n connected: \"connected\",\n failed: \"failed\",\n disabled: \"disabled\",\n reconnect: \"reconnect\\u2026\",\n initDetail: \"initialise \\u2192 tools/list \\u2192 resources/list\",\n reconnectDetail: \"tearing down \\u00b7 re-handshake \\u00b7 listing tools\",\n disabledDetail: \"via /mcp disable {name}\",\n },\n checkpointPicker: {\n title: \"restore a checkpoint \\u2014 {workspace}\",\n header: \" \\u25c8 LUCKERR \\u00b7 pick a checkpoint \",\n empty: \" no checkpoints in this workspace yet - see /checkpoint to make one\",\n more: \" \\u2026 {hidden} more\",\n footer: \" \\u2191\\u2193 pick \\u00b7 \\u23ce restore \\u00b7 [d] forget \\u00b7 esc quit\",\n footerEmpty: \" esc quit\",\n },\n planReviseConfirm: {\n title: \"plan revision proposed\",\n metaRight: \"\\u2212{removed} +{added} \\u00b7 {kept} kept\",\n updatedSummary: \"updated summary: {summary}\",\n acceptLabel: \"Accept revision - apply the new step list\",\n acceptHint: \"Replaces the remaining plan with the proposed steps. Done steps are untouched.\",\n rejectLabel: \"Reject - keep the original plan\",\n rejectHint: \"Drops the proposal. Model continues with the original remaining steps.\",\n },\n diffApp: {\n title: \"luckerr diff\",\n turnLabel: \"turn {turn} ({current}/{total})\",\n turnsAligned: \"{count} turns aligned\",\n paneEmpty: \"(no records on this side for this turn)\",\n kindMatch: \"\\u2713 match\",\n kindDiverge: \"\\u2605 diverge\",\n kindOnlyInA: \"\\u2190 only in A\",\n kindOnlyInB: \"\\u2192 only in B\",\n },\n recordView: {\n userPrefix: \"you \\u203a \",\n assistant: \"assistant\",\n toolPrefix: \"tool<\",\n argsLabel: \" args: \",\n resultArrow: \" \\u2192 \",\n error: \"error \",\n cache: \" \\u00b7 cache \",\n toolCallOnly: \"(tool-call response only)\",\n truncateExtra: \"(+{extra} chars)\",\n },\n replayApp: {\n emptyTranscript: \"empty transcript\",\n turnProgress: \"turn {current}/{total}\",\n noRecords: \"no records\",\n untracked: \"(untracked)\",\n churned: \"(churned \\u00d7{count})\",\n },\n};\n","import type { TranslationSchema } from \"./types.js\";\n\nexport const zhCN: TranslationSchema = {\n common: {\n error: \"错误\",\n warning: \"警告\",\n loading: \"加载中...\",\n done: \"完成\",\n cancel: \"取消\",\n confirm: \"确认\",\n back: \"返回\",\n next: \"下一步\",\n tool: \"工具\",\n running: \"运行中\",\n noTurns: \"(暂无对话)\",\n },\n cli: {\n description: \"DeepSeek 原生智能体框架 — 专为缓存命中和低成本令牌构建。\",\n continue: \"恢复最近使用的聊天会话,不显示选择器。\",\n setup: \"交互式向导 — API 密钥、预设、MCP 服务器。随时重新运行以重新配置。\",\n code: \"代码编辑聊天 — 以 <dir>(默认:cwd)为根的文件系统工具,编码系统提示词,v4-flash 基线。\",\n chat: \"具有实时缓存/成本面板的交互式 Ink TUI。\",\n run: \"以非交互方式运行单个任务,流式输出。\",\n stats: \"显示使用情况仪表板。\",\n doctor: \"一键健康检查。\",\n commit: \"从暂存的差异中起草提交消息。\",\n sessions: \"列出保存的聊天会话,或按名称检查。\",\n pruneSessions: \"删除空闲 ≥N 天的已保存会话(默认 90)。使用 --dry-run 预览。\",\n events: \"美化打印内核事件日志侧边文件。\",\n replay: \"交互式 Ink TUI,用于浏览转录稿。\",\n diff: \"在分栏 Ink TUI 中比较两个转录稿。\",\n mcp: \"模型上下文协议 (MCP) 助手 — 发现服务器,测试您的设置。\",\n version: \"打印 Luckerr 版本。\",\n update: \"检查较新版本的 Luckerr 并安装。\",\n index: \"构建(或增量刷新)本地语义搜索索引。\",\n },\n ui: {\n welcome: \"随时运行 `luckerr` 开始聊天 — 您的设置将被记住。\",\n taglineChat: \"DeepSeek 原生智能体\",\n taglineCode: \"DeepSeek 原生代码智能体\",\n taglineSub: \"缓存优先 · Flash 优先\",\n startSessionHint: \"输入消息以开始您的会话\",\n inputPlaceholder: \"输入任何内容... (输入 / 使用命令, @ 引用文件)\",\n busy: \"思考中...\",\n thinking: \"▸ 思考中...\",\n undo: \"撤消\",\n undoHint: \"在 5 秒内按 u 撤消\",\n applied: \"已应用\",\n rejected: \"已拒绝\",\n noDashboard: \"禁止自动启动嵌入式 Web 仪表板。\",\n dashboardPortHint:\n \"将仪表板绑定到固定端口 (1–65535)。重启后保持稳定 — SSH 隧道访问必需。默认为临时端口。\",\n dashboardPortInvalid:\n \"▲ 忽略 --dashboard-port={value} (必须为 1–65535 之间的整数) — 回退到临时端口\",\n dashboardAutoStartFailed:\n \"▲ 仪表板自动启动失败 ({reason}) — 尝试 /dashboard,或传递 --no-dashboard 以静默\",\n systemAppendHint: \"追加指令到代码系统提示词。不替换默认提示词 — 在其后添加。\",\n systemAppendFileHint:\n \"追加文件内容到代码系统提示词。不替换默认提示词。UTF-8,相对于 cwd 或绝对路径。\",\n resumedSession:\n '▸ 已恢复会话 \"{name}\",包含 {count} 条历史消息 · /new 重新开始 · /sessions 管理',\n newSession: '▸ 会话 \"{name}\" (新) — 随聊随存 · /sessions 重命名或删除',\n ephemeralSession: \"▸ 临时聊天 (不保存会话) — 去掉 --no-session 以启用保存\",\n restoredEdits:\n \"▸ 从中断的运行中恢复了 {count} 个待处理的编辑块 — /apply 提交或 /discard 放弃。\",\n resumedPlan: \"已恢复计划 · {when}{summary}\",\n tipEditBindings: {\n topic: \"编辑门控快捷键\",\n sections: [\n {\n rows: [\n { key: \"y / n\", text: \"接受或放弃待处理的编辑\" },\n { key: \"Shift+Tab\", text: \"切换 预览 ↔ 自动(持久化;自动模式立即应用)\" },\n { key: \"u\", text: \"撤销上次自动应用的批处理(5 秒横幅内)\" },\n ],\n },\n ],\n footer: \"当前模式显示在底部状态栏 · /keys 查看完整快捷键参考\",\n },\n tipMouseClipboard: {\n topic: \"鼠标 + 剪贴板\",\n sections: [\n {\n rows: [\n { key: \"拖动\", text: \"直接选中文本 — 终端原生,不需要按 Shift\" },\n {\n key: \"右键\",\n text: \"终端原生菜单(Windows Terminal 等的复制 / 粘贴)\",\n },\n { key: \"滚轮\", text: \"滚动聊天记录(Web / 云端 / SSH 终端也能用)\" },\n {\n key: \"↑ / ↓\",\n text: \"滚动聊天 · 输入框历史 + 多行光标用 Ctrl+P / Ctrl+N\",\n },\n ],\n },\n ],\n footer: \"运行 /keys 查看完整键盘 + 鼠标参考\",\n },\n keysReference: {\n topic: \"Luckerr 键盘 + 鼠标参考\",\n sections: [\n {\n title: \"键盘\",\n rows: [\n { key: \"Enter\", text: \"提交输入\" },\n { key: \"Shift+Enter\", text: \"在输入框中插入换行\" },\n { key: \"↑ / ↓\", text: \"滚动聊天记录(鼠标滚轮也走这条路径)\" },\n {\n key: \"Ctrl+P / Ctrl+N\",\n text: \"上一条 / 下一条输入历史 · 多行草稿中按行移动光标\",\n },\n { key: \"Ctrl+A / Ctrl+E\", text: \"跳到当前行的开头 / 结尾\" },\n { key: \"Ctrl+W\", text: \"删除光标前的一个词\" },\n { key: \"Ctrl+U\", text: \"清空整个输入缓冲区\" },\n { key: \"Tab\", text: \"补全 @-mention · 进入文件夹 · 接受 slash 命令\" },\n { key: \"Shift+Tab\", text: \"编辑门控:切换 预览 ↔ 自动 模式\" },\n { key: \"Esc\", text: \"关闭弹出选择器 · 中止当前模型回合\" },\n { key: \"Ctrl+C\", text: \"中止当前模型回合(不是复制 — 见剪贴板段)\" },\n { key: \"PgUp / PgDn\", text: \"整页滚动聊天记录\" },\n { key: \"End\", text: \"跳到聊天的最新一行\" },\n ],\n },\n {\n title: \"鼠标\",\n rows: [\n { key: \"滚轮\", text: \"滚动聊天记录(Web / 云端 / SSH 终端也能用)\" },\n { key: \"拖动\", text: \"原生选中文本 — 直接复制,不需要修饰键\" },\n { key: \"右键\", text: \"终端原生(Windows Terminal 等的粘贴菜单)\" },\n ],\n },\n {\n title: \"复制 / 粘贴\",\n rows: [\n { key: \"选中文字\", text: \"直接拖动 — 终端原生(不需要任何修饰键)\" },\n {\n key: \"/copy\",\n text: \"vim/tmux 风格复制模式 — SSH / mosh / tmux 下拖选越过可视区无效时用这个\",\n },\n {\n key: \"复制\",\n text: \"Ctrl+Shift+C(Win/Linux)· Cmd+C(macOS)— 或选中即复制(看终端设置)\",\n },\n { key: \"粘贴\", text: \"Ctrl+V 或 Ctrl+Shift+V(Win/Linux)· Cmd+V(macOS)\" },\n {\n key: \"bracketed paste\",\n text: \"多行粘贴整体进入 — 中间换行不会触发提交\",\n },\n ],\n },\n {\n title: \"编辑门控(仅 code 模式)\",\n rows: [\n { key: \"y / n\", text: \"在预览模态中接受或放弃待处理的编辑\" },\n { key: \"Shift+Tab\", text: \"切换 预览 ↔ 自动(持久化)\" },\n { key: \"u\", text: \"撤销上次自动应用的批处理(5 秒横幅内)\" },\n ],\n },\n ],\n footer:\n \"通过 DECSET 1007(alternate-scroll),终端把滚轮翻译成 ↑/↓ 发给应用 — 大多数终端(含 Web / 云端 / SSH)都能滚,且不影响终端原生选区。直接拖动选中文本无需 Shift。传入 --no-mouse 可关闭。\",\n },\n tipShownOnce: \"仅显示一次\",\n modelOverride: \"覆盖默认模型\",\n noSession: \"禁用本次运行的会话持久化\",\n resumeHint: \"强制恢复指定会话(即使空闲)\",\n newHint: \"强制创建新会话(忽略 --session / --continue)\",\n transcriptHint: \"JSONL 转录稿的写入路径\",\n budgetHint: \"会话美元上限 — 80% 时警告,100% 时拒绝下一轮\",\n modelIdHint: \"DeepSeek 模型 ID(例如 deepseek-v4-flash)\",\n systemPromptHint: \"覆盖默认系统提示词\",\n presetHint: \"模型组合 — auto|flash|pro\",\n sessionNameHint: \"会话名称(默认:'default')\",\n ephemeralHint: \"禁用本次运行的会话持久化\",\n mcpSpecHint: \"MCP 服务器规格(可重复)\",\n mcpPrefixHint: \"用此字符串为 MCP 工具名添加前缀\",\n noConfigHint: \"本次运行忽略 ~/.luckerr/config.json\",\n presetHintShort: \"模型组合 — auto|flash|pro\",\n budgetHintShort: \"会话美元上限\",\n transcriptHintShort: \"JSONL 转录稿路径\",\n mcpSpecHintShort: \"MCP 服务器规格(可重复)\",\n mcpPrefixHintShort: \"MCP 工具名前缀\",\n dryRunHint: \"显示将要安装的内容但不实际安装\",\n rebuildHint: \"从头重建索引\",\n embedModelHint: \"嵌入模型名称\",\n projectDirHint: \"项目根目录\",\n ollamaUrlHint: \"Ollama 服务器 URL\",\n skipPromptsHint: \"跳过确认提示\",\n verboseHint: \"显示完整的会话元数据\",\n pruneDaysHint: \"删除空闲此天数或更多的会话(默认 90)\",\n pruneDryRunHint: \"列出将要删除的内容但不实际删除\",\n eventTypeHint: \"按事件类型过滤\",\n eventSinceHint: \"从此事件 ID 开始\",\n eventTailHint: \"仅显示最后 N 个事件\",\n jsonHint: \"以 JSON 格式输出\",\n projectionHint: \"显示每个事件的投影状态\",\n printHint: \"打印到标准输出而非 TUI\",\n headHint: \"仅显示前 N 个事件\",\n tailHint: \"仅显示最后 N 个事件\",\n mdReportHint: \"将 markdown 差异报告写入此路径\",\n printHintTable: \"打印表格到标准输出\",\n tuiHint: \"打开交互式 TUI\",\n labelAHint: \"左侧面板的标签\",\n labelBHint: \"右侧面板的标签\",\n mcpListDescription: \"浏览 MCP 注册表(官方 → smithery → 本地 fallback)\",\n mcpInspectDescription: \"检查 MCP 服务器规格(工具、资源、提示)\",\n mcpSearchDescription: \"在 MCP 注册表中搜索匹配的服务器\",\n mcpInstallDescription: \"按名称安装 MCP 服务器(将其规格写入配置)\",\n mcpBrowseDescription: \"交互式市场浏览器 — 输入过滤、回车安装\",\n mcpLocalHint: \"只显示内置的离线目录\",\n mcpRefreshHint: \"忽略 24 小时缓存,强制刷新\",\n mcpLimitHint: \"最多显示多少条\",\n mcpPagesHint: \"一次性预加载多少页(默认 1)\",\n mcpAllHint: \"加载全部页(首次较慢)\",\n mcpMaxPagesHint: \"搜索时最多走多少页(默认 20)\",\n jsonHintCatalog: \"以 JSON 格式输出\",\n jsonHintReport: \"以 JSON 格式输出检查报告\",\n modelOverrideFlash: \"覆盖模型(默认:deepseek-v4-flash)\",\n skipConfirmHint: \"跳过确认提示\",\n },\n slash: {\n help: { description: \"显示完整命令参考\" },\n copy: {\n description: \"进入 vim/tmux 风格复制模式 — j/k 移动、v 起选区、y 复制到剪贴板\",\n },\n status: { description: \"当前模型、标志、上下文、会话\" },\n preset: {\n description: \"模型组合 — 自动在 flash → pro 之间切换,或锁定 flash/pro\",\n argsHint: \"<auto|flash|pro>\",\n },\n model: { description: \"切换 DeepSeek 模型 ID\", argsHint: \"<id>\" },\n models: { description: \"列出从 DeepSeek /models 获取的可用模型\" },\n theme: {\n description: \"显示或持久化终端主题偏好。无参数时打开选择器。\",\n argsHint: \"[auto|default|dark|light|tokyo-night|github-dark|github-light|high-contrast]\",\n },\n language: {\n description: \"切换运行时语言\",\n argsHint: \"<en|zh-CN>\",\n success: \"语言已切换为简体中文。\",\n unsupported: \"不支持的语言代码:{code}。支持的语言:{supported}。\",\n },\n pro: {\n description: \"仅为下一轮启用 v4-pro(一次性 · 自动解除)\",\n argsHint: \"[off]\",\n },\n budget: {\n description: \"会话美元上限 — 80% 时警告,100% 时拒绝下一轮。默认关闭。单独 /budget 显示状态\",\n argsHint: \"[usd|off]\",\n },\n mcp: { description: \"列出附加到此会话的 MCP 服务器 + 工具\" },\n resource: {\n description: \"浏览 + 读取 MCP 资源(无参数 → 列出 URI;<uri> → 获取内容)\",\n argsHint: \"[uri]\",\n },\n prompt: {\n description: \"浏览 + 获取 MCP 提示(无参数 → 列出名称;<name> → 渲染提示)\",\n argsHint: \"[name]\",\n },\n memory: {\n description: \"显示 / 管理固定记忆(LUCKERR.md + ~/.luckerr/memory)\",\n argsHint: \"[list|show <name>|forget <name>|clear <scope> confirm]\",\n },\n skill: {\n description: \"列出 / 运行用户技能(<project>/.luckerr/skills + ~/.luckerr/skills)\",\n argsHint: \"[list|show <name>|<name> [args]]\",\n },\n hooks: {\n description: \"列出活跃的 hooks(.luckerr/ 下的 settings.json)· reload 从磁盘重新读取\",\n argsHint: \"[reload]\",\n },\n permissions: {\n description: \"显示 / 编辑 shell 允许列表(内置只读 · 项目级:~/.luckerr/config.json)\",\n argsHint: \"[list|add <prefix>|remove <prefix|N>|clear confirm]\",\n },\n dashboard: {\n description: \"启动嵌入式 Web 仪表板(127.0.0.1,token 保护)\",\n argsHint: \"[stop]\",\n },\n update: { description: \"显示当前版本与最新版本及升级命令\" },\n stats: {\n description: \"跨会话成本仪表板(今日 / 本周 / 本月 / 全部 · 缓存命中 · 与 Claude 对比)\",\n },\n cost: {\n description: \"空 → 上一轮花费(使用卡片);带文本 → 估算发送成本(最坏情况 + 可能缓存命中)\",\n argsHint: \"[text]\",\n },\n doctor: {\n description: \"健康检查(api / config / api-reach / index / hooks / project)\",\n },\n context: { description: \"显示上下文窗口分解(系统 / 工具 / 日志 / 输入)\" },\n retry: { description: \"截断并重发您的最后一条消息(重新采样)\" },\n compact: {\n description: \"缩小日志中过大的工具结果和工具调用参数;上限为 tokens,默认 4000\",\n argsHint: \"[tokens]\",\n },\n keys: { description: \"键盘 + 鼠标 + 复制粘贴参考\" },\n cwd: {\n description:\n \"切换工作区根目录 — 重新指向文件/Shell/记忆工具,重载项目 hooks,刷新 @ 引用遍历器\",\n argsHint: \"<path>\",\n },\n stop: { description: \"中止当前模型回合(按 Esc 的替代方式)\" },\n feedback: { description: \"打开 GitHub Issue,诊断信息已复制到剪贴板\" },\n plans: { description: \"列出此会话的活跃 + 归档计划(最新在前)\" },\n replay: {\n description: \"加载归档计划为只读的时间旅行快照(默认:最新)\",\n argsHint: \"[N]\",\n },\n sessions: { description: \"列出已保存的会话(当前标记为 ▸)\" },\n setup: { description: \"提醒您退出并运行 `luckerr setup`\" },\n semantic: {\n description: \"显示 semantic_search 状态 — 已构建?Ollama 已安装?如何启用\",\n },\n clear: { description: \"仅清除可见的滚动回放(日志/上下文保留)\" },\n new: { description: \"开始全新对话(清除上下文 + 滚动回放)\" },\n loop: {\n description: \"每 <interval> 自动重新提交 <prompt>,直到您输入 / Esc / /loop stop\",\n argsHint: \"<5s..6h> <prompt> · stop · (无参数 = 状态)\",\n },\n exit: { description: \"退出 TUI\" },\n init: {\n description:\n \"扫描项目并合成基线 LUCKERR.md(模型写入;使用 /apply 审查)。`force` 覆盖已有文件。\",\n argsHint: \"[force]\",\n },\n apply: {\n description:\n \"将待处理的编辑块提交到磁盘(无参数 → 全部;`1`、`1,3` 或 `1-4` → 该子集,其余保持待处理)\",\n argsHint: \"[N|N,M|N-M]\",\n },\n discard: {\n description: \"丢弃待处理的编辑块而不写入(无参数 → 全部;索引 → 该子集)\",\n argsHint: \"[N|N,M|N-M]\",\n },\n walk: {\n description: \"逐块逐步处理待处理的编辑(git-add-p 风格:每块 y/n,a 应用剩余,A 切换 AUTO)\",\n },\n undo: { description: \"回滚最后应用的编辑批处理\" },\n history: {\n description: \"列出此会话的每个编辑批处理(用于 /show 的 ID,撤消标记)\",\n },\n show: {\n description: \"转储存储的编辑差异(省略 id 时为最新未撤消的)\",\n argsHint: \"[id]\",\n },\n commit: { description: \"git add -A && git commit -m ...\", argsHint: '\"msg\"' },\n checkpoint: {\n description: \"快照会话涉及的每个文件(Cursor 风格内部存储,非 git)。单独 /checkpoint 列出。\",\n argsHint: \"[name|list|forget <id>]\",\n },\n restore: {\n description: \"将文件回滚到命名的检查点(见 /checkpoint list)\",\n argsHint: \"<name|id>\",\n },\n plan: {\n description: \"切换只读计划模式(写入被弹回直到 submit_plan + 审批)\",\n argsHint: \"[on|off]\",\n },\n mode: {\n description:\n \"编辑门控:review(排队)· auto(应用+撤消)· yolo(应用+自动 shell)。Shift+Tab 循环。\",\n argsHint: \"[review|auto|yolo]\",\n },\n jobs: { description: \"列出 run_background 启动的后台作业\" },\n kill: {\n description: \"按 ID 停止后台作业(SIGTERM → 宽限期后 SIGKILL)\",\n argsHint: \"<id>\",\n },\n logs: {\n description: \"跟踪后台作业的输出(默认最后 80 行)\",\n argsHint: \"<id> [lines]\",\n },\n },\n wizard: {\n languageTitle: \"选择语言\",\n languageSubtitle: \"已根据系统语言自动选中。之后可用 /language 切换。\",\n welcomeTitle: \"欢迎使用 Luckerr。\",\n apiKeyPrompt: \"粘贴你的 DeepSeek API key 开始使用。\",\n apiKeyGetOne: \"在此获取:https://platform.deepseek.com/api_keys\",\n apiKeySavedLocally: \"保存在本地:{path}\",\n apiKeyInputLabel: \"key › \",\n apiKeyInvalid: \"key 长度不足——请粘贴完整 token(16+ 字符,不含空格)。\",\n apiKeyChecking: \"正在检查 API key…\",\n apiKeyRejected: \"DeepSeek 拒绝了这个 API key。请粘贴有效 key,或按 Esc 取消设置。\",\n apiKeyCheckFailed: \"暂时无法验证 API key({message})。请检查网络后重试。\",\n apiKeyPreview: \"预览:{redacted}\",\n themeTitle: \"选择主题\",\n themeSubtitle: \"方向键切换时即时预览效果,之后可用 /theme 更改。\",\n themeSampleHeading: \"示例\",\n themeFooter: \"[↑↓] 移动 · [Enter] 确认 · [Esc] 取消\",\n themeCaption: {\n default: \"GitHub 深色(默认)\",\n dark: \"深色调\",\n light: \"清爽浅色\",\n \"tokyo-night\": \"东京夜色\",\n \"github-dark\": \"GitHub 深色\",\n \"github-light\": \"GitHub 浅色\",\n \"high-contrast\": \"高对比度(无障碍)\",\n },\n reviewLabelTheme: \"主题\",\n presetTitle: \"选择预设\",\n mcpTitle: \"Luckerr 要为你接入哪些 MCP 服务器?\",\n mcpUserArgsHint: \"(需要你提供 {arg})\",\n mcpFooterMulti: \"[↑↓] 移动 · [空格] 选择 · [Enter] 确认 · [Esc] 取消 · 全不选 = 跳过\",\n mcpArgsTitle: \"配置 {name}\",\n mcpArgsDirMissing: \"目录 {path} 不存在。\",\n mcpArgsDirCreateHint: \"[Y/Enter] 创建(mkdir -p)· [N/Esc] 输入其他路径\",\n mcpArgsDirCreateFailed: \"无法创建 {path}:{message}\",\n mcpArgsRequiredParam: \"必填参数:\",\n mcpArgsEmpty: \"{name} 需要一个值 — 不能为空。\",\n mcpArgsNotADir: \"{path} 存在但不是目录。\",\n reviewTitle: \"确认保存\",\n reviewLabelApiKey: \"API key\",\n reviewLabelLanguage: \"语言\",\n reviewLabelPreset: \"预设\",\n reviewLabelMcp: \"MCP\",\n reviewMcpNone: \"(无)\",\n reviewMcpServers: \"{count} 个服务器\",\n reviewSavesTo: \"保存到 {path}\",\n reviewSaveError: \"保存配置失败:{message}\",\n reviewFooter: \"[Enter] 保存 · [Esc] 取消\",\n savedTitle: \"▸ 已保存。\",\n savedFooter: \"[Enter] 退出\",\n selectFooter: \"[↑↓] 移动 · [Enter] 确认 · [Esc] 取消\",\n stepCounter: \"步骤 {step}/{total} · \",\n exitHint: \"/exit 中止\",\n apiKeyPlaceholder: \"sk-...\",\n themeSampleReasoning: \"推理中\",\n providerKeysTitle: \"配置 AI 服务商\",\n providerKeysSubtitle: \"输入你想使用的 AI 服务商的 API key。至少填写一个。\",\n providerKeysConfigured: \"已配置 {count} 个服务商\",\n providerKeysNone: \"(未配置——至少需要一个)\",\n providerKeysFooter: \"[↑↓] 移动 · [Enter] 配置 · [Esc] 取消 · [Space] 确认\",\n providerKeysInputFor: \"{label} 的 API key:\",\n providerKeysGetOne: \"在此获取:{url}\",\n providerKeysDetected: \"(从环境变量中检测到)\",\n providerKeysFromConfig: \"(已保存在配置中)\",\n providerKeysNeeded: \"至少需要一个 API key 才能继续。\",\n reviewLabelProviders: \"AI 服务商\",\n },\n themePicker: {\n header: \"主题\",\n footer: \"↑↓ 选择 · ⏎ 确认 · Esc 取消\",\n currentPref: \"当前偏好\",\n activeNow: \"当前生效\",\n autoDesc: \"使用 LUCKERR_THEME 或默认主题\",\n },\n planFlow: {\n approveCardTitle: \"确认计划\",\n approveCardMetaRight: \"等待中\",\n openQuestionsBanner:\n \"▲ 计划中标记了待确认的问题或风险 —— 请选 {refine} 给出明确答案,再让模型继续。\",\n openQuestionsHeader: \"待确认 / 风险\",\n truncatedBodyMore: \"… 还有 {n} 行在上方滚动历史中\",\n truncatedBodyMorePlural: \"… 还有 {n} 行在上方滚动历史中\",\n picker: {\n accept: \"采纳\",\n acceptHint: \"立即按顺序执行\",\n refine: \"细化\",\n refineHint: \"给模型更多指引,重新出一版计划\",\n revise: \"改写\",\n reviseHint: \"在执行前就地编辑计划(跳过 / 重排步骤)\",\n reject: \"驳回\",\n rejectHint: \"丢弃,让模型从头再来\",\n },\n refineFooter: \"⏎ 发送 · esc 返回选项\",\n refineQuestionsHeading: \"回答以下问题,或直接说明你想要的修改:\",\n modes: {\n approve: {\n title: \"采纳 —— 还有补充指示吗?\",\n hint: \"回答计划中的问题、补充约束,或直接回车按现状采纳。\",\n blankHint: \"(留空回车 = 不附加指示直接采纳。)\",\n },\n refine: {\n title: \"细化 —— 模型应该改什么?\",\n hint: \"说明问题在哪、缺什么,或者回答计划提出的疑问。\",\n blankHint: \"(留空回车 = 让模型对所有待确认问题选用安全默认。)\",\n },\n reject: {\n title: \"驳回 —— 告诉模型原因(可选)\",\n hint: \"说明模型对你的目标理解错在哪里,或你真正想要什么。\",\n blankHint: \"(留空回车 = 不解释直接取消;模型会反过来问你想要什么。)\",\n },\n \"checkpoint-revise\": {\n title: \"改写 —— 下一步前要调整什么?\",\n hint: \"范围调整、跳过步骤、换个思路 —— 模型会据此修改剩余步骤。\",\n blankHint: \"(留空回车 = 按当前计划继续。)\",\n },\n \"choice-custom\": {\n title: \"自定义回答 —— 想说什么都行\",\n hint: \"自由文本。模型会原样读取并继续 —— 不必匹配候选项。\",\n blankHint: \"(留空回车 = 让模型反过来问你想要什么。)\",\n },\n },\n checkpoint: {\n title: \"检查点 —— 当前步骤已完成\",\n continue: \"继续 —— 执行下一步\",\n continueHint: \"模型从下一步继续。\",\n revise: \"调整 —— 在下一步前给反馈\",\n reviseHint: \"先暂停,输入指引;模型会调整剩余计划。\",\n stop: \"停止 —— 在此结束计划\",\n stopHint: \"模型总结已完成的工作并结束。\",\n },\n stepList: {\n counter: \"{total} 个步骤\",\n counterSingular: \"{total} 个步骤\",\n counterDone: \"{done}/{total} 已完成({pct}%) · 共 {total} 步\",\n counterDoneSingular: \"{done}/{total} 已完成({pct}%) · 共 {total} 步\",\n },\n noPlanSummary: \"尚未提交计划内容。\",\n detailCollapsedHint: \"Ctrl+P 展开完整计划详情。\",\n detailExpandedHint: \"Ctrl+P 收起详情。\",\n detailHeader: \"计划详情\",\n detailWindow: \"显示第 {start}-{end} 行,共 {total} 行\",\n detailScrollHint: \"PgUp/PgDn 滚动详情 · Home/End 跳转\",\n reviseTitle: \"修改计划\",\n reviseSteps: \"{count} 个步骤\",\n reviseFooter:\n \"\\u2191\\u2193 焦点 \\u00b7 空格切换跳过 \\u00b7 k/j 移动 \\u00b7 \\u23ce 确认 \\u00b7 Esc 取消\",\n riskMed: \" 中\",\n riskHigh: \" 高\",\n completeMsg:\n \"\\u25b8 \\u8ba1\\u5212\\u5b8c\\u6210 \\u2014 \\u5168\\u90e8 {total} \\u4e2a\\u6b65\\u9aa4\\u5df2\\u5b8c\\u6210 \\u00b7 \\u5df2\\u5f52\\u6863\",\n },\n app: {\n walkCancelledRemaining: \"▸ 浏览已取消 — 还有 {count} 个待处理编辑块。\",\n walkCancelled: \"▸ 浏览已取消。\",\n editModeYolo:\n \"▸ 编辑模式:YOLO — 编辑和 shell 命令都自动执行。/undo 仍可撤销编辑。请谨慎使用。\",\n editModeAuto:\n \"▸ 编辑模式:AUTO — 编辑立即应用;5 秒内按 u 撤销(空格暂停计时)。shell 命令仍会询问。\",\n editModeReview: \"▸ 编辑模式:review — 编辑入队待 /apply(或 y)/ /discard(或 n)\",\n rejectedEdit: \"▸ 拒绝了对 {path} 的编辑{context}\",\n autoApprovingRest: \"▸ 本轮剩余编辑自动批准\",\n flippedAutoSession: \"▸ 已切换到 AUTO 模式(本会话剩余生效,已持久化)\",\n flippedAutoWalk: \"▸ 已切换到 AUTO 模式 — 后续编辑立即应用。浏览模式退出。\",\n dashboardStopped: \"▸ 仪表板已停止。\",\n notedMemory: \"▸ 已记录({scope})— {verb} {path}\",\n notedScopeProject: \"项目\",\n notedScopeGlobal: \"全局\",\n notedVerbCreated: \"创建\",\n notedVerbAppended: \"追加到\",\n memoryWriteFailed: \"# 记忆写入失败\",\n commandFailed: \"! 命令失败\",\n btwUsage: \"▸ /btw <问题> — 顺便问个题外话,不会写入当前会话上下文。\",\n btwHeader: \"≫ btw\",\n btwFailed: \"/btw 调用失败\",\n restoreCodeOnly: \"▸ /restore 仅在代码模式可用\",\n hookUserPromptSubmit: \"UserPromptSubmit 钩子\",\n hookStop: \"Stop 钩子\",\n atMentions: \"▸ @mentions:{parts}\",\n atUrl: \"▸ @url:{parts}\",\n atUrlFailed: \"@url 展开失败\",\n denied: \"▸ 已拒绝:{cmd}{context}\",\n alwaysAllowed: '▸ 已对 {dir} 永久允许 \"{prefix}\"',\n runningCommand: \"▸ 正在执行:{cmd}\",\n startingBackground: \"▸ 后台启动:{cmd}\",\n checkpointSaved: \"⛁ 已保存检查点 · {id} · {count} 个文件 · /restore {id} 可回滚此步\",\n continuingAfter: \"▸ 在 {label}{counter} 之后继续\",\n planStoppedAt: \"▸ 计划在 {label}{counter} 处停止\",\n revisingAfter: \"▸ 在 {label} 之后修订 — {feedback}\",\n },\n hooks: {\n head: \"钩子 {tag} `{cmd}` {decision}{truncTag}\",\n headWithDetail: \"钩子 {tag} `{cmd}` {decision}{truncTag}:{detail}\",\n truncated: \"(输出在 256KB 处截断)\",\n decisionBlock: \"拦截\",\n decisionWarn: \"告警\",\n decisionTimeout: \"超时\",\n decisionError: \"错误\",\n },\n summary: {\n status: \"正在总结已收集的内容…\",\n hallucinatedFallback:\n \"(模型生成了伪造的工具调用标记而非纯文本总结 — 试试 /retry 换个更窄的问题,或 /think 查看 R1 的推理)\",\n failedAfterReason:\n \"{label},且回退的总结调用也失败:{message}。请运行 /clear 后用更窄的问题重试,或提高 --max-tool-iters。\",\n },\n loop: {\n budgetExhausted:\n \"会话预算已用完 — 已花费 ${spent} ≥ 上限 ${cap}。用 /budget <usd> 提高上限,/budget off 清除上限,或结束会话。\",\n budget80Pct: \"▲ 预算已用 80% — ${spent} / ${cap}。下一两轮可能就触顶。\",\n proArmed: \"⇧ /pro 已装备 — 本轮使用 deepseek-v4-pro(一次性 · 本轮后自动解除)\",\n abortedAtIter:\n \"在第 {iter}/{cap} 次工具调用处中断 — 未生成总结即停止(按 ↑ + Enter 或 /retry 恢复)\",\n toolUploadStatus: \"工具结果已上传 · 模型在生成下一条响应前思考中…\",\n toolBudgetWarning: \"已用 {iter}/{cap} 次工具调用 — 接近上限。按 Esc 立即强制总结。\",\n preflightFoldStatus: \"预检:上下文接近上限,尝试折叠…\",\n preflightFolded:\n \"预检:请求约 {estimate}/{ctxMax} tokens({pct}%)— 已折叠 {beforeMessages} 条消息 → {afterMessages}(总结 {summaryChars} 字)。发送中。\",\n preflightNoFold:\n \"预检:请求约 {estimate}/{ctxMax} tokens({pct}%)且没有可折叠的内容 — DeepSeek 大概率会返回 400。请运行 /clear 或 /new 重新开始。\",\n flashEscalation: \"⇧ flash 请求升级 — 本轮改用 {model}{reasonSuffix}\",\n harvestStatus: \"正在从推理过程提取计划状态…\",\n autoEscalation:\n \"⇧ 本轮剩余调用自动升级到 {model} — flash 命中 {breakdown}。下一轮回退到 {fallback},除非已装备 /pro。\",\n readOnlyLoopEscalation:\n \"⇧ 自动升级到 {model} — flash 连续 {n} 次只读调用,未产出修改或最终答案。下一轮回退到 {fallback},除非已装备 /pro。\",\n repeatToolCallWarning: \"拦截到重复工具调用 — 让模型察觉问题并换种方式重试。\",\n stormStuck:\n \"已停止卡死的重试循环 — 模型在自纠提示后仍以相同参数重复调用同一工具。请尝试 /retry、换种说法,或排查底层阻塞。\",\n stormSuppressed: \"已抑制 {count} 次重复工具调用 — 同一名称 + 参数触发 3 次以上。\",\n compactingHistoryStatus: \"正在压缩历史{aggressiveTag}…\",\n aggressiveTag: \"(激进)\",\n foldedHistory:\n \"上下文 {before}/{ctxMax}({pct}%)— 已折叠 {beforeMessages} 条消息 → {afterMessages}(总结 {summaryChars} 字)。继续。\",\n aggressivelyFoldedHistory:\n \"上下文 {before}/{ctxMax}({pct}%)— 已激进折叠 {beforeMessages} 条消息 → {afterMessages}(总结 {summaryChars} 字)。继续。\",\n forcingSummary:\n \"上下文 {before}/{ctxMax}({pct}%)— 基于已收集到的内容强制总结。请运行 /compact、/clear 或 /new 重置。\",\n },\n errors: {\n contextOverflow:\n \"上下文溢出(DeepSeek 400):会话历史已达 {requested},超出模型 prompt 上限(V4:1M tokens;旧版 chat/reasoner:131k)。通常是单个工具结果太大。Luckerr 默认将新工具结果限制在 8k tokens,并在会话加载时自动修复超大历史 — 重启常能清掉。如果仍然溢出,运行 /new 重新开始,或打开 /sessions 选中后按 [d] 删除该会话。\",\n contextOverflowTooMany: \"tokens 数量过多\",\n auth401:\n \"认证失败(DeepSeek 401):{inner}。你的 API key 被拒绝。运行 `luckerr setup` 或 `export DEEPSEEK_API_KEY=sk-...` 修复。在 https://platform.deepseek.com/api_keys 获取 key。\",\n balance402:\n \"余额不足(DeepSeek 402):{inner}。在 https://platform.deepseek.com/top_up 充值 — 余额非零时面板顶栏会显示。\",\n badparam422: \"参数错误(DeepSeek 422):{inner}\",\n badrequest400: \"请求错误(DeepSeek 400):{inner}\",\n deepseek5xxHead:\n \"DeepSeek 服务不可用({status}) — 这是 DeepSeek 服务端问题,不是 Luckerr 故障。已按指数退避重试 4 次。\",\n deepseek5xxReachable:\n \" DeepSeek 主 API 健康检查通过,但 /chat/completions 在挂 — 他们那边部分服务异常。\",\n deepseek5xxUnreachable:\n \" 无法从你的网络访问 DeepSeek API — 可能是 DS 整体故障,也可能是本地网络问题。\",\n deepseek5xxActionNetwork:\n \" 建议:(1) 检查网络,(2) 等 30 秒后重试,(3) 查看状态页 https://status.deepseek.com。\",\n deepseek5xxActionRetry:\n \" 建议:(1) 等 30 秒后重试,(2) 用 /preset 切换模型,(3) 查看状态页 https://status.deepseek.com。\",\n innerNoMessage: \"(无错误信息)\",\n reasonAborted: \"[用户已中断(Esc) — 正在总结到目前为止的发现]\",\n reasonContextGuard: \"[上下文额度即将耗尽 — 在下一次调用溢出之前先总结]\",\n reasonStuck: \"[卡在重复的工具调用上 — 说明已尝试的方法以及阻塞点]\",\n reasonBudget: \"[工具调用配额({iterCap})已用尽 — 基于已发现的内容强制总结]\",\n labelAborted: \"用户中断\",\n labelContextGuard: \"触发上下文保护(prompt > 80% 窗口)\",\n labelStuck: \"卡死(重复工具调用被反风暴机制抑制)\",\n labelBudget: \"工具调用配额({iterCap})已用尽\",\n },\n handlers: {\n basic: {\n newInfo: \"▸ 新对话 — 已从上下文中丢弃 {count} 条消息。同一会话,全新开始。\",\n newInfoArchived:\n \"▸ 新对话 — 已从上下文中丢弃 {count} 条消息。原对话已归档为「{archived}」,可在 Sessions 面板查看。\",\n newInfoSystemReloaded: \" · LUCKERR.md / 项目记忆已重新加载(下一轮一次性 cache miss)\",\n helpTitle: \"命令:\",\n helpShellTitle: \"Shell 快捷方式:\",\n helpShell: \" !<cmd> 在沙箱根目录运行 <cmd>;输出进入对话\",\n helpShellDetail: \" 以便模型在下一轮看到。无允许列表限制。\",\n helpShellConsent: \" 用户输入 = 明确同意。\",\n helpShellExample: \" 示例:!git status !ls src/ !npm test\",\n helpMemoryTitle: \"快速记忆:\",\n helpMemoryPin:\n \" #<note> 将 <note> 追加到 <project>/LUCKERR.md(可提交)。\",\n helpMemoryPinEx: \" 示例:#findByEmail 必须区分大小写\",\n helpMemoryGlobal:\n \" #g <note> 将 <note> 追加到 ~/.luckerr/LUCKERR.md(全局,不提交)。\",\n helpMemoryGlobalEx: \" 示例:#g 始终使用 pnpm 而非 npm\",\n helpMemoryPinBoth:\n \" 两者都固定到每个未来会话的前缀中。比 /memory 更快。\",\n helpMemoryEscape: \" 使用 `\\\\#text` 发送字面量 `#text` 给模型。\",\n helpFileTitle: \"文件引用(代码模式):\",\n helpFile: \" @path/to/file 发送时将文件内容内联到 [Referenced files] 下。\",\n helpFilePicker:\n \" 输入 `@` 打开选择器(↑↓ 导航,Tab/Enter 选择)。\",\n helpUrlTitle: \"URL 引用:\",\n helpUrl: \" @https://example.com 获取 URL,剥离 HTML,内联到 [Referenced URLs] 下。\",\n helpUrlCache: \" 同一会话中相同 URL 只获取一次(内存缓存)。\",\n helpUrlPunct: \" 自动剥离尾部标点符号(./,/))。\",\n helpPresetsTitle: \"预设(branch + harvest 永远不会自动启用 — 仅手动选择):\",\n helpPresetAuto: \" auto v4-flash → v4-pro 在困难轮次切换 ← 默认 · 简单时便宜,困难时智能\",\n helpPresetFlash: \" flash 始终使用 v4-flash 最便宜 · 每轮成本可预测\",\n helpPresetPro:\n \" pro 始终使用 v4-pro 约 3 倍 flash · 用于困难的多轮工作\",\n helpSessionsTitle: \"会话(默认自动启用,命名为 'default'):\",\n helpSessionCustom: \" luckerr chat --session <name> 使用不同的命名会话\",\n helpSessionNone: \" luckerr chat --no-session 禁用本次运行的持久化\",\n retryNone: \"没有可重试的内容 — 此会话日志中没有先前的用户消息。\",\n retryInfo: '▸ 重试中:\"{preview}\"',\n loopTuiOnly: \"/loop 仅在交互式 TUI 中可用(不在 run/replay 中)。\",\n loopStopped: \"▸ 循环已停止。\",\n loopNoActive: \"没有活动的循环可停止。\",\n loopNoActiveHint:\n \"没有活动的循环。使用 `/loop <interval> <prompt>` 启动一个(例如 /loop 30s npm test)。\\n取消方式:/loop stop · Esc · /clear /new · 任何用户输入的提示。\",\n loopStarted:\n '▸ 循环已启动 — 每 {duration} 重新提交 \"{prompt}\"。输入任何内容(或 /loop stop)取消。',\n keysNeedsTui: \"/keys 需要 TUI 上下文(postKeys 已连接)。\",\n unknownCommand: \"未知命令:/{cmd} — 你是不是想用 {list}?\",\n unknownCommandShort: \"未知命令:/{cmd} (试试 /help)\",\n },\n admin: {\n doctorNeedsTui: \"/doctor 需要 TUI 上下文(postDoctor 已连接)。\",\n doctorRunning: \"⚕ 健康检查 — 正在运行…\",\n hooksReloadUnavailable: \"/hooks reload 在此上下文中不可用(无重载回调)。\",\n hooksReloaded: \"▸ 已重载 hooks · {count} 个活跃\",\n hooksUsage:\n \"用法:/hooks 列出活跃的 hooks\\n /hooks reload 重新读取 settings.json 文件\",\n hooksNone: \"未配置 hooks。\",\n hooksDropHint: \"将包含 `hooks` 键的 settings.json 放入以下任一位置:\",\n hooksProject: \" · {path}(项目)\",\n hooksProjectFallback: \" · <project>/.luckerr/settings.json(项目)\",\n hooksGlobal: \" · {path}(全局)\",\n hooksEvents: \"事件:PreToolUse, PostToolUse, UserPromptSubmit, Stop\",\n hooksExitCodes: \"exit 0 = 通过 · exit 2 = 阻止(Pre*)· 其他 = 警告\",\n hooksLoaded: \"▸ 已加载 {count} 个 hook\",\n hooksSources: \"来源:project={project} · global={global}\",\n updateCurrent: \"当前:luckerr {version}\",\n updateLatestPending: \"最新:(尚未解析 — 后台检查进行中或离线)\",\n updateRetryHint: \"已触发新的注册表获取 — 几秒后重试 `/update`,\",\n updateRetryHint2: \"或在另一个终端运行 `luckerr update` 强制同步执行。\",\n updateLatest: \"最新:luckerr {version}\",\n updateUpToDate: \"您已是最新版本。无需操作。\",\n updateNpxHint: \"您正在通过 npx 运行 — 下次 `npx luckerr ...` 启动时将自动获取。\",\n updateNpxForce: \"要强制刷新:`npm cache clean --force`。\",\n updateUpgradeHint: \"要升级,请退出此会话并运行:\",\n updateUpgradeCmd1: \" luckerr update (交互式,支持 --dry-run 预览)\",\n updateUpgradeCmd2: \" {command} (直接安装)\",\n updateInSessionDisabled: \"会话内安装被刻意禁用 — 安装命令会\",\n updateInSessionDisabled2: \"破坏此 TUI 的渲染,且 Windows 可能锁定运行中的二进制文件。\",\n statsNoData: \"尚无使用数据。\",\n statsEveryTurn: \"您在此运行的每一轮都会追加一条记录 — 此会话的轮次\",\n statsWillAppear: \"将在您发送消息后显示在仪表板中。\",\n },\n edits: {\n undoCodeOnly: \"/undo 仅在 `luckerr code` 中可用 — 聊天模式不应用编辑。\",\n historyCodeOnly: \"/history 仅在 `luckerr code` 中可用。\",\n showCodeOnly: \"/show 仅在 `luckerr code` 中可用。\",\n applyCodeOnly: \"/apply 仅在 `luckerr code` 中可用(此处无内容可应用)。\",\n discardCodeOnly: \"/discard 仅在 `luckerr code` 中可用。\",\n planCodeOnly: \"/plan 仅在 `luckerr code` 中可用 — 聊天模式不限制工具写入。\",\n planOn:\n \"▸ 计划模式开启 — 写入工具被限制;模型必须先调用 `submit_plan` 才能执行任何操作。(模型也可以在计划模式关闭时自主调用 submit_plan 处理大型任务 — 此开关是更强的显式约束。)输入 /plan off 退出。\",\n planOff: \"▸ 计划模式关闭 — 写入工具再次可用。模型仍可为大型任务自主提出计划。\",\n modeCodeOnly: \"/mode 仅在 `luckerr code` 中可用。\",\n modeUsage: \"用法:/mode <review|auto|yolo> (Shift+Tab 也可循环)\",\n modeYolo:\n \"▸ 编辑模式:YOLO — 编辑和 Shell 命令自动运行,无提示。/undo 仍可回滚编辑。请谨慎使用。\",\n modeAuto:\n \"▸ 编辑模式:AUTO — 编辑立即应用;在 5 秒内按 u 撤消,或稍后使用 /undo。Shell 命令仍会询问。\",\n modeReview: \"▸ 编辑模式:review — 编辑排队等待 /apply(或 y)/ /discard(或 n)\",\n commitCodeOnly: \"/commit 仅在 `luckerr code` 中可用(需要有根的 git 仓库)。\",\n commitUsage: '用法:/commit \"提交消息\" — 在 {root} 中运行 `git add -A && git commit -m \"…\"`',\n walkCodeOnly: \"/walk 仅在 `luckerr code` 中可用。\",\n checkpointCodeOnly: \"/checkpoint 仅在 `luckerr code` 中可用 — 聊天模式不应用编辑。\",\n checkpointNone:\n \"尚无检查点 — `/checkpoint <name>` 快照会话涉及的每个文件。稍后使用 `/restore <name>` 恢复。\",\n checkpointHeader: \"◈ 检查点 · 已存储 {count} 个\",\n checkpointRestoreHint:\n \" /restore <name|id> · /checkpoint forget <id> · /checkpoint <name> 添加\",\n checkpointForgetUsage: \"用法:/checkpoint forget <id|name>\",\n checkpointNoMatch: '▸ 未找到匹配 \"{name}\" 的检查点 — 见 /checkpoint list',\n checkpointDeleted: \"▸ 已删除检查点 {id}({name})\",\n checkpointDeleteFailed: \"▸ 删除 {id} 失败(已消失?)\",\n checkpointSaveUsage: \"用法:/checkpoint <name> (或 /checkpoint list 查看现有)\",\n checkpointSavedEmpty:\n '▸ 检查点 \"{name}\" 已保存({id})— 但尚未涉及任何文件,因此是空基线。此后的编辑将可撤消。',\n checkpointSaved:\n '▸ 检查点 \"{name}\" 已保存({id})— {files} 个文件,{size} KB。恢复:/restore {name}',\n restoreCodeOnly: \"/restore 仅在 `luckerr code` 中可用。\",\n restoreUsage: \"用法:/restore <name|id> (见 /checkpoint list 获取 ID)\",\n restoreNoMatch: '▸ 未找到匹配 \"{target}\" 的检查点 — 尝试 /checkpoint list',\n restoreInfo: '▸ 已恢复 \"{name}\"({id}),来自 {when}',\n restoreWrote: \" · 写回了 {count} 个文件\",\n restoreRemoved: \" · 移除了 {count} 个文件(检查点时不存在)\",\n restoreSkipped: \" ✗ 跳过了 {count} 个文件:\",\n cwdCodeOnly: \"/cwd 仅在 `luckerr code` 中可用。\",\n cwdUsage:\n \"用法:/cwd <path> (当前根目录:{current})。重新指向 filesystem / shell / memory 工具到 <path>。\",\n cwdUsageNoCurrent: \"用法:/cwd <path> 将工作区根目录切换到 <path>。\",\n },\n model: {\n modelHint: \"尝试 deepseek-v4-flash 或 deepseek-v4-pro — 运行 /models 获取实时列表\",\n modelUsage: \"用法:/model <id> ({hint})\",\n modelNotInCatalog:\n \"model → {id} (⚠ 不在获取的目录中:{list}。如果这是错误的,下次调用将返回 400 — 运行 /models 刷新。)\",\n modelSet: \"model → {id}\",\n presetAuto: \"preset → auto (v4-flash → v4-pro 在困难轮次切换 · 默认)\",\n presetFlash: \"preset → flash (始终使用 v4-flash · 最便宜 · /pro 仍可临时提升一轮)\",\n presetPro: \"preset → pro (始终使用 v4-pro · 约 3 倍 flash · 用于困难的多轮工作)\",\n presetUsage: \"用法:/preset <auto|flash|pro>\",\n proNothingArmed: \"未启用 — /pro 不带参数将为下一轮启用 pro\",\n proDisarmed: \"▸ /pro 已解除 — 下一轮回退到当前预设\",\n proUsage:\n \"用法:/pro 为下一轮启用 pro(一次性,自动解除)\\n /pro off 在下一轮前取消启用状态\",\n proArmed:\n \"▸ /pro 已启用 — 您的下一条消息将在 {model} 上运行,无论预设如何。一轮后自动解除。使用 /preset max 进行持久切换。\",\n budgetNoCap:\n \"未设置会话预算 — Luckerr 将持续运行直到您停止。使用以下方式设置:/budget <usd> (例如 /budget 5)\",\n budgetStatus: \"预算:${spent} / ${cap}({pct}%)· /budget off 清除,/budget <usd> 更改\",\n budgetOff: \"budget → 关闭(无上限)\",\n budgetUsage:\n '用法:/budget <usd> (收到 \"{arg}\" — 必须是正数,例如 /budget 5 或 /budget 12.50)',\n budgetExhausted:\n \"▲ budget → ${cap} 但已花费 ${spent}。下一轮将被拒绝 — 提高上限以继续,或结束会话。\",\n budgetSet:\n \"budget → ${cap} (迄今:${spent} · 80% 时警告,100% 时拒绝下一轮 · /budget off 清除)\",\n },\n permissions: {\n mutateCodeOnly:\n \"/permissions add / remove / clear 仅在 `luckerr code` 中可用 — 它们编辑项目范围的允许列表(`~/.luckerr/config.json` projects[<root>].shellAllowed)。\",\n addUsage:\n '用法:/permissions add <prefix> (多 token 可用:/permissions add \"git push origin\")',\n addAlready: \"▸ 已允许:{prefix}\",\n addBuiltin: \"▸ `{prefix}` 已在内置允许列表中 — 无需项目条目。(内置条目始终开启。)\",\n addInfo: \"▸ 已添加:{prefix}\\n → 在此项目中,下次 `{prefix}` 调用将无需提示。\",\n removeUsage:\n \"用法:/permissions remove <prefix-or-index> (例如 /permissions remove 3,或 /permissions remove npm)\",\n removeEmpty: \"▸ 没有项目允许列表条目可移除。\",\n removeIndexOob: \"▸ 索引超出范围:{idx}(项目列表有 {count} 个条目)\",\n removeNothing: \"▸ 无内容可移除。\",\n removeBuiltin:\n \"▸ `{prefix}` 在内置允许列表中(只读)。内置条目无法在运行时移除 — 它们已编译到二进制文件中。\",\n removeInfo: \"▸ 已移除:{prefix}\",\n removeNotFound: \"▸ 无此项目条目:{prefix} (尝试 /permissions list 查看已存储的内容)\",\n clearAlready: \"▸ 项目允许列表已为空。\",\n clearConfirm:\n \"即将丢弃 {root} 的 {count} 个项目允许列表条目。重新运行并附带 'confirm' 一词以继续:/permissions clear confirm\",\n clearedNone: \"▸ 项目允许列表已为空 — 无变化。\",\n cleared: \"▸ 已清除 {count} 个项目允许列表条目。\",\n usage:\n '用法:/permissions [list] 显示当前状态\\n /permissions add <prefix> 持久化(例如 \"npm run build\")\\n /permissions remove <prefix-or-N> 删除一个条目\\n /permissions clear confirm 清除所有项目条目',\n modeYolo:\n \"▸ 编辑模式:YOLO — 每个 shell 命令自动运行,允许列表被绕过。/mode review 重新启用提示。\",\n modeAuto:\n \"▸ 编辑模式:auto — 编辑自动应用,shell 仍受允许列表限制(或非允许列表的 ShellConfirm 提示)。\",\n modeReview: \"▸ 编辑模式:review — 编辑和非允许列表的 shell 命令在运行前都会询问。\",\n projectHeader: \"项目允许列表({count})— {root}\",\n projectNone1: ' (无 — 在 ShellConfirm 提示中选择 \"always allow\" 添加一个,',\n projectNone2: \" 或直接 `/permissions add <prefix>`。)\",\n projectNoRoot: \"项目允许列表 — (无项目根目录;聊天模式仅显示内置条目)\",\n builtinHeader: \"内置允许列表({count})— 只读,已编译\",\n subcommands:\n \"子命令:/permissions add <prefix> · /permissions remove <prefix-or-N> · /permissions clear confirm\",\n },\n dashboard: {\n notAvailable: \"/dashboard 在此上下文中不可用(无 startDashboard 回调)。\",\n stopNoCallback: \"/dashboard stop:无停止回调。\",\n notRunning: \"▸ 仪表板未运行。\",\n stopping: \"▸ 仪表板正在停止…\",\n alreadyRunning: \"▸ 仪表板已在运行:\",\n alreadyRunningHint: \"在任何浏览器中打开它。输入 `/dashboard stop` 关闭。\",\n ready: \"▸ 仪表板就绪:\",\n readyHint: \"仅 127.0.0.1 · token 保护。输入 `/dashboard stop` 关闭。\",\n failed: \"▸ 仪表板启动失败:{reason}\",\n starting: \"▸ 正在启动仪表板服务器…\",\n },\n observability: {\n contextInfo: \"上下文:~{total} / {max}({pct}%)· 系统 {sys} · 工具 {tools} · 日志 {log}\",\n compactStarting: \"▸ 正在折叠旧轮次为摘要…\",\n compactNoop: \"▸ 无需折叠 — 日志已足够小,或最近轮次本身已超过预算。\",\n compactDone: \"▸ 已折叠 {before} 条消息 → {after}(摘要 {chars} 字符)。继续。\",\n compactFailed: \"▸ 折叠失败:{reason}\",\n costNoTurn: \"尚无轮次 — `/cost` 显示最近一轮的 token + 花费明细。\",\n costNeedsTui: \"/cost 需要 TUI 上下文(postUsage 已连接)。\",\n costNoPricing: '▸ /cost:模型 \"{model}\" 无定价表。请在 telemetry/stats.ts 中添加。',\n costEstimate:\n \"▸ /cost 估算 · {model} · {prompt} prompt tokens(系统 {sys} + 工具 {tools} + 日志 {log} + 消息 {msg})\",\n costWorstCase:\n \" 最坏情况(完全未命中):{input} 输入 + ~{output} 输出({avg} 平均)≈ {total}\",\n costLikely: \" 可能({pct}% 会话缓存命中):{input} 输入 + ~{output} 输出 ≈ {total}\",\n costLikelyCold: \" 可能:在缓存填充前与最坏情况相同(无已完成的轮次)\",\n statusModel: \" 模型 {model}\",\n statusFlags: \" 标志 stream={stream} · effort={effort}\",\n statusCtx: \" 上下文 {bar} {used}/{max}({pct}%)\",\n statusCtxNone: \" 上下文 尚无轮次\",\n statusCost: \" 成本 ${cost} · 缓存 {bar} {pct}% · 轮次 {turns}\",\n statusCostCold: \" 成本 ${cost} · 轮次 {turns}(缓存预热中)\",\n statusBudget: \" 预算 ${spent} / ${cap}({pct}%){tag}\",\n statusSession: ' 会话 \"{name}\" · 日志中 {count} 条消息(恢复了 {resumed} 条)',\n statusSessionEphemeral: \" 会话 (临时 — 无持久化)\",\n statusWorkspace: \" 工作区 {path} · 启动时锁定(用 --dir <path> 重新启动以切换)\",\n statusMcp: \" MCP {servers} 个服务器,注册表中 {tools} 个工具\",\n statusEdits: \" 编辑 {count} 个待处理(/apply 提交,/discard 丢弃)\",\n statusPlan: \" 计划 开启 — 写入受限(submit_plan + 审批)\",\n statusModeYolo:\n \" 模式 YOLO — 编辑 + shell 自动运行,无提示(/undo 仍可回滚 · Shift+Tab 切换)\",\n statusModeAuto: \" 模式 AUTO — 编辑立即应用(5 秒内按 u 撤消 · Shift+Tab 切换)\",\n statusModeReview: \" 模式 review — 编辑排队等待 /apply 或 y(Shift+Tab 切换)\",\n statusDash: \" 仪表板 {url}(在浏览器中打开 · /dashboard stop)\",\n },\n plans: {\n noSession: \"未附加会话 — `/plans` 是按会话的。在项目中运行 `luckerr code` 以获取会话。\",\n activePlan: \"▸ 活跃计划{label} — {done}/{total} 步骤已完成 · 最后触及 {when}\",\n activeNone: \"▸ 活跃计划:(无)\",\n noArchives: \"此会话尚无归档计划 — 当每个步骤完成时自动归档\",\n archivedHeader: \"已归档({count}):\",\n replayNoSession:\n \"未附加会话 — `/replay` 是按会话的。在项目中运行 `luckerr code` 以获取会话。\",\n replayNoArchives:\n \"此会话尚无归档计划 — `/replay` 在计划完成后启用(每个步骤完成时自动归档)。\",\n replayInvalidIndex:\n \"无效索引 — `/replay` 接受 1..{max}(最新 = 1)。使用 `/plans` 查看列表。\",\n archivedRow: \" ✓ {when} {total}步 · {completion} {label}\",\n completionComplete: \"已完成\",\n stopAborted: \"▸ 计划已停止 — 模型已中止;输入后续内容继续,或开始新任务。\",\n doneUsage:\n \"用法:/plans done <stepId> · /plans done all — 模型忘记调用 mark_step_complete 时的手动兜底\",\n doneUnavailable: \"/plans done 仅在活跃会话内可用。\",\n doneNoPlan: \"当前无活跃计划 — 没有可标记的内容。\",\n doneNotInPlan: \"步骤 `{id}` 不在当前计划中。运行 /plans 查看步骤 id。\",\n doneAlready: \"步骤 `{id}` 已被标记为完成。\",\n doneOk: \"▸ 已将步骤 `{id}` 标记为完成。\",\n doneAllNoop: \"所有步骤均已完成。\",\n doneAllOk: \"▸ 已标记 {count} 个步骤为完成。\",\n },\n jobs: {\n codeOnly: \"/jobs 仅在 `luckerr code` 中可用。\",\n killCodeOnly: \"/kill 仅在 `luckerr code` 中可用。\",\n logsCodeOnly: \"/logs 仅在 `luckerr code` 中可用。\",\n empty:\n \"◈ 作业 · 0 运行中 · 共 0 个\\n (run_background 生成一个 — 开发服务器、监视器、长时间运行的脚本)\",\n header: \"◈ 作业 · {running} 运行中 · 共 {total} 个\",\n footer: \" /logs <id> 跟踪 · /kill <id> SIGTERM → SIGKILL\",\n killUsage: \"用法:/kill <id> (见 /jobs 获取 ID)\",\n killNotFound: \"作业 {id}:未找到\",\n killAlreadyExited: \"作业 {id} 已退出({code})\",\n killStopping:\n \"▸ 正在停止作业 {id}(树终止:SIGTERM → 2 秒宽限期后 SIGKILL;Windows:taskkill /T /F)\",\n killStatus: \"▸ 作业 {id} {status}\",\n killStillAlive: \"SIGKILL 后仍存活 (!) — 请将此作为 bug 报告\",\n logsUsage: \"用法:/logs <id> [lines] (默认最后 80 行)\",\n logsNotFound: \"作业 {id}:未找到\",\n logsStatus: \"[作业 {id} · {status}]\\n$ {command}\",\n logsRunning: \"运行中 · pid {pid}\",\n logsExited: \"已退出 {code}\",\n logsFailed: \"失败({reason})\",\n logsStopped: \"已停止\",\n },\n memory: {\n disabled:\n \"记忆已禁用(环境变量 LUCKERR_MEMORY=off)。取消设置该变量以重新启用 — 此期间不会固定任何 LUCKERR.md 或 ~/.luckerr/memory 内容。\",\n noRoot:\n \"此会话无工作目录 — `/memory` 需要一个根目录来解析 LUCKERR.md。(在测试环境中运行?)\",\n listEmpty:\n \"尚无用户记忆。模型可以调用 `remember` 保存一个,或您可以在 ~/.luckerr/memory/global/ 或项目子目录中手动创建文件。\",\n listHeader: \"用户记忆({count}):\",\n listFooter: \"查看正文:/memory show <name> 删除:/memory forget <name>\",\n showUsage: \"用法:/memory show <name> 或 /memory show <scope>/<name>\",\n showNotFound: \"未找到记忆:{target}\",\n showFailed: \"显示失败:{reason}\",\n forgetUsage: \"用法:/memory forget <name> 或 /memory forget <scope>/<name>\",\n forgetNotFound: \"未找到记忆:{target}\",\n forgetInfo: \"▸ 已遗忘 {scope}/{name}。下次 /new 或启动时将不可见。\",\n forgetFailed: \"无法遗忘 {scope}/{name}(已消失?)\",\n forgetError: \"遗忘失败:{reason}\",\n clearUsage: \"用法:/memory clear <global|project> confirm\",\n clearConfirm:\n \"即将删除 scope={scope} 中的每个记忆。重新运行并附带 'confirm' 一词以继续:/memory clear {scope} confirm\",\n cleared: \"▸ 已清除 scope={scope} — 删除了 {count} 个记忆文件。\",\n noMemory: \"在 {root} 中未固定记忆。\",\n layers: \"可用的三个层级:\",\n layerProject: \" 1. {file} — 可提交的团队记忆(在仓库中)。\",\n layerGlobal: \" 2. ~/.luckerr/memory/global/ — 您的跨项目私有记忆。\",\n layerProjectHash: \" 3. ~/.luckerr/memory/<project-hash>/ — 此项目的私有记忆。\",\n askModel: \"让模型 `remember` 某些内容,或直接手编辑文件。\",\n changesNote: \"更改在下次 /new 或启动时生效 — 系统提示词每会话哈希一次以保持前缀缓存热度。\",\n subcommands:\n \"子命令:/memory list | /memory show <name> | /memory forget <name> | /memory clear <scope> confirm\",\n changesNoteShort:\n \"更改在下次 /new 或启动时生效。子命令:/memory list | show | forget | clear\",\n },\n mcp: {\n noServers:\n '未附加 MCP 服务器。运行 `luckerr setup` 选择一些,或使用 --mcp \"<spec>\" 启动。`luckerr mcp list` 显示目录。',\n toolsLabel: \" 工具 {count}\",\n resourcesHint: \"`/resource` 浏览+读取\",\n promptsHint: \"`/prompt` 浏览+获取\",\n awarenessOnly: \"聊天模式目前消耗工具;资源+提示在此展示供了解。\",\n catalogHint: \"完整目录:`luckerr mcp list` · 深度诊断:`luckerr mcp inspect <spec>`。\",\n fallbackServers: \"MCP 服务器({count}):\",\n fallbackTools: \"注册表中的工具({count}):\",\n fallbackChange: \"要更改此设置,请退出并运行 `luckerr setup`。\",\n usageDisableEnable:\n \"用法:/mcp {action} <name> · 从 /mcp 列表中挑一个名字(匿名服务器无法按名切换)。\",\n usageReconnect: \"用法:/mcp reconnect <name> · 从 /mcp 列表中挑一个名字。\",\n unknownServer: '未知 MCP 服务器 \"{name}\"。已知:{list}。',\n noneList: \"(无)\",\n reconnectNoTui: \"/mcp reconnect 需要交互式 TUI(postInfo 未连接)。\",\n liveTab: \"已连接\",\n marketplaceTab: \"市场\",\n tabHint: \"按 tab 切换\",\n },\n init: {\n codeOnly:\n \"/init 仅在代码模式下工作(需要文件系统工具)。\\n运行 `luckerr code [path]` 启动一个以您要初始化的项目为根的会话,\\n然后运行 /init。\",\n exists: \"▸ LUCKERR.md 已存在于 {path}\",\n existsForce: \" /init force 从头重新生成(覆盖)\",\n existsEdit: \" 或手动编辑 — 它只是 markdown。当前文件已\",\n existsPinned: \" 固定到每次启动的系统提示词中。\",\n info: \"▸ /init — 模型将扫描项目并合成 LUCKERR.md。\\n 结果将作为待处理的编辑;使用 /apply 或 /walk 审查。\",\n },\n webSearchEngine: {\n currentEngine: \"当前网页搜索引擎:{engine}\",\n endpoint: \"SearXNG 端点:{url}\",\n usageHeader: \"用法:\",\n usageMojeek: \" /search-engine mojeek 使用 Mojeek(默认,无外部依赖)\",\n usageSearxng: \" /search-engine searxng 使用 SearXNG 默认端点\",\n usageSearxngUrl: \" /search-engine searxng <url> 使用 SearXNG 自定义端点\",\n alias: \"别名:/se\",\n searxngInfo: \"SearXNG 是一个自托管的元搜索引擎(https://github.com/searxng/searxng)。\",\n searxngInstall: \"安装命令: docker run -d -p 8080:8080 searxng/searxng\",\n switched: '已切换网页搜索引擎为 \"{engine}\"。{note}',\n switchedSearxngNote: \" 请确保 SearXNG 在 {endpoint} 运行。\",\n confirmed: '✓ 网页搜索引擎已设为 \"{engine}\"{detail}。下一轮模型调用将生效。',\n confirmedDetail: \"({endpoint})\",\n },\n skill: {\n listEmpty: \"未找到技能。Luckerr 从以下位置读取技能:\",\n listProjectScope:\n \" · <project>/.luckerr/skills/<name>/SKILL.md (或 <name>.md) — 项目范围\",\n listGlobalScope: \" · ~/.luckerr/skills/<name>/SKILL.md (或 <name>.md) — 全局范围\",\n listProjectOnly: \" (项目范围仅在 `luckerr code` 中活跃)\",\n listFrontmatter: \"每个文件的 frontmatter 至少需要 `name` 和 `description`。\",\n listInvoke: \"使用 `/skill <name> [args]` 调用技能,或让模型调用 `run_skill`。\",\n listHeader: \"用户技能({count}):\",\n listFooter: \"查看:/skill show <name> 运行:/skill <name> [args] 新建:/skill new <name>\",\n listEmptyNewHint:\n \"用 `/skill new <name>` 在项目范围下生成一个空白模板 — 暂无在线市场,技能需要自己写。\",\n showUsage: \"用法:/skill show <name>\",\n showNotFound: \"未找到技能:{name}\",\n runNotFound: \"未找到技能:{name} (尝试 /skill list)\",\n runInfo: \"▸ 正在运行技能:{name}{args}\",\n newUsage: \"用法:/skill new <name> [--global]\",\n newCreated: \"▸ 已创建技能:{name}\\n {path}\\n 编辑后用 `/skill {name}` 调用\",\n newError: \"▲ /skill new 失败:{reason}\",\n },\n },\n statusBar: {\n turn: \"轮\",\n cache: \"缓存\",\n spent: \"已花费\",\n left: \" 剩余\",\n slow: \"慢速\",\n disconnect: \"断开\",\n reconnecting: \"重连中…\",\n approvingIn: \"即将批准,\",\n escToInterrupt: \"秒 · Esc 中断\",\n recordingGlyph: \"●REC\",\n mb: \" MB\",\n evt: \" 事件\",\n editsLabel: \"编辑:\",\n mcpLoading: \"MCP\",\n },\n editMode: {\n plan: \"计划\",\n yolo: \"自由\",\n auto: \"自动\",\n review: \"审查\",\n writesGated: \" 已限制写入 · /plan off 解除\",\n editsShellAuto: \"编辑 + Shell 自动 · /undo 可回滚\",\n editsLandNow: \"编辑已生效 · 按 u 撤消\",\n queuedApplyDiscard: \"{count} 个待处理 · y 应用 · n 丢弃\",\n editsQueued: \"编辑已排队 · y 应用 · n 丢弃\",\n shiftTabFlip: \" {mid} · Shift+Tab 切换\",\n queuedDots: \"排队中…\",\n },\n composer: {\n placeholder: \"输入任何内容 · / 使用命令 · @ 引用文件\",\n waitingForResponse: \"…等待响应…\",\n hintSend: \"发送\",\n hintNewline: \"换行\",\n hintClear: \"清空\",\n hintScroll: \"滚动\",\n hintHistory: \"历史\",\n hintAbort: \"中止\",\n hintQuit: \"退出\",\n abortedHint: \"用户已中止本轮 · 再按 Esc 清除 · ⏎ 继续提问\",\n editorNoRawMode: \"外部编辑器不可用 — 当前终端不支持 raw-mode 切换\",\n editorFailed: \"外部编辑器:\",\n editorMissing:\n \"未设置 $EDITOR / $VISUAL / $GIT_EDITOR — 请导出环境变量(例如 `export EDITOR=nano`)后重试\",\n editorExited: \"编辑器异常退出,返回码 {code}\",\n },\n pathConfirm: {\n title: \"沙箱外路径\",\n subtitleRead: \"{tool} 想要读取沙箱外的文件\",\n subtitleWrite: \"{tool} 想要写入沙箱外的文件\",\n awaiting: \"等待中\",\n denyTitle: \"拒绝 — 提供原因\",\n optional: \"可选\",\n denyFooter: \"输入原因 · ⏎ 提交 · Esc 跳过(直接拒绝)\",\n pickFooter: \"↑↓ 选择 · ⏎ 确认 · Tab 添加说明 · Esc 取消\",\n allowOnce: \"允许一次\",\n allowOnceDesc: \"本次允许,本会话内此目录不再询问\",\n allowAlways: \"始终允许\",\n allowAlwaysDesc: \"记住 `{prefix}`,本项目永久允许(写入 ~/.luckerr/config.json)\",\n deny: \"拒绝\",\n denyDesc: \"按 Tab 添加说明,告诉模型原因\",\n pathLabel: \"路径\",\n sandboxLabel: \"沙箱\",\n allowPrefixLabel: \"前缀\",\n },\n shellConfirm: {\n title: \"Shell 命令\",\n bgTitle: \"后台进程\",\n subtitle: \"模型请求执行 Shell 命令\",\n bgSubtitle: \"长时间运行 — 批准后持续运行,/kill 可停止\",\n denyTitle: \"拒绝 — 提供原因\",\n optional: \"可选\",\n denyFooter: \"输入原因 · ⏎ 提交 · Esc 跳过(直接拒绝)\",\n awaiting: \"等待中\",\n pickFooter: \"↑↓ 选择 · ⏎ 确认 · Tab 添加说明 · Esc 取消\",\n allowOnce: \"允许一次\",\n allowOnceDesc: \"执行此命令,下次再问\",\n allowAlways: \"始终允许\",\n allowAlwaysDesc: \"记住 `{prefix}`,本项目内不再询问\",\n deny: \"拒绝\",\n denyDesc: \"按 Tab 添加说明,告诉模型原因\",\n cwdLabel: \"工作目录\",\n timeoutLabel: \"超时\",\n waitLabel: \"等待\",\n previewMore: \"… 还有 {n} 行未显示 — 按 esc 取消,让模型拆分后再试\",\n previewMorePlural: \"… 还有 {n} 行未显示 — 按 esc 取消,让模型拆分后再试\",\n },\n editConfirm: {\n footer:\n \"[y/Enter] 应用 · [n] 拒绝并说明 · [a] 应用剩余 · [A] 切换 AUTO · [↑↓/Space] 滚动 · [Esc] 中止\",\n newTag: \"新增\",\n editTag: \"编辑\",\n linesCount: \"-{removed} +{added} 行\",\n viewingRange: \"正在查看 {start}-{end}/{total}\",\n denyFooter: \"⏎ 提交 · Esc 跳过(直接拒绝)\",\n oldLabel: \" 旧内容\",\n newLabel: \" 新内容\",\n sideBySide: \" 左右对比 · 左侧删除,右侧新增 · 按偏移配对\",\n linesAbove: \" ↑ 上方 {count} 行(↑/k 或 PgUp)\",\n linesAbovePlural: \" ↑ 上方 {count} 行(↑/k 或 PgUp)\",\n linesBelow: \" ↓ 下方 {count} 行(↓/j 或 Space/PgDn)\",\n linesBelowPlural: \" ↓ 下方 {count} 行(↓/j 或 Space/PgDn)\",\n },\n sessionPicker: {\n header: \" ◈ LUCKERR · 选择会话 \",\n title: \"选择会话 — {workspace}\",\n messages: \"{count} 条消息\",\n messagesPlural: \"{count} 条消息\",\n turns: \"{count} 轮\",\n pickerHint: \"↑↓ 选择 · ⏎ 打开 · [n] 新建 · [d] 删除 · [r] 重命名 · Esc 退出\",\n empty: \" 此工作区暂无已保存的会话 — 按 \",\n emptyNew: \" 开始新会话\",\n renamePrompt: ' 重命名 \"{from}\" → ',\n renameHint: \" ⏎ 确认重命名 · Esc 取消\",\n emptyHint: \" ⏎ 新建会话 · Esc 退出\",\n justNow: \"刚刚\",\n minAgo: \"{count} 分钟前\",\n yesterday: \"昨天\",\n hoursAgo: \"{count} 小时前\",\n daysAgo: \"{count} 天前\",\n },\n modelPicker: {\n header: \" ◈ LUCKERR · 选择配置 \",\n loading: \" · 加载目录…\",\n catalogEmpty: \" · 目录为空 — 使用已知备选\",\n modelsAvailable: \" · {count} 个模型可用\",\n presetsHeader: \" 预设 · 推荐 — 模型 + 强度 + 自动升级\",\n modelsHeader: \" 模型 · 直接选择 — 自动升级保持不变\",\n pickerFooter: \" ↑↓ 选择 · ⏎ 确认 · [r] 刷新 · Esc 取消\",\n currentLabel: \" · 当前\",\n },\n slashSuggestions: {\n noMatch: \"没有匹配此前缀的斜杠命令\",\n backspaceHint: \" — 按 Backspace 修改,或 /help 查看完整列表\",\n commandCount: \"{count} 个命令\",\n commandCountPlural: \"{count} 个命令\",\n aboveLabel: \" ↑ {count} 个以上\",\n belowLabel: \" ↓ {count} 个以下\",\n advancedHint: \" + {count} 个高级命令 · 输入字母搜索\",\n footerHint: \" ↑↓ 导航 · Tab / ⏎ 选择 · Esc 取消\",\n groupChat: \"聊天\",\n groupSetup: \"设置\",\n groupInfo: \"信息\",\n groupSession: \"会话\",\n groupExtend: \"扩展\",\n groupCode: \"代码\",\n groupJobs: \"任务\",\n groupAdvanced: \"高级\",\n groupDetailSetup: \"模型 + 成本\",\n groupDetailInfo: \"当前状态\",\n groupDetailChat: \"日常聊天操作\",\n groupDetailExtend: \"MCP, 记忆, 技能\",\n groupDetailSession: \"已保存的会话\",\n groupDetailCode: \"编辑 + 计划 (代码模式)\",\n groupDetailJobs: \"后台进程 (代码模式)\",\n groupDetailAdvanced: \"高级或一次性设置\",\n },\n atMentions: {\n loading: \"加载中…\",\n entrySingular: \"{count} 条\",\n entryPlural: \"{count} 条\",\n searching: \"搜索中…\",\n scanned: \"已扫描\",\n match: \"个匹配\",\n matches: \"个匹配\",\n forFilter: '匹配 \"{filter}\"',\n noMatch: '没有匹配 \"{filter}\" 的文件',\n emptyDir: \"空目录\",\n scanning: \"正在扫描目录树…\",\n footerBrowse: \"↑↓ 导航 · Tab 进入文件夹 · ⏎ 插入 · Esc 取消\",\n footerBrowseSearch: \"↑↓ 导航 · Tab / ⏎ 以 @path 插入 · Esc 取消\",\n footerInsert: \"↑↓ 导航 · Tab / ⏎ 以 @path 插入 · Esc 取消\",\n },\n statsPanel: {\n modePlan: \"计划\",\n modeYolo: \"自由\",\n modeAuto: \"自动\",\n modeReview: \"审查\",\n pro: \"⇧ 专业\",\n budget: \" 预算 \",\n },\n welcomeBanner: {\n workspace: \"▸ 工作区\",\n relaunchHint: \"(重启时用 --dir <path> 切换)\",\n dashboard: \"▸ 网页\",\n },\n ctxBreakdown: {\n title: \"▣ 上下文\",\n compactHint: \" /compact 折叠(超过 50% 自动触发)· /new 清空日志\",\n topTools: \" 常用工具(按成本排序,{count} 个):\",\n msg: \"条\",\n turnLabel: \"轮\",\n },\n startup: {\n codeRooted:\n '▸ luckerr code:根目录 {rootDir},会话 \"{session}\" · {tools} 个原生工具{semantic}',\n ephemeral: \"(临时)\",\n semanticOn: \" · 语义搜索已开启\",\n },\n doctorErrors: {\n unreadable: \"{path} 无法读取 — {message}\",\n cannotList: \"无法列出 — {message}\",\n parseFailed: \"无法解析 settings.json — {message}\",\n probeFailed: \"探测失败 — {message}\",\n },\n webErrors: {\n status:\n \"web_search {status} — try: 搜索后端返回错误;请改写查询,或使用 /search-engine mojeek|searxng 切换引擎\",\n rateLimit429:\n \"web_search 429 — try: 等待 10 秒后重试,或改写查询;搜索后端正在对该客户端进行限流\",\n forbidden403:\n \"web_search 403 — try: 搜索后端拒绝该客户端访问;使用 /search-engine mojeek|searxng 切换引擎,或稍后重试\",\n serverError5xx:\n \"web_search {status} — try: 在浏览器中打开搜索 URL;若能加载则属临时故障,等 30 秒重试即可\",\n mojeekBlocked:\n \"web_search: Mojeek 反爬页面 — 频率限制或被屏蔽 — try: 等待 30 秒后重试,或使用 /search-engine searxng 切换引擎\",\n mojeekNoResults:\n \"web_search: 返回 0 条结果但响应看起来不是正常空结果页({chars} 字符,前 120 字符:{preview})— try: 使用更简单的关键词改写查询,或使用 /search-engine searxng 切换引擎\",\n invalidEndpoint:\n 'web_search: 无效的 SearXNG 端点 \"{endpoint}\" — try: 使用 /search-endpoint http://host:port 设置有效的 URL',\n endpointMustBeHttp:\n \"web_search: SearXNG 端点必须是 http(s) 协议,当前为 {protocol} — try: 使用 /search-endpoint http://host:port 设置有效的 URL\",\n cannotReach:\n \"web_search: 无法访问 SearXNG 服务器 {endpoint} — try: 安装并启动 SearXNG(https://github.com/searxng/searxng,例如 `docker run -d -p 8080:8080 searxng/searxng`),或使用 /search-engine mojeek 切换到默认引擎\",\n searxngNoResults:\n \"web_search: 返回 0 条结果但 SearXNG 响应看起来不是正常空结果页({chars} 字符)— try: 使用更简单的关键词改写查询,或使用 /search-engine mojeek 切换引擎\",\n fetchStatus:\n \"web_fetch {status} for {url} — try: 在浏览器中确认该 URL 能否访问;该状态码表明目标主机返回了错误页面\",\n fetchRateLimit429:\n \"web_fetch 429 for {url} — try: 等待 10 秒后重试;目标主机正在对该客户端进行限流\",\n fetchForbidden403:\n \"web_fetch 403 for {url} — try: 目标主机拒绝该客户端访问;该页面可能需要登录或屏蔽爬虫 — 改用 web_search 摘要\",\n fetchServerError5xx:\n \"web_fetch {status} for {url} — try: 在浏览器中打开该 URL;若能加载则属临时故障,等 30 秒重试即可\",\n fetchTimeout:\n \"web_fetch: timed out after {ms}ms for {url} — try: 更短的 URL 或更小的内容;可能是 CDN 较慢,或重试一次\",\n fetchTooLarge:\n \"web_fetch 拒绝:content-length {len} 字节超过上限 {cap} 字节({url})— try: 改换其他 URL 获取较小的内容;该页面过大无法获取\",\n fetchBodyTooLarge:\n \"web_fetch 拒绝:响应体超过 {cap} 字节上限(已接收 {seen} 字节)— try: 改换其他 URL 获取较小的内容;该页面流式传输超出大小上限\",\n fetchInvalidUrl:\n \"web_fetch: URL 必须以 http:// 或 https:// 开头 — try: 传入绝对的 http(s) URL(该 URL 格式错误或使用了不支持的协议)\",\n },\n choiceConfirm: {\n customLabel: \"自定义回答\",\n customDesc: \"以上选项都不合适 — 输入自由格式回复,模型会原样读取\",\n cancelLabel: \"取消 — 放弃问题\",\n cancelDesc: \"模型停止并询问你真正的需求\",\n },\n cardTitles: {\n usage: \"用量\",\n context: \"上下文\",\n search: \"搜索\",\n subagent: \"子代理\",\n reply: \"回复\",\n reasoning: \"推理中\",\n reasoningAborted: \"推理(已中止)\",\n reasoningEllipsis: \"推理中…\",\n error: \"错误\",\n doctor: \"环境诊断\",\n you: \"你\",\n task: \"任务\",\n },\n cardLabels: {\n prompt: \"提示\",\n reason: \"推理\",\n output: \"输出\",\n cache: \"缓存\",\n session: \"会话\",\n balance: \"余额\",\n turn: \"轮\",\n system: \"系统\",\n tools: \"工具\",\n log: \"日志\",\n input: \"输入\",\n topTools: \"常用工具\",\n logMsgs: \"日志消息\",\n hitSingular: \"{count} 条结果 · {files} 个文件\",\n hitsPlural: \"{count} 条结果 · {files} 个文件\",\n moreHitSingular: \"⋮ +{count} 条结果\",\n moreHitsPlural: \"⋮ +{count} 条结果\",\n earlierLine: \"⋮ 前 {count} 行(使用 /tool 阅读全文)\",\n earlierLines: \"⋮ 前 {count} 行(使用 /tool 阅读全文)\",\n earlierStackLine: \"⋮ 前 {count} 行堆栈已隐藏\",\n earlierStackLines: \"⋮ 前 {count} 行堆栈已隐藏\",\n agent: \"代理 · {name}\",\n response: \"回复\",\n writing: \"输出中 …\",\n tok: \"tok\",\n pilcrow: \"¶\",\n aborted: \"已中止\",\n truncatedByEsc: \"[已被 Esc 截断]\",\n rejected: \"已拒绝\",\n exit: \"退出码 {code}\",\n bytesIn: \"{bytes} 输入\",\n elapsedSec: \"{secs}秒\",\n stackTrace: \"堆栈跟踪\",\n retries: \"次重试\",\n reasoningLabel: \"推理 · {count} ¶\",\n runningLabel: \"运行中\",\n workingLabel: \"处理中\",\n defaultFooter: \"↑↓ 选择 · ⏎ 确认 · Esc 取消\",\n applyAction: \"[a] 应用\",\n skipAction: \"[s] 跳过\",\n rejectAction: \"[r] 拒绝\",\n levelOk: \"正常\",\n levelWarn: \"警告\",\n levelFail: \"失败\",\n checksLabel: \"检查项\",\n passed: \"通过\",\n warnTag: \"警告\",\n failTag: \"失败\",\n stepLabel: \"步骤\",\n done: \"已完成\",\n inProgress: \"← 进行中\",\n upcoming: \"待处理\",\n resumed: \"已恢复 · \",\n archive: \"⏪ 归档 · \",\n more: \"⋮ +{count} 更多\",\n categoryUser: \"用户\",\n categoryFeedback: \"反馈\",\n categoryProject: \"项目\",\n categoryReference: \"参考\",\n },\n copyMode: {\n title: \"── 复制模式 ──\",\n help: \"j/k 或 ↑/↓ 移动 · v 起选区 · y 复制 · g/G 顶/底 · q 退出\",\n statusBar: \"第 {cur}/{total} 行 · 选区:{sel}\",\n statusYanked: \"已复制 {size} 字符(osc52={osc52})\",\n statusEmpty: \"未选中内容\",\n empty: \"(还没有聊天内容 — 先和模型说点什么)\",\n labelUser: \"你\",\n labelAssistant: \"助手\",\n labelReasoning: \"推理\",\n yankedToast: \"▸ 已复制 {size} 字符到剪贴板 (osc52)\",\n yankedToastFile: \"▸ 已复制 {size} 字符 · 文件:{path}\",\n },\n mcpHealth: {\n noData: \"无检查数据\",\n healthy: \"正常 \\u00b7 {ms}ms\",\n slow: \"缓慢 \\u00b7 {ms}ms\",\n verySlow: \"非常慢 \\u00b7 {ms}ms\",\n slowToast: \"\\u26a0 MCP `{name}` 响应缓慢 \\u00b7 P95 {seconds}s \\u00b7 最近 {sampleSize} 次调用\",\n emptyHint:\n \"\\u2139 未配置 MCP 服务器 —— 可尝试:`luckerr setup` 重新选择,或 `luckerr mcp install filesystem`\",\n },\n denyContextInput: {\n description: \"告诉模型你为什么拒绝了。模型下次会看到你的理由作为额外的上下文。\",\n },\n cardStream: {\n scrollAbove: \" \\u2191 {scroll}/{max} 行\",\n scrollAbovePlural: \" \\u2191 {scroll}/{max} 行\",\n scrollMore: \" \\u2014 还有 {remaining} 行\",\n scrollPgUp: \" \\u00b7 PgUp/\\u6eda\\u8f6e/\\u2191\",\n },\n slashArgPicker: {\n noMatch: '\\u6ca1\\u6709\\u5339\\u914d \"{partial}\"',\n keepTyping: \" \\u2014 \\u7ee7\\u7eed\\u8f93\\u5165\\uff0c\\u6216 Backspace \\u4fee\\u6539\",\n above: \" \\u2191 \\u8fd8\\u6709 {hidden} \\u4e2a\",\n below: \" \\u2193 \\u8fd8\\u6709 {hidden} \\u4e2a\",\n footer: \" \\u2191\\u2193 \\u5bfc\\u822a \\u00b7 Tab/\\u23ce \\u9009\\u62e9 \\u00b7 Esc \\u53d6\\u6d88\",\n },\n mcpMarketplace: {\n title: \"MCP 市场\",\n filter: \"筛选:\",\n filterPlaceholder: \"(输入筛选)\",\n matchSingular: \"{n} 条匹配\",\n matchPlural: \"{n} 条匹配\",\n loading: \"加载中…\",\n noEntries: \"无条目\",\n opening: \"正在打开注册表…\",\n cached: \" · 已缓存\",\n exhausted: \" · 已耗尽\",\n loadingMore: \"加载更多…\",\n allLoaded: \"所有页面已加载\",\n fetchingDetail: \"正在获取 smithery 详情…\",\n noInstallInfo: \"没有 {name} 的安装信息 — 试试 `npx -y @smithery/cli install {name}`\",\n alreadyInstalled: \"已安装:{spec}\",\n installed: \"已安装 → {spec}\",\n uninstalled: \"已卸载 {name}\",\n installFailed: \"安装失败:{message}\",\n notInstalled: \"未安装:{name}\",\n bridged: \"✓ 已安装 {name} — 已桥接\",\n bridgeFailed: \"▲ 已安装 {name} — 桥接失败:{reason}\",\n bridgeReloadFailed: \"✓ 已安装 {name} — 重启 `luckerr code` 以桥接(重载失败:{message})\",\n restartBridge: \"✓ 已安装 {name} — 重启 `luckerr code` 以桥接\",\n needsEnv: \" · 需要环境变量:{env}\",\n badgeOfficial: \"[官方]\",\n badgeSmithery: \"[三方]\",\n badgeLocal: \"[本地]\",\n footerHint: \"输入筛选 · ↑↓ 选择 · ⏎ 安装/切换 · PgDn 加载更多 · Esc 关闭\",\n specLine: \"配置:{runtime} {id} \\u00b7 {transport}\",\n smitheryDetail: \"(smithery 列表 \\u2014 按 Enter 获取安装详情)\",\n statusError: \"错误:{message}\",\n },\n mcpBrowser: {\n title: \"◈ MCP 浏览器\",\n empty: \"没有挂载 MCP 服务器。运行 `luckerr setup` 选择一些,或使用 --mcp 启动。\",\n serverCount: \"{count} 个服务器\",\n footer: \"↑↓ 选择 · [r] 重连 · [d] 禁用 · Esc 退出\",\n },\n mcpLifecycle: {\n handshake: \"握手中…\",\n connected: \"已连接\",\n failed: \"失败\",\n disabled: \"已禁用\",\n reconnect: \"重连中…\",\n initDetail: \"初始化 → tools/list → resources/list\",\n reconnectDetail: \"断开旧连接 · 重新握手 · 列出工具\",\n disabledDetail: \"通过 /mcp disable {name}\",\n },\n checkpointPicker: {\n title: \"恢复检查点 \\u2014 {workspace}\",\n header: \" \\u25c8 LUCKERR \\u00b7 选择检查点 \",\n empty: \" 此工作区暂无检查点 — 参见 /checkpoint 创建\",\n more: \" … 还有 {hidden} 个\",\n footer: \" ↑↓ 选择 · ⏎ 恢复 · [d] 删除 · Esc 退出\",\n footerEmpty: \" Esc 退出\",\n },\n planReviseConfirm: {\n title: \"计划修改已提交\",\n metaRight: \"−{removed} +{added} · {kept} 个保留\",\n updatedSummary: \"更新摘要:{summary}\",\n acceptLabel: \"接受修改 — 应用新的步骤列表\",\n acceptHint: \"用新步骤替换剩余计划。已完成的步骤不变。\",\n rejectLabel: \"拒绝 — 保留原计划\",\n rejectHint: \"放弃修改。模型继续按原步骤执行。\",\n },\n diffApp: {\n title: \"luckerr diff\",\n turnLabel: \"第 {turn} 轮({current}/{total})\",\n turnsAligned: \"{count} 轮已对齐\",\n paneEmpty: \"(此轮该侧无记录)\",\n kindMatch: \"✓ 一致\",\n kindDiverge: \"★ 分歧\",\n kindOnlyInA: \"← 仅 A 有\",\n kindOnlyInB: \"→ 仅 B 有\",\n },\n recordView: {\n userPrefix: \"你 › \",\n assistant: \"助手\",\n toolPrefix: \"tool<\",\n argsLabel: \" 参数:\",\n resultArrow: \" → \",\n error: \"错误 \",\n cache: \" · 缓存 \",\n toolCallOnly: \"(仅工具调用响应)\",\n truncateExtra: \"(+{extra} 字符)\",\n },\n replayApp: {\n emptyTranscript: \"空记录\",\n turnProgress: \"第 {current}/{total} 轮\",\n noRecords: \"无记录\",\n untracked: \"(未追踪)\",\n churned: \"(已变更 ×{count})\",\n },\n};\n","import { loadLanguage, saveLanguage } from \"../config.js\";\nimport { EN } from \"./EN.js\";\nimport type { LanguageCode, TranslationSchema } from \"./types.js\";\nimport { zhCN } from \"./zh-CN.js\";\n\nconst translations: Record<LanguageCode, TranslationSchema> = {\n EN,\n \"zh-CN\": zhCN,\n};\n\n/** Map a system locale (e.g. \"zh-CN\", \"en-US\") to a supported LanguageCode, or null. */\nexport function detectSystemLanguage(\n locale: string = Intl.DateTimeFormat().resolvedOptions().locale,\n): LanguageCode | null {\n if (locale.startsWith(\"zh\")) return \"zh-CN\";\n if (locale.startsWith(\"en\")) return \"EN\";\n return null;\n}\n\nlet currentLang: LanguageCode = loadLanguage() ?? detectSystemLanguage() ?? \"EN\";\n\ntype Listener = () => void;\nconst listeners: Listener[] = [];\n\nexport function onLanguageChange(cb: Listener): () => void {\n listeners.push(cb);\n return () => {\n const i = listeners.indexOf(cb);\n if (i >= 0) listeners.splice(i, 1);\n };\n}\n\nexport function notifyLanguageChange(): void {\n for (const cb of listeners) cb();\n}\n\nexport function setLanguage(lang: LanguageCode): void {\n if (translations[lang]) {\n currentLang = lang;\n saveLanguage(lang);\n }\n}\n\n/** Set language for the current process only (no disk write). Used by tests. */\nexport function setLanguageRuntime(lang: LanguageCode): void {\n if (translations[lang]) {\n currentLang = lang;\n }\n}\n\nexport function getLanguage(): LanguageCode {\n return currentLang;\n}\n\nexport function getSupportedLanguages(): LanguageCode[] {\n return Object.keys(translations) as LanguageCode[];\n}\n\n/** Returns a structured (non-string) translation entry — for tables / row objects passed to TipCard etc. */\nexport function tObj<T>(path: string): T {\n const parts = path.split(\".\");\n let val: unknown = translations[currentLang] || translations.EN;\n for (const part of parts) {\n val = (val as Record<string, unknown> | undefined)?.[part];\n if (val === undefined) break;\n }\n if (val === undefined && currentLang !== \"EN\") {\n val = translations.EN;\n for (const part of parts) {\n val = (val as Record<string, unknown> | undefined)?.[part];\n if (val === undefined) break;\n }\n }\n return val as T;\n}\n\n/** Simple t() — nested keys (e.g. \"common.error\") + param replacement (e.g. \"{code}\"). */\nexport function t(path: string, params?: Record<string, string | number>): string {\n const parts = path.split(\".\");\n let val: any = translations[currentLang] || translations.EN;\n\n for (const part of parts) {\n val = val?.[part];\n if (val === undefined) break;\n }\n\n // Fallback to English if not found in current language\n if (val === undefined && currentLang !== \"EN\") {\n val = translations.EN;\n for (const part of parts) {\n val = val?.[part];\n if (val === undefined) break;\n }\n }\n\n if (typeof val !== \"string\") {\n return path;\n }\n\n if (params) {\n let result = val;\n for (const [k, v] of Object.entries(params)) {\n result = result.replace(new RegExp(`\\\\{${k}\\\\}`, \"g\"), String(v));\n }\n return result;\n }\n\n return val;\n}\n","/** Encode-only DeepSeek V3 tokenizer port; ~3% drift vs API (chat-template framing not replayed). */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { gunzipSync } from \"node:zlib\";\n\ninterface AddedToken {\n id: number;\n content: string;\n special: boolean;\n normalized: boolean;\n}\n\ninterface SplitPretokenizer {\n type: \"Split\";\n pattern: { Regex: string };\n behavior: \"Isolated\" | \"Removed\" | string;\n invert: boolean;\n}\n\ninterface ByteLevelPretokenizer {\n type: \"ByteLevel\";\n add_prefix_space: boolean;\n trim_offsets: boolean;\n use_regex: boolean;\n}\n\ntype Pretokenizer = SplitPretokenizer | ByteLevelPretokenizer;\n\ninterface TokenizerData {\n added_tokens: AddedToken[];\n pre_tokenizer: {\n type: \"Sequence\";\n pretokenizers: Pretokenizer[];\n };\n model: {\n type: \"BPE\";\n vocab: Record<string, number>;\n merges: string[];\n };\n}\n\ninterface LoadedTokenizer {\n vocab: Record<string, number>;\n mergeRank: Map<string, number>;\n splitRegexes: RegExp[];\n byteToChar: string[];\n /** Non-special added tokens only — special tokens in user text tokenize byte-by-byte (HF default). */\n addedPattern: RegExp | null;\n addedMap: Map<string, number>;\n}\n\n/** GPT-2 byte→unicode map; lets byte-level BPE vocab serialize as readable JSON strings. */\nfunction buildByteToChar(): string[] {\n const result: string[] = new Array(256);\n const bs: number[] = [];\n for (let b = 33; b <= 126; b++) bs.push(b);\n for (let b = 161; b <= 172; b++) bs.push(b);\n for (let b = 174; b <= 255; b++) bs.push(b);\n const cs = bs.slice();\n let n = 0;\n for (let b = 0; b < 256; b++) {\n if (!bs.includes(b)) {\n bs.push(b);\n cs.push(256 + n);\n n++;\n }\n }\n for (let i = 0; i < bs.length; i++) {\n result[bs[i]!] = String.fromCodePoint(cs[i]!);\n }\n return result;\n}\n\nlet cached: LoadedTokenizer | null = null;\n\n/** Two ../data candidates needed: dist/index.js AND dist/cli/index.js resolve to different roots. */\nexport function resolveDataPath(): string {\n if (process.env.LUCKERR_TOKENIZER_PATH) return process.env.LUCKERR_TOKENIZER_PATH;\n const candidates: string[] = [];\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n candidates.push(join(here, \"..\", \"data\", \"deepseek-tokenizer.json.gz\"));\n candidates.push(join(here, \"..\", \"..\", \"data\", \"deepseek-tokenizer.json.gz\"));\n } catch {\n /* import.meta.url unavailable — skip to the package resolution step. */\n }\n try {\n const req = createRequire(import.meta.url);\n candidates.push(\n join(dirname(req.resolve(\"luckerr/package.json\")), \"data\", \"deepseek-tokenizer.json.gz\"),\n );\n } catch {\n /* Not installed as `luckerr/` — the earlier candidates still may hit. */\n }\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n // Nothing exists — return the first candidate anyway so readFileSync\n // surfaces a concrete path in the ENOENT message (better than silent miss).\n return candidates[0] ?? join(process.cwd(), \"data\", \"deepseek-tokenizer.json.gz\");\n}\n\nfunction loadTokenizer(): LoadedTokenizer {\n if (cached) return cached;\n const buf = readFileSync(resolveDataPath());\n const json = gunzipSync(buf).toString(\"utf8\");\n const data = JSON.parse(json) as TokenizerData;\n\n const mergeRank = new Map<string, number>();\n for (let i = 0; i < data.model.merges.length; i++) {\n mergeRank.set(data.model.merges[i]!, i);\n }\n\n const splitRegexes: RegExp[] = [];\n for (const p of data.pre_tokenizer.pretokenizers) {\n if (p.type === \"Split\") {\n // All three Split rules use Isolated — matches become their own\n // pre-tokens and so do the in-between stretches. The ByteLevel\n // stage in the Sequence does no extra splitting here\n // (use_regex:false), so our 3 Split regexes are the whole story.\n splitRegexes.push(new RegExp(p.pattern.Regex, \"gu\"));\n }\n }\n\n const addedMap = new Map<string, number>();\n const addedContents: string[] = [];\n for (const t of data.added_tokens) {\n if (!t.special) {\n addedMap.set(t.content, t.id);\n addedContents.push(t.content);\n }\n }\n // Longest-first ensures greedy matching doesn't lose a longer token\n // to a shorter prefix (e.g. `<think>` before `<`).\n addedContents.sort((a, b) => b.length - a.length);\n const addedPattern = addedContents.length\n ? new RegExp(addedContents.map(escapeRegex).join(\"|\"), \"g\")\n : null;\n\n cached = {\n vocab: data.model.vocab,\n mergeRank,\n splitRegexes,\n byteToChar: buildByteToChar(),\n addedPattern,\n addedMap,\n };\n return cached;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction applySplit(chunks: string[], re: RegExp): string[] {\n const out: string[] = [];\n for (const chunk of chunks) {\n if (!chunk) continue;\n // Reset lastIndex — reusing a /g regex across matchAll iterations\n // is safe (matchAll internally advances), but across different\n // input strings we want a clean start.\n re.lastIndex = 0;\n let last = 0;\n for (const m of chunk.matchAll(re)) {\n const idx = m.index ?? 0;\n if (idx > last) out.push(chunk.slice(last, idx));\n if (m[0].length > 0) out.push(m[0]);\n last = idx + m[0].length;\n }\n if (last < chunk.length) out.push(chunk.slice(last));\n }\n return out;\n}\n\n/** UTF-8 bytes of `s`, each mapped to its byte-level visible char. */\nfunction byteLevelEncode(s: string, byteToChar: string[]): string {\n const bytes = new TextEncoder().encode(s);\n let out = \"\";\n for (let i = 0; i < bytes.length; i++) out += byteToChar[bytes[i]!];\n return out;\n}\n\nfunction bpeEncode(piece: string, mergeRank: Map<string, number>): string[] {\n if (piece.length <= 1) return piece ? [piece] : [];\n let word: string[] = Array.from(piece);\n while (true) {\n let bestIdx = -1;\n let bestRank = Number.POSITIVE_INFINITY;\n for (let i = 0; i < word.length - 1; i++) {\n const pair = `${word[i]} ${word[i + 1]}`;\n const rank = mergeRank.get(pair);\n if (rank !== undefined && rank < bestRank) {\n bestRank = rank;\n bestIdx = i;\n if (rank === 0) break; // 0 is already the best possible\n }\n }\n if (bestIdx < 0) break;\n word = [\n ...word.slice(0, bestIdx),\n word[bestIdx]! + word[bestIdx + 1]!,\n ...word.slice(bestIdx + 2),\n ];\n if (word.length === 1) break;\n }\n return word;\n}\n\nexport function encode(text: string): number[] {\n if (!text) return [];\n const t = loadTokenizer();\n const ids: number[] = [];\n\n const process = (segment: string) => {\n if (!segment) return;\n let chunks: string[] = [segment];\n for (const re of t.splitRegexes) chunks = applySplit(chunks, re);\n for (const chunk of chunks) {\n if (!chunk) continue;\n const byteLevel = byteLevelEncode(chunk, t.byteToChar);\n const pieces = bpeEncode(byteLevel, t.mergeRank);\n for (const p of pieces) {\n const id = t.vocab[p];\n // If not in vocab we silently skip: shouldn't happen for\n // byte-level BPE (every single byte has its own vocab entry),\n // but if a future tokenizer update breaks that invariant we'd\n // rather under-count than throw from a UI gauge.\n if (id !== undefined) ids.push(id);\n }\n }\n };\n\n if (t.addedPattern) {\n t.addedPattern.lastIndex = 0;\n let last = 0;\n for (const m of text.matchAll(t.addedPattern)) {\n const idx = m.index ?? 0;\n if (idx > last) process(text.slice(last, idx));\n const id = t.addedMap.get(m[0]);\n if (id !== undefined) ids.push(id);\n last = idx + m[0].length;\n }\n if (last < text.length) process(text.slice(last));\n } else {\n process(text);\n }\n return ids;\n}\n\nexport function countTokens(text: string): number {\n return encode(text).length;\n}\n\n/** Doesn't add chat-template framing overhead; under-counts ~3-6% vs real `prompt_tokens`. */\nexport function estimateConversationTokens(\n messages: Array<{ content?: string | null; tool_calls?: unknown }>,\n): number {\n let total = 0;\n for (const m of messages) {\n if (typeof m.content === \"string\" && m.content) {\n total += countTokens(m.content);\n }\n // Tool-call arguments are serialized as JSON in the prompt by the\n // chat template; their bytes WILL count upstream, so we count\n // them too. Stringify-once is cheap relative to the tokenize.\n if (m.tool_calls && Array.isArray(m.tool_calls) && m.tool_calls.length > 0) {\n total += countTokens(JSON.stringify(m.tool_calls));\n }\n }\n return total;\n}\n\n/** Tool specs ride in a separate request blob; must be counted separately for an accurate preflight. */\nexport function estimateRequestTokens(\n messages: Array<{ content?: string | null; tool_calls?: unknown }>,\n toolSpecs?: ReadonlyArray<unknown> | null,\n): number {\n let total = estimateConversationTokens(messages);\n if (toolSpecs && toolSpecs.length > 0) {\n total += countTokens(JSON.stringify(toolSpecs));\n }\n return total;\n}\n\n/** Exposed for tests — resets the lazy-load singleton. */\nexport function _resetForTests(): void {\n cached = null;\n}\n","/** DeepSeek drops args on schemas >2 levels deep or >10 leaves; flatten to dot-paths and re-nest after dispatch. */\n\nimport type { JSONSchema } from \"../types.js\";\n\nexport interface FlattenDecision {\n shouldFlatten: boolean;\n leafCount: number;\n maxDepth: number;\n}\n\nexport function analyzeSchema(schema: JSONSchema | undefined): FlattenDecision {\n if (!schema) return { shouldFlatten: false, leafCount: 0, maxDepth: 0 };\n let leafCount = 0;\n let maxDepth = 0;\n walk(schema, 0, (depth, isLeaf) => {\n if (isLeaf) leafCount++;\n if (depth > maxDepth) maxDepth = depth;\n });\n return {\n shouldFlatten: leafCount > 10 || maxDepth > 2,\n leafCount,\n maxDepth,\n };\n}\n\nexport function flattenSchema(schema: JSONSchema): JSONSchema {\n const flatProps: Record<string, JSONSchema> = {};\n const required: string[] = [];\n collect(\"\", schema, flatProps, required, true);\n return {\n type: \"object\",\n properties: flatProps,\n required,\n };\n}\n\nexport function nestArguments(flatArgs: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(flatArgs)) {\n setByPath(out, key.split(\".\"), value);\n }\n return out;\n}\n\nfunction walk(\n schema: JSONSchema,\n depth: number,\n visit: (depth: number, isLeaf: boolean) => void,\n): void {\n if (schema.type === \"object\" && schema.properties) {\n for (const child of Object.values(schema.properties)) {\n walk(child, depth + 1, visit);\n }\n return;\n }\n if (schema.type === \"array\" && schema.items) {\n walk(schema.items, depth + 1, visit);\n return;\n }\n visit(depth, true);\n}\n\nfunction collect(\n prefix: string,\n schema: JSONSchema,\n out: Record<string, JSONSchema>,\n required: string[],\n isRootRequired: boolean,\n): void {\n if (schema.type === \"object\" && schema.properties) {\n const requiredSet = new Set(schema.required ?? []);\n for (const [key, child] of Object.entries(schema.properties)) {\n const nextPrefix = prefix ? `${prefix}.${key}` : key;\n const childRequired = isRootRequired && requiredSet.has(key);\n collect(nextPrefix, child, out, required, childRequired);\n }\n return;\n }\n // Treat anything non-object (including arrays) as a leaf for flattening purposes.\n out[prefix] = schema;\n if (isRootRequired) required.push(prefix);\n}\n\nfunction setByPath(target: Record<string, unknown>, path: string[], value: unknown): void {\n let cur: any = target;\n for (let i = 0; i < path.length - 1; i++) {\n const key = path[i]!;\n if (typeof cur[key] !== \"object\" || cur[key] === null) cur[key] = {};\n cur = cur[key];\n }\n cur[path[path.length - 1]!] = value;\n}\n","import type { PauseGate } from \"./core/pause-gate.js\";\nimport { truncateForModel, truncateForModelByTokens } from \"./mcp/registry.js\";\nimport { analyzeSchema, flattenSchema, nestArguments } from \"./repair/flatten.js\";\nimport type { JSONSchema, ToolSpec } from \"./types.js\";\n\nexport interface ToolCallContext {\n signal?: AbortSignal;\n /** Inject a mock PauseGate for tests. When absent, tools use the singleton. */\n confirmationGate?: PauseGate;\n}\n\nexport interface ToolDefinition<A = any, R = any> {\n name: string;\n description?: string;\n parameters?: JSONSchema;\n /** Safe in plan mode — registry refuses non-readonly calls when `planMode` is on. */\n readOnly?: boolean;\n /** Per-args check; takes precedence over `readOnly`. e.g. `run_command` + allowlisted argv. */\n readOnlyCheck?: (args: A) => boolean;\n /** Safe to dispatch concurrently with other parallel-safe calls in the same turn. Default false — opt-in only. */\n parallelSafe?: boolean;\n /** Excluded from repeat-loop storm accounting; use only for cheap, state-inspection tools. */\n stormExempt?: boolean;\n fn: (args: A, ctx?: ToolCallContext) => R | Promise<R>;\n}\n\ninterface InternalTool extends ToolDefinition {\n /** Set when schema is deep (>2 levels) or wide (>10 leaves) — DeepSeek V3/R1 drop args otherwise. */\n flatSchema?: JSONSchema;\n}\n\nexport interface ToolRegistryOptions {\n /** Auto-flatten + re-nest at dispatch; default true. */\n autoFlatten?: boolean;\n}\n\nexport type ToolCallAuditEvent = {\n name: string;\n args: Record<string, unknown>;\n};\n\nexport type ToolCallAuditListener = (event: ToolCallAuditEvent) => void;\n\n/** String return short-circuits dispatch; null/undefined falls through to the tool fn. */\nexport type ToolInterceptor = (\n name: string,\n args: Record<string, unknown>,\n) => string | null | undefined | Promise<string | null | undefined>;\n\n/** Final-stage post-processor — runs on every dispatch return (success and error paths) so callers can append context like a remaining-budget hint. Whatever it returns becomes the dispatch result. */\nexport type ToolResultAugmenter = (\n name: string,\n args: Record<string, unknown>,\n result: string,\n) => string;\n\nexport class ToolRegistry {\n private readonly _tools = new Map<string, InternalTool>();\n private readonly _autoFlatten: boolean;\n private _planMode = false;\n private _interceptor: ToolInterceptor | null = null;\n private _auditListener: ToolCallAuditListener | null = null;\n private _resultAugmenter: ToolResultAugmenter | null = null;\n /** Per-tool fingerprint of the last call that failed schema validation. Cleared by any successful validation for that tool. */\n private readonly _lastMalformed = new Map<string, string>();\n\n constructor(opts: ToolRegistryOptions = {}) {\n this._autoFlatten = opts.autoFlatten !== false;\n }\n\n /** Enable / disable plan-mode enforcement at dispatch. */\n setPlanMode(on: boolean): void {\n this._planMode = Boolean(on);\n }\n\n /** True when the registry is currently refusing non-readonly calls. */\n get planMode(): boolean {\n return this._planMode;\n }\n\n /** At most one interceptor active; calling twice replaces. */\n setToolInterceptor(fn: ToolInterceptor | null): void {\n this._interceptor = fn;\n }\n\n setAuditListener(fn: ToolCallAuditListener | null): void {\n this._auditListener = fn;\n }\n\n /** Final-stage post-processor; replaces previous augmenter when called twice. Pass null to clear. */\n setResultAugmenter(fn: ToolResultAugmenter | null): void {\n this._resultAugmenter = fn;\n }\n\n /** True when an augmenter is already wired — lets late-installing callers skip clobbering an earlier one. */\n get hasResultAugmenter(): boolean {\n return this._resultAugmenter !== null;\n }\n\n register<A, R>(def: ToolDefinition<A, R>): this {\n if (!def.name) throw new Error(\"tool requires a name\");\n const internal: InternalTool = { ...(def as ToolDefinition) };\n if (this._autoFlatten && def.parameters) {\n const decision = analyzeSchema(def.parameters);\n if (decision.shouldFlatten) {\n internal.flatSchema = flattenSchema(def.parameters);\n }\n }\n this._tools.set(def.name, internal);\n return this;\n }\n\n /** Drop a registered tool. Returns true if the name was present. Used by MCP hot-unbridge. */\n unregister(name: string): boolean {\n return this._tools.delete(name);\n }\n\n has(name: string): boolean {\n return this._tools.has(name);\n }\n\n get(name: string): ToolDefinition | undefined {\n return this._tools.get(name);\n }\n\n get size(): number {\n return this._tools.size;\n }\n\n /** True if a registered tool's schema was flattened for the model. */\n wasFlattened(name: string): boolean {\n return Boolean(this._tools.get(name)?.flatSchema);\n }\n\n /** Unknown / unannotated tools default to false — third-party MCP tools must opt in. */\n isParallelSafe(name: string): boolean {\n return this._tools.get(name)?.parallelSafe === true;\n }\n\n specs(): ToolSpec[] {\n return [...this._tools.values()].map((t) => ({\n type: \"function\",\n function: {\n name: t.name,\n description: t.description ?? \"\",\n parameters: t.flatSchema ?? t.parameters ?? { type: \"object\", properties: {} },\n },\n }));\n }\n\n async dispatch(\n name: string,\n argumentsRaw: string | Record<string, unknown>,\n opts: {\n signal?: AbortSignal;\n maxResultChars?: number;\n maxResultTokens?: number;\n /** Inject a mock PauseGate for tests. */\n confirmationGate?: PauseGate;\n } = {},\n ): Promise<string> {\n const tool = this._tools.get(name);\n if (!tool) {\n return JSON.stringify({ error: `unknown tool: ${name}` });\n }\n const fingerprint = fingerprintArgs(argumentsRaw);\n let args: Record<string, unknown>;\n try {\n args =\n typeof argumentsRaw === \"string\"\n ? argumentsRaw.trim()\n ? (JSON.parse(argumentsRaw) ?? {})\n : {}\n : (argumentsRaw ?? {});\n } catch (err) {\n return this._noteMalformed(\n name,\n fingerprint,\n `invalid tool arguments JSON: ${(err as Error).message}`,\n );\n }\n\n // Re-nest dot-notation args back to the original shape, but only when\n // (a) we flattened this tool's schema, AND\n // (b) the incoming args actually use dot keys.\n // The second condition handles the case where a model ignores the flat\n // spec and emits nested args anyway — we shouldn't double-process them.\n if (tool.flatSchema && args && typeof args === \"object\" && hasDotKey(args)) {\n args = nestArguments(args);\n }\n\n const missing = tool.parameters ? missingRequiredParam(tool.parameters, args) : null;\n if (missing) {\n return this._noteMalformed(\n name,\n fingerprint,\n `missing required parameter \"${missing}\". Retry with all required parameters filled.`,\n );\n }\n // Validation passed — this tool's malformed-args streak is broken.\n this._lastMalformed.delete(name);\n\n // Plan-mode enforcement — runs AFTER arg parsing so a tool with a\n // runtime `readOnlyCheck` can inspect the actual args (e.g.\n // `run_command` is read-only iff the command matches its allowlist).\n if (this._planMode && !isReadOnlyCall(tool, args)) {\n return JSON.stringify({\n error: `${name}: unavailable in plan mode — this is a read-only exploration phase. Use read_file / list_directory / search_files / directory_tree / web_search / allowlisted shell commands to investigate. Call submit_plan with your proposed plan when you're ready for the user's review.`,\n rejectedReason: \"plan-mode\",\n });\n }\n\n // Interceptor runs after plan-mode (so a plan-mode refusal still\n // wins) but before the real tool fn. A string return is treated as\n // the full tool result; null / undefined means \"not my concern,\n // fall through.\" Uncaught throws from the interceptor are surfaced\n // through the same error path as a failed tool fn below.\n if (this._interceptor) {\n try {\n const short = await this._interceptor(name, args);\n if (typeof short === \"string\") return short;\n } catch (err) {\n return JSON.stringify({\n error: `${name}: interceptor failed — ${(err as Error).message}`,\n });\n }\n }\n\n let finalResult: string;\n try {\n try {\n this._auditListener?.({ name, args });\n } catch {\n /* audit path must never break tool execution */\n }\n const result = await tool.fn(args, {\n signal: opts.signal,\n confirmationGate: opts.confirmationGate,\n });\n const str = typeof result === \"string\" ? result : JSON.stringify(result);\n // Pre-clip at dispatch so a single fat result can't balloon the\n // log (and disk session file) on its way in. Healing at load time\n // still catches pre-existing oversize entries; this closes the\n // door on new ones.\n //\n // Two caps available: `maxResultTokens` (preferred — bounds the\n // real context footprint, so CJK doesn't slip past at 2× density)\n // and `maxResultChars` (legacy). If both are set, apply both and\n // the tighter one wins; char-only callers keep their old behavior.\n let clipped = str;\n if (opts.maxResultTokens !== undefined) {\n clipped = truncateForModelByTokens(clipped, opts.maxResultTokens);\n }\n if (opts.maxResultChars !== undefined) {\n clipped = truncateForModel(clipped, opts.maxResultChars);\n }\n finalResult = clipped;\n } catch (err) {\n const e = err as Error & { toToolResult?: () => unknown };\n // Errors may opt into a richer tool-result shape by implementing\n // `toToolResult()`. Used by `PlanProposedError` to smuggle the\n // submitted plan text out to the UI without stuffing it into the\n // error message (which the dispatcher truncates at no fixed limit,\n // but keeping payloads structured is cleaner for UI parsing).\n if (typeof e.toToolResult === \"function\") {\n try {\n finalResult = JSON.stringify(e.toToolResult());\n } catch {\n finalResult = JSON.stringify({ error: `${e.name}: ${e.message}` });\n }\n } else {\n finalResult = JSON.stringify({ error: `${e.name}: ${e.message}` });\n }\n }\n\n if (this._resultAugmenter) {\n try {\n return this._resultAugmenter(name, args, finalResult);\n } catch {\n /* augmenter must never break the tool result */\n }\n }\n return finalResult;\n }\n\n /** Records the failed call's fingerprint; on the 2nd consecutive identical malformed call to the same tool, returns a sharper error that tells the model to stop retrying. */\n private _noteMalformed(name: string, fingerprint: string, detail: string): string {\n const prev = this._lastMalformed.get(name);\n this._lastMalformed.set(name, fingerprint);\n if (prev === fingerprint) {\n return JSON.stringify({\n error: `${name}: same call just failed validation (${detail}) — DO NOT retry with identical args. Either fix the call (read the schema in the tool spec) or pick a different tool.`,\n consecutiveMalformed: true,\n });\n }\n return JSON.stringify({ error: `${name}: ${detail}` });\n }\n}\n\nfunction isReadOnlyCall(tool: InternalTool, args: Record<string, unknown>): boolean {\n if (tool.readOnlyCheck) {\n try {\n return Boolean(tool.readOnlyCheck(args as never));\n } catch (err) {\n // A buggy readOnlyCheck silently downgrades to \"may mutate\" — log it so\n // the bug doesn't hide behind plan-mode refusals or storm-breaker noise.\n process.stderr.write(`readOnlyCheck for ${tool.name} threw: ${(err as Error).message}\\n`);\n return false;\n }\n }\n return tool.readOnly === true;\n}\n\nfunction hasDotKey(obj: Record<string, unknown>): boolean {\n for (const k of Object.keys(obj)) {\n if (k.includes(\".\")) return true;\n }\n return false;\n}\n\n/** Stable per-call key for the malformed-args storm guard. String args compare as-is; objects round-trip through JSON so key order is stable. */\nfunction fingerprintArgs(argumentsRaw: string | Record<string, unknown>): string {\n if (typeof argumentsRaw === \"string\") return argumentsRaw;\n try {\n return JSON.stringify(argumentsRaw);\n } catch {\n return \"\";\n }\n}\n\n/** If the schema declares required params, return the first one that's missing. */\nfunction missingRequiredParam(schema: JSONSchema, args: Record<string, unknown>): string | null {\n const required = schema.required;\n if (!required || required.length === 0) return null;\n for (const key of required) {\n if (args[key] === undefined) return key;\n }\n return null;\n}\n","/** Per-server ring-buffered latency tracker; emits a \"slow\" event on threshold cross only. */\n\nconst SAMPLE_SIZE = 5;\nconst DEFAULT_THRESHOLD_MS = 4000;\n\nexport interface SlowEvent {\n serverName: string;\n p95Ms: number;\n sampleSize: number;\n}\n\nexport interface LatencyTrackerOptions {\n thresholdMs?: number;\n onSlow?: (ev: SlowEvent) => void;\n}\n\nexport class LatencyTracker {\n private samples: number[] = [];\n private wasOverThreshold = false;\n private readonly thresholdMs: number;\n private readonly onSlow?: (ev: SlowEvent) => void;\n\n constructor(\n private readonly serverName: string,\n opts: LatencyTrackerOptions = {},\n ) {\n this.thresholdMs = opts.thresholdMs ?? DEFAULT_THRESHOLD_MS;\n this.onSlow = opts.onSlow;\n }\n\n record(elapsedMs: number): void {\n this.samples.push(elapsedMs);\n if (this.samples.length > SAMPLE_SIZE) this.samples.shift();\n if (this.samples.length < SAMPLE_SIZE) return;\n const p95 = computeP95(this.samples);\n const nowOver = p95 > this.thresholdMs;\n if (nowOver && !this.wasOverThreshold) {\n this.onSlow?.({ serverName: this.serverName, p95Ms: p95, sampleSize: this.samples.length });\n }\n this.wasOverThreshold = nowOver;\n }\n}\n\n/** Plain p95 — sort the buffer and pick the index at floor(N * 0.95). */\nexport function computeP95(samples: readonly number[]): number {\n if (samples.length === 0) return 0;\n const sorted = [...samples].sort((a, b) => a - b);\n const idx = Math.min(sorted.length - 1, Math.floor(sorted.length * 0.95));\n return sorted[idx] ?? 0;\n}\n","import { countTokens } from \"../tokenizer.js\";\nimport { ToolRegistry } from \"../tools.js\";\nimport type { JSONSchema } from \"../types.js\";\nimport type { McpClient } from \"./client.js\";\nimport { LatencyTracker, type SlowEvent } from \"./latency.js\";\nimport type { CallToolResult, McpContentBlock } from \"./types.js\";\n\nexport interface BridgeOptions {\n /** Prefix for tool names — disambiguates collisions when bridging multiple servers. */\n namePrefix?: string;\n /** Registry to populate. Creates a fresh one if omitted. */\n registry?: ToolRegistry;\n /** Auto-flatten deep schemas (Pillar 3). Defaults to the registry's own default (true). */\n autoFlatten?: boolean;\n /** Cap on tool result chars; head+tail truncation. Floor against context-poisoning oversized reads. */\n maxResultChars?: number;\n /** Absent → no `_meta.progressToken` sent and server won't emit progress. */\n onProgress?: (info: {\n toolName: string;\n progress: number;\n total?: number;\n message?: string;\n }) => void;\n /** Server name used to tag latency samples + slow events. Falls through to namePrefix without trailing `_`. */\n serverName?: string;\n /** p95 cutoff in ms before a slow event fires — defaults to 4000. */\n slowThresholdMs?: number;\n /** Fired exactly when the per-server p95 transitions over `slowThresholdMs`. */\n onSlow?: (ev: SlowEvent) => void;\n /** Indirection so reconnect can swap the underlying client without re-registering tools. */\n host?: McpClientHost;\n /** Awaited before each `callTool` — resolves on `connected`, rejects on `failed`, caps via `readyTimeoutMs`. */\n ready?: Promise<void>;\n /** How long to wait on `ready` before failing the dispatch. Default 30_000ms. */\n readyTimeoutMs?: number;\n}\n\n/** Mutable holder so `/mcp reconnect` can swap the underlying client without re-bridging tools. */\nexport interface McpClientHost {\n client: McpClient;\n}\n\nexport const DEFAULT_MAX_RESULT_CHARS = 32_000;\n\n/** ~6% of DeepSeek V3 context. Char cap alone fails on CJK (~1 char/token). */\nexport const DEFAULT_MAX_RESULT_TOKENS = 8_000;\n\n/** Default per-call wait before failing if the server is still handshaking. */\nexport const DEFAULT_READY_TIMEOUT_MS = 30_000;\n\nexport interface BridgeResult {\n registry: ToolRegistry;\n /** Names actually registered (may differ from MCP names when a prefix is applied). */\n registeredNames: string[];\n /** Names the server listed but the bridge skipped (e.g. invalid schemas). */\n skipped: Array<{ name: string; reason: string }>;\n}\n\n/** Resolved bridge environment that `registerSingleMcpTool` needs. Stored on summaries so reconnect can append new tools later. */\nexport interface BridgeEnv {\n registry: ToolRegistry;\n host: McpClientHost;\n prefix: string;\n maxResultChars: number;\n tracker: LatencyTracker | null;\n onProgress?: BridgeOptions[\"onProgress\"];\n /** Optional readiness gate awaited before each `callTool` dispatch. */\n ready?: Promise<void>;\n /** Timeout for waiting on `ready` — milliseconds. Defaults to DEFAULT_READY_TIMEOUT_MS. */\n readyTimeoutMs?: number;\n /** Server name surfaced in timeout errors. Defaults to the prefix or \"anon\". */\n serverName?: string;\n}\n\n/** Register one MCP tool's bridged closure into the registry. Returns the registered name (or \"\" if skipped). */\nexport function registerSingleMcpTool(\n mcpTool: import(\"./types.js\").McpTool,\n env: BridgeEnv,\n): string {\n if (!mcpTool.name) return \"\";\n const registeredName = `${env.prefix}${mcpTool.name}`;\n env.registry.register({\n name: registeredName,\n description: mcpTool.description ?? \"\",\n parameters: mcpTool.inputSchema as JSONSchema,\n fn: async (args: Record<string, unknown>, ctx) => {\n if (env.ready) {\n await waitForReady(\n env.ready,\n env.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,\n env.serverName ?? (env.prefix.replace(/_$/, \"\") || \"anon\"),\n ctx?.signal,\n );\n }\n const t0 = env.tracker ? Date.now() : 0;\n // Resolve client at call time via the host indirection so `/mcp reconnect`\n // can swap a fresh client in without re-bridging tools.\n const live = env.host.client;\n const toolResult = await live.callTool(mcpTool.name, args, {\n onProgress: env.onProgress\n ? (info) => env.onProgress!({ toolName: registeredName, ...info })\n : undefined,\n signal: ctx?.signal,\n });\n if (env.tracker) env.tracker.record(Date.now() - t0);\n return flattenMcpResult(toolResult, { maxChars: env.maxResultChars });\n },\n });\n return registeredName;\n}\n\nasync function waitForReady(\n ready: Promise<void>,\n timeoutMs: number,\n serverName: string,\n signal: AbortSignal | undefined,\n): Promise<void> {\n let settled = false;\n let timer: NodeJS.Timeout | undefined;\n let onAbort: (() => void) | undefined;\n try {\n await new Promise<void>((resolve, reject) => {\n ready.then(\n () => {\n if (settled) return;\n settled = true;\n resolve();\n },\n (err) => {\n if (settled) return;\n settled = true;\n reject(err instanceof Error ? err : new Error(String(err)));\n },\n );\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n reject(\n new Error(\n `MCP server \"${serverName}\" still handshaking after ${timeoutMs}ms — try /mcp reconnect or check the server logs.`,\n ),\n );\n }, timeoutMs);\n }\n if (signal) {\n if (signal.aborted) {\n if (settled) return;\n settled = true;\n reject(new Error(\"aborted\"));\n return;\n }\n onAbort = () => {\n if (settled) return;\n settled = true;\n reject(new Error(\"aborted\"));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n });\n } finally {\n if (timer) clearTimeout(timer);\n if (signal && onAbort) signal.removeEventListener(\"abort\", onAbort);\n }\n}\n\nexport async function bridgeMcpTools(\n client: McpClient,\n opts: BridgeOptions = {},\n): Promise<BridgeResult & { env: BridgeEnv }> {\n const registry = opts.registry ?? new ToolRegistry({ autoFlatten: opts.autoFlatten });\n const prefix = opts.namePrefix ?? \"\";\n const maxResultChars = opts.maxResultChars ?? DEFAULT_MAX_RESULT_CHARS;\n const result: BridgeResult = { registry, registeredNames: [], skipped: [] };\n\n const serverName = opts.serverName ?? prefix.replace(/_$/, \"\") ?? \"anon\";\n const tracker = opts.onSlow\n ? new LatencyTracker(serverName, { thresholdMs: opts.slowThresholdMs, onSlow: opts.onSlow })\n : null;\n // Synthesize a host on the fly when the caller didn't provide one. Older\n // callers (tests, single-shot non-reconnectable bridges) get the live\n // `client` reference frozen in; reconnect-aware callers pass their own\n // mutable host.\n const host: McpClientHost = opts.host ?? { client };\n const env: BridgeEnv = {\n registry,\n host,\n prefix,\n maxResultChars,\n tracker,\n onProgress: opts.onProgress,\n ready: opts.ready,\n readyTimeoutMs: opts.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,\n serverName,\n };\n const listed = await client.listTools();\n for (const mcpTool of listed.tools) {\n if (!mcpTool.name) {\n result.skipped.push({ name: \"?\", reason: \"empty tool name\" });\n continue;\n }\n const registeredName = registerSingleMcpTool(mcpTool, env);\n if (registeredName) result.registeredNames.push(registeredName);\n }\n return { ...result, env };\n}\n\nexport interface FlattenOptions {\n /** Cap the flattened string at this many characters. Default: no cap. */\n maxChars?: number;\n}\n\nexport function flattenMcpResult(result: CallToolResult, opts: FlattenOptions = {}): string {\n const parts = result.content.map(blockToString);\n const joined = parts.join(\"\\n\").trim();\n const prefixed = result.isError ? `ERROR: ${joined || \"(no error message from server)\"}` : joined;\n return opts.maxChars ? truncateForModel(prefixed, opts.maxChars) : prefixed;\n}\n\n/** Head + 1KB tail so error messages at end of stack traces aren't lost. */\nexport function truncateForModel(s: string, maxChars: number): string {\n if (s.length <= maxChars) return s;\n const tailBudget = Math.min(1024, Math.floor(maxChars * 0.1));\n const headBudget = Math.max(0, maxChars - tailBudget);\n const head = s.slice(0, headBudget);\n const tail = s.slice(-tailBudget);\n const dropped = s.length - head.length - tail.length;\n return `${head}\\n\\n[…truncated ${dropped} chars — raise BridgeOptions.maxResultChars, or call the tool with a narrower scope (filter, head, pagination)…]\\n\\n${tail}`;\n}\n\n/** Never tokenizes full input — pathological repetitive text (`AAAA…`) costs 30s+ on the pure-TS BPE port. */\nexport function truncateForModelByTokens(s: string, maxTokens: number): string {\n if (maxTokens <= 0) return \"\";\n // Every token is ≥1 char — if length ≤ budget, tokens ≤ budget.\n if (s.length <= maxTokens) return s;\n // Small enough to tokenize-check without pathological cost: confirm\n // whether we're actually over budget. (Threshold is the char-bound\n // worst case for English/code — ~4 chars/token.)\n if (s.length <= maxTokens * 4) {\n const tokens = countTokens(s);\n if (tokens <= maxTokens) return s;\n }\n\n const markerOverhead = 48; // rough token cost of the truncation marker\n const contentBudget = Math.max(0, maxTokens - markerOverhead);\n const tailBudget = Math.min(256, Math.floor(contentBudget * 0.1));\n const headBudget = Math.max(0, contentBudget - tailBudget);\n\n const head = sizePrefixToTokens(s, headBudget);\n const tail = sizeSuffixToTokens(s, tailBudget);\n const droppedChars = s.length - head.length - tail.length;\n // Estimate dropped tokens from the per-slice char/token ratio we\n // already measured, rather than paying another full-string tokenize.\n // The marker says \"~N tokens\" so the ≤10% slop is visible to readers.\n const headTokens = head ? countTokens(head) : 0;\n const tailTokens = tail ? countTokens(tail) : 0;\n const sampleChars = head.length + tail.length;\n const sampleTokens = headTokens + tailTokens;\n const ratio = sampleChars > 0 ? sampleTokens / sampleChars : 0.3;\n const estTotalTokens = Math.ceil(s.length * ratio);\n const droppedTokens = Math.max(0, estTotalTokens - sampleTokens);\n return `${head}\\n\\n[…truncated ~${droppedTokens} tokens (${droppedChars} chars) — raise BridgeOptions.maxResultTokens, or call the tool with a narrower scope (filter, head, pagination)…]\\n\\n${tail}`;\n}\n\nfunction sizePrefixToTokens(s: string, budget: number): string {\n if (budget <= 0 || s.length === 0) return \"\";\n // Optimistic starting size: assume ~4 chars/token (English/code\n // average). If the content is denser (CJK ~1 char/token), the first\n // tokenize will show we're over and we shrink.\n let size = Math.min(s.length, budget * 4);\n for (let iter = 0; iter < 6; iter++) {\n if (size <= 0) return \"\";\n const slice = s.slice(0, size);\n const count = countTokens(slice);\n if (count <= budget) return slice;\n // Shrink by the overshoot fraction plus a small safety margin.\n const next = Math.floor(size * (budget / count) * 0.95);\n if (next >= size) return s.slice(0, Math.max(0, size - 1));\n size = next;\n }\n return s.slice(0, Math.max(0, size));\n}\n\n/** Slice `s` from the end to the largest suffix that fits `budget` tokens. */\nfunction sizeSuffixToTokens(s: string, budget: number): string {\n if (budget <= 0 || s.length === 0) return \"\";\n let size = Math.min(s.length, budget * 4);\n for (let iter = 0; iter < 6; iter++) {\n if (size <= 0) return \"\";\n const slice = s.slice(-size);\n const count = countTokens(slice);\n if (count <= budget) return slice;\n const next = Math.floor(size * (budget / count) * 0.95);\n if (next >= size) return s.slice(-Math.max(0, size - 1));\n size = next;\n }\n return s.slice(-Math.max(0, size));\n}\n\nfunction blockToString(block: McpContentBlock): string {\n if (block.type === \"text\") return block.text;\n if (block.type === \"image\") return `[image ${block.mimeType}, ${block.data.length} chars base64]`;\n // Unknown block type — preserve for diagnostics.\n return `[unknown block: ${JSON.stringify(block)}]`;\n}\n","/** JSONL append-only message log under `~/.luckerr/sessions/`; concurrent-write safe. */\n\nimport { execFileSync } from \"node:child_process\";\nimport {\n appendFileSync,\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { ChatMessage } from \"../types.js\";\n\n/** Best-effort git branch sniff; returns undefined if not a git repo or git missing. */\nexport function detectGitBranch(cwd: string): string | undefined {\n try {\n const out = execFileSync(\"git\", [\"branch\", \"--show-current\"], {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 800,\n encoding: \"utf8\",\n }).trim();\n return out || undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface SessionInfo {\n name: string;\n path: string;\n size: number;\n messageCount: number;\n mtime: Date;\n meta: SessionMeta;\n}\n\nexport interface SessionMeta {\n branch?: string;\n summary?: string;\n totalCostUsd?: number;\n turnCount?: number;\n /** Absolute path of the workspace root the session was created/used in. */\n workspace?: string;\n /** Wallet currency at last save — used to format `totalCostUsd` in the picker without re-fetching balance. */\n balanceCurrency?: string;\n /** Cumulative cache hit / miss tokens across the session — survives resume so /status cache% isn't 0 on a fresh boot. */\n cacheHitTokens?: number;\n cacheMissTokens?: number;\n /** Last turn's promptTokens — lets /status render the context bar before the next turn fires. */\n lastPromptTokens?: number;\n}\n\nexport function sessionsDir(): string {\n return join(homedir(), \".luckerr\", \"sessions\");\n}\n\nexport function sessionPath(name: string): string {\n return join(sessionsDir(), `${sanitizeName(name)}.jsonl`);\n}\n\nexport function sanitizeName(name: string): string {\n const cleaned = name.replace(/[^\\w\\-\\u4e00-\\u9fa5]/g, \"_\").slice(0, 64);\n return cleaned || \"default\";\n}\n\n/** Sortable timestamp `YYYYMMDDHHmm` — used as a session-name suffix. */\nexport function timestampSuffix(): string {\n return new Date().toISOString().replace(/[^\\d]/g, \"\").slice(0, 12);\n}\n\n/** Unique name for an in-app \"new session\" — strips a trailing 12/14-digit timestamp from the current name and re-stamps with seconds precision so back-to-back clicks don't collide. */\nexport function freshSessionName(currentName: string | undefined): string {\n const base = currentName ? currentName.replace(/-\\d{12,14}$/, \"\") : \"default\";\n const stamp = new Date().toISOString().replace(/[^\\d]/g, \"\").slice(0, 14);\n return `${base || \"default\"}-${stamp}`;\n}\n\n/** Names of `.jsonl` sessions starting with `prefix`, newest-first by filename. */\nexport function findSessionsByPrefix(prefix: string): string[] {\n const dir = sessionsDir();\n if (!existsSync(dir)) return [];\n try {\n const files = readdirSync(dir)\n .filter((f) => f.endsWith(\".jsonl\") && !f.endsWith(\".events.jsonl\") && f.startsWith(prefix))\n .sort()\n .reverse();\n return files.map((f) => f.replace(/\\.jsonl$/, \"\"));\n } catch {\n return [];\n }\n}\n\nexport interface SessionPreview {\n messageCount: number;\n lastActive: Date;\n}\n\n/** Resolve launch-time session: forceNew → timestamped suffix; else latest `${name}-*` if any, else base. Preview returned only on the default branch when messages exist. */\nexport function resolveSession(\n sessionName: string | undefined,\n forceNew?: boolean,\n forceResume?: boolean,\n): { resolved: string | undefined; preview: SessionPreview | undefined } {\n let resolved = sessionName;\n let preview: SessionPreview | undefined;\n\n if (sessionName && forceNew) {\n resolved = `${sessionName}-${timestampSuffix()}`;\n } else if (sessionName && !forceResume) {\n let sessionToCheck = sessionName;\n const prefixed = findSessionsByPrefix(`${sessionName}-`);\n if (prefixed.length > 0) {\n sessionToCheck = prefixed[0]!;\n }\n const prior = loadSessionMessages(sessionToCheck);\n if (prior.length > 0) {\n resolved = sessionToCheck;\n const p = sessionPath(sessionToCheck);\n const mtime = existsSync(p) ? statSync(p).mtime : new Date();\n preview = { messageCount: prior.length, lastActive: mtime };\n }\n } else if (sessionName && forceResume) {\n const prefixed = findSessionsByPrefix(`${sessionName}-`);\n if (prefixed.length > 0) {\n resolved = prefixed[0]!;\n }\n }\n\n return { resolved, preview };\n}\n\nexport function loadSessionMessages(name: string): ChatMessage[] {\n const path = sessionPath(name);\n if (!existsSync(path)) return [];\n try {\n const raw = readFileSync(path, \"utf8\");\n const out: ChatMessage[] = [];\n for (const line of raw.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const msg = JSON.parse(trimmed) as ChatMessage;\n if (msg && typeof msg === \"object\" && \"role\" in msg) out.push(msg);\n } catch {\n /* skip malformed line */\n }\n }\n return out;\n } catch {\n return [];\n }\n}\n\nexport function appendSessionMessage(name: string, message: ChatMessage): void {\n const path = sessionPath(name);\n mkdirSync(dirname(path), { recursive: true });\n appendFileSync(path, `${JSON.stringify(message)}\\n`, \"utf8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n /* chmod not supported on this platform */\n }\n}\n\nexport function listSessions(): SessionInfo[] {\n const dir = sessionsDir();\n if (!existsSync(dir)) return [];\n try {\n // Exclude `.events.jsonl` sidecars — they share the .jsonl suffix.\n const files = readdirSync(dir).filter(\n (f) => f.endsWith(\".jsonl\") && !f.endsWith(\".events.jsonl\"),\n );\n return files\n .map((file) => {\n const path = join(dir, file);\n const stat = statSync(path);\n const name = file.replace(/\\.jsonl$/, \"\");\n const messageCount = countLines(path);\n return {\n name,\n path,\n size: stat.size,\n messageCount,\n mtime: stat.mtime,\n meta: loadSessionMeta(name),\n };\n })\n .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n } catch {\n return [];\n }\n}\n\n/** Strict match — legacy sessions without meta.workspace are hidden; resume by name still works. */\nexport function listSessionsForWorkspace(workspace: string): SessionInfo[] {\n return listSessions().filter((s) => s.meta.workspace === workspace);\n}\n\nfunction metaPath(name: string): string {\n return join(sessionsDir(), `${sanitizeName(name)}.meta.json`);\n}\n\nexport function loadSessionMeta(name: string): SessionMeta {\n const p = metaPath(name);\n if (!existsSync(p)) return {};\n try {\n const raw = JSON.parse(readFileSync(p, \"utf8\")) as SessionMeta;\n return raw && typeof raw === \"object\" ? raw : {};\n } catch {\n return {};\n }\n}\n\nexport function patchSessionMeta(name: string, patch: Partial<SessionMeta>): SessionMeta {\n const cur = loadSessionMeta(name);\n const next: SessionMeta = { ...cur, ...patch };\n const p = metaPath(name);\n mkdirSync(dirname(p), { recursive: true });\n writeFileSync(p, JSON.stringify(next), \"utf8\");\n try {\n chmodSync(p, 0o600);\n } catch {\n /* chmod not supported */\n }\n return next;\n}\n\n/** Renames the JSONL plus all known sidecars together; returns false if target already exists. */\nexport function renameSession(oldName: string, newName: string): boolean {\n const safeOld = sanitizeName(oldName);\n const safeNew = sanitizeName(newName);\n if (safeOld === safeNew) return false;\n const oldJsonl = sessionPath(oldName);\n const newJsonl = sessionPath(newName);\n if (!existsSync(oldJsonl) || existsSync(newJsonl)) return false;\n renameSync(oldJsonl, newJsonl);\n for (const ext of [\".events.jsonl\", \".meta.json\", \".pending.json\", \".plan.json\"]) {\n const oldP = oldJsonl.replace(/\\.jsonl$/, ext);\n const newP = newJsonl.replace(/\\.jsonl$/, ext);\n if (existsSync(oldP)) {\n try {\n renameSync(oldP, newP);\n } catch {\n /* sidecar rename failed — leave the jsonl rename in place */\n }\n }\n }\n return true;\n}\n\n/** Best-effort: per-file delete errors are swallowed so partial pruning still finishes. */\nexport function pruneStaleSessions(daysOld = 90): string[] {\n const cutoff = Date.now() - daysOld * 24 * 60 * 60 * 1000;\n const deleted: string[] = [];\n for (const s of listSessions()) {\n if (s.mtime.getTime() < cutoff) {\n if (deleteSession(s.name)) deleted.push(s.name);\n }\n }\n return deleted;\n}\n\nexport function deleteSession(name: string): boolean {\n const path = sessionPath(name);\n try {\n unlinkSync(path);\n for (const ext of [\".events.jsonl\", \".pending.json\", \".meta.json\", \".plan.json\"]) {\n const sidecar = path.replace(/\\.jsonl$/, ext);\n try {\n unlinkSync(sidecar);\n } catch {\n /* expected when the sidecar doesn't exist */\n }\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** Non-atomic truncate+write window is acceptable — a concurrent crash leaves the session file empty, same end state as the user deleting it. */\nexport function rewriteSession(name: string, messages: ChatMessage[]): void {\n const path = sessionPath(name);\n mkdirSync(dirname(path), { recursive: true });\n const body = messages.map((m) => JSON.stringify(m)).join(\"\\n\");\n writeFileSync(path, body ? `${body}\\n` : \"\", \"utf8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n /* chmod not supported */\n }\n}\n\n/** Rotate the live jsonl + sidecars to `<name>__archive_<ts>` so /new doesn't destroy history. Returns the archive name, or null if there was nothing to archive. */\nexport function archiveSession(name: string): string | null {\n const path = sessionPath(name);\n if (!existsSync(path)) return null;\n try {\n if (statSync(path).size === 0) return null;\n } catch {\n return null;\n }\n for (let attempt = 0; attempt < 5; attempt++) {\n const target = `${name}__archive_${timestampSuffix()}${attempt > 0 ? `_${attempt}` : \"\"}`;\n if (renameSession(name, target)) return target;\n }\n return null;\n}\n\nfunction countLines(path: string): number {\n try {\n const raw = readFileSync(path, \"utf8\");\n return raw.split(/\\r?\\n/).filter((l) => l.trim()).length;\n } catch {\n return 0;\n }\n}\n","import type { Usage } from \"../client.js\";\nimport { pricingFor, contextTokensFor } from \"../providers/registry.js\";\n\n/**\n * @deprecated Use `pricingFor(model)` from the provider registry instead.\n * Kept for backwards-compat — still resolves through provider system.\n */\nexport function deepseekPricingFor(model: string): {\n inputCacheHit: number;\n inputCacheMiss: number;\n output: number;\n} {\n return pricingFor(model);\n}\n\n/**\n * USD per 1M tokens — DeepSeek-specific pricing table.\n * @deprecated Use `pricingFor(model)` from the provider registry.\n * Kept as a snapshot for backwards-compat importers.\n */\nexport const DEEPSEEK_PRICING: Record<\n string,\n { inputCacheHit: number; inputCacheMiss: number; output: number }\n> = {\n \"deepseek-v4-flash\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n \"deepseek-v4-pro\": { inputCacheHit: 0.139, inputCacheMiss: 1.667, output: 3.333 },\n \"deepseek-chat\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n \"deepseek-reasoner\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n};\n\n/** Reference Claude Sonnet 4.6 pricing (USD per 1M tokens). */\nexport const CLAUDE_SONNET_PRICING = { input: 3.0, output: 15.0 };\n\n/**\n * Prompt-side window only; completion caps live server-side and don't affect\n * this gauge.\n * @deprecated Use `contextTokensFor(model)` from the provider registry.\n */\nexport const DEEPSEEK_CONTEXT_TOKENS: Record<string, number> = {\n \"deepseek-v4-flash\": 1_000_000,\n \"deepseek-v4-pro\": 1_000_000,\n \"deepseek-chat\": 1_000_000,\n \"deepseek-reasoner\": 1_000_000,\n};\n\n/** Fallback when the caller's model id isn't in any provider table. */\nexport const DEFAULT_CONTEXT_TOKENS = 131_072;\n\n/** Context window size for a model, resolved through the provider registry. */\nexport function contextTokensForModel(model: string): number {\n return contextTokensFor(model) || DEFAULT_CONTEXT_TOKENS;\n}\n\nexport function costUsd(model: string, usage: Usage): number {\n const p = pricingFor(model);\n if (!p || (p.inputCacheHit === 0 && p.inputCacheMiss === 0 && p.output === 0)) return 0;\n return (\n (usage.promptCacheHitTokens * p.inputCacheHit +\n usage.promptCacheMissTokens * p.inputCacheMiss +\n usage.completionTokens * p.output) /\n 1_000_000\n );\n}\n\n/** Input-side cost only (prompt, cache hit + miss). Used for the panel breakdown. */\nexport function inputCostUsd(model: string, usage: Usage): number {\n const p = pricingFor(model);\n if (!p || (p.inputCacheHit === 0 && p.inputCacheMiss === 0)) return 0;\n return (\n (usage.promptCacheHitTokens * p.inputCacheHit +\n usage.promptCacheMissTokens * p.inputCacheMiss) /\n 1_000_000\n );\n}\n\n/** Output-side cost only (completion tokens). Used for the panel breakdown. */\nexport function outputCostUsd(model: string, usage: Usage): number {\n const p = pricingFor(model);\n if (!p) return 0;\n return (usage.completionTokens * p.output) / 1_000_000;\n}\n\nexport function cacheSavingsUsd(model: string, hitTokens: number): number {\n if (hitTokens <= 0) return 0;\n const p = pricingFor(model);\n if (!p) return 0;\n return (hitTokens * (p.inputCacheMiss - p.inputCacheHit)) / 1_000_000;\n}\n\nexport function claudeEquivalentCost(usage: Usage): number {\n return (\n (usage.promptTokens * CLAUDE_SONNET_PRICING.input +\n usage.completionTokens * CLAUDE_SONNET_PRICING.output) /\n 1_000_000\n );\n}\n\nexport interface TurnStats {\n turn: number;\n model: string;\n usage: Usage;\n cost: number;\n cacheHitRatio: number;\n}\n\nexport interface SessionSummary {\n turns: number;\n totalCostUsd: number;\n totalInputCostUsd: number;\n /** Output-side (completion) cost aggregated across the session. */\n totalOutputCostUsd: number;\n /** @deprecated Claude reference; kept for benchmarks + replay compat, no longer surfaced in the TUI. */\n claudeEquivalentUsd: number;\n /** @deprecated. Same as claudeEquivalentUsd — synthetic ratio, not a real measurement. */\n savingsVsClaudePct: number;\n cacheHitRatio: number;\n /** Floor estimate for next call — actual cost = this + user delta + new tool outputs. */\n lastPromptTokens: number;\n lastTurnCostUsd: number;\n}\n\nexport class SessionStats {\n readonly turns: TurnStats[] = [];\n /** Cost from prior runs of a resumed session, restored from session meta. */\n private _carryoverCost = 0;\n /** Turn count from prior runs of a resumed session. */\n private _carryoverTurns = 0;\n private _carryoverCacheHit = 0;\n private _carryoverCacheMiss = 0;\n /** Last turn's promptTokens before exit — surfaced via summary() until the next live turn lands. */\n private _carryoverLastPromptTokens = 0;\n\n /** Seed totals from a resumed session's persisted meta — only call once at construction. */\n seedCarryover(opts: {\n totalCostUsd?: number;\n turnCount?: number;\n cacheHitTokens?: number;\n cacheMissTokens?: number;\n lastPromptTokens?: number;\n }): void {\n if (typeof opts.totalCostUsd === \"number\" && opts.totalCostUsd > 0) {\n this._carryoverCost = opts.totalCostUsd;\n }\n if (typeof opts.turnCount === \"number\" && opts.turnCount > 0) {\n this._carryoverTurns = opts.turnCount;\n }\n if (typeof opts.cacheHitTokens === \"number\" && opts.cacheHitTokens > 0) {\n this._carryoverCacheHit = opts.cacheHitTokens;\n }\n if (typeof opts.cacheMissTokens === \"number\" && opts.cacheMissTokens > 0) {\n this._carryoverCacheMiss = opts.cacheMissTokens;\n }\n if (typeof opts.lastPromptTokens === \"number\" && opts.lastPromptTokens > 0) {\n this._carryoverLastPromptTokens = opts.lastPromptTokens;\n }\n }\n\n record(turn: number, model: string, usage: Usage): TurnStats {\n const cost = costUsd(model, usage);\n const stats: TurnStats = {\n turn,\n model,\n usage,\n cost,\n cacheHitRatio: usage.cacheHitRatio,\n };\n this.turns.push(stats);\n return stats;\n }\n\n get totalCost(): number {\n return this._carryoverCost + this.turns.reduce((sum, t) => sum + t.cost, 0);\n }\n\n get totalClaudeEquivalent(): number {\n return this.turns.reduce((sum, t) => sum + claudeEquivalentCost(t.usage), 0);\n }\n\n get savingsVsClaude(): number {\n const c = this.totalClaudeEquivalent;\n return c > 0 ? 1 - this.totalCost / c : 0;\n }\n\n get totalInputCost(): number {\n return this.turns.reduce((sum, t) => sum + inputCostUsd(t.model, t.usage), 0);\n }\n\n get totalOutputCost(): number {\n return this.turns.reduce((sum, t) => sum + outputCostUsd(t.model, t.usage), 0);\n }\n\n get aggregateCacheHitRatio(): number {\n let hit = this._carryoverCacheHit;\n let miss = this._carryoverCacheMiss;\n for (const t of this.turns) {\n hit += t.usage.promptCacheHitTokens;\n miss += t.usage.promptCacheMissTokens;\n }\n const denom = hit + miss;\n return denom > 0 ? hit / denom : 0;\n }\n\n summary(): SessionSummary {\n const last = this.turns[this.turns.length - 1];\n return {\n turns: this.turns.length + this._carryoverTurns,\n totalCostUsd: round(this.totalCost, 6),\n totalInputCostUsd: round(this.totalInputCost, 6),\n totalOutputCostUsd: round(this.totalOutputCost, 6),\n claudeEquivalentUsd: round(this.totalClaudeEquivalent, 6),\n savingsVsClaudePct: round(this.savingsVsClaude * 100, 2),\n cacheHitRatio: round(this.aggregateCacheHitRatio, 4),\n lastPromptTokens: last?.usage.promptTokens ?? this._carryoverLastPromptTokens,\n lastTurnCostUsd: round(last?.cost ?? 0, 6),\n };\n }\n}\n\nfunction round(n: number, digits: number): number {\n const f = 10 ** digits;\n return Math.round(n * f) / f;\n}\n","import type { DeepSeekClient } from \"./client.js\";\nimport { Usage } from \"./client.js\";\nimport { healLoadedMessages } from \"./loop.js\";\nimport { thinkingModeForModel } from \"./loop.js\";\nimport { stripHallucinatedToolMarkup } from \"./loop.js\";\nimport { DEFAULT_MAX_RESULT_CHARS } from \"./mcp/registry.js\";\nimport type { AppendOnlyLog } from \"./memory/runtime.js\";\nimport { rewriteSession } from \"./memory/session.js\";\nimport {\n DEEPSEEK_CONTEXT_TOKENS,\n DEFAULT_CONTEXT_TOKENS,\n type SessionStats,\n} from \"./telemetry/stats.js\";\nimport { estimateConversationTokens, estimateRequestTokens } from \"./tokenizer.js\";\nimport type { ChatMessage } from \"./types.js\";\n\n/** Auto-fold when a turn's response shows promptTokens above this fraction of ctxMax. */\nexport const HISTORY_FOLD_THRESHOLD = 0.5;\n/** Tail budget after a normal fold, as a fraction of ctxMax. */\nexport const HISTORY_FOLD_TAIL_FRACTION = 0.2;\n/** Above this fraction the normal fold's tail budget didn't buy enough headroom — fold harder. */\nexport const HISTORY_FOLD_AGGRESSIVE_THRESHOLD = 0.7;\n/** Tail budget after an aggressive fold — half the normal one, sacrifices recent context for headroom. */\nexport const HISTORY_FOLD_AGGRESSIVE_TAIL_FRACTION = 0.1;\n/** Skip the fold if the head wouldn't shrink the log by at least this fraction. */\nexport const HISTORY_FOLD_MIN_SAVINGS_FRACTION = 0.3;\n/** Above this fraction we exit the turn with a summary instead of folding (defense in depth). */\nexport const FORCE_SUMMARY_THRESHOLD = 0.8;\n/** Local preflight estimate above this fraction trips the emergency in-place compact path. */\nexport const PREFLIGHT_EMERGENCY_THRESHOLD = 0.95;\n/** Prepended to fold summary content so the model knows it's a synthesized recap. */\nexport const HISTORY_FOLD_MARKER =\n \"[CONVERSATION HISTORY SUMMARY — earlier turns folded for context efficiency]\\n\\n\";\n\nexport interface ContextManagerDeps {\n client: DeepSeekClient;\n log: AppendOnlyLog;\n stats: SessionStats;\n sessionName: string | null;\n getAbortSignal: () => AbortSignal;\n getCurrentTurn: () => number;\n}\n\nexport type PostUsageDecisionKind = \"none\" | \"fold\" | \"exit-with-summary\";\n\nexport interface PostUsageDecision {\n kind: PostUsageDecisionKind;\n promptTokens: number;\n ctxMax: number;\n ratio: number;\n /** Token budget for the recent tail when kind === \"fold\"; smaller in the aggressive band. */\n tailBudget?: number;\n /** True when this fold is in the 70-85% band — used in user-facing messaging. */\n aggressive?: boolean;\n}\n\nexport interface PreflightDecision {\n needsAction: boolean;\n estimateTokens: number;\n ctxMax: number;\n}\n\nexport interface FoldResult {\n folded: boolean;\n beforeMessages: number;\n afterMessages: number;\n summaryChars: number;\n}\n\nexport class ContextManager {\n constructor(private deps: ContextManagerDeps) {}\n\n /** Decision after a turn's response — fold, exit with summary, or carry on. */\n decideAfterUsage(\n usage: Usage | null,\n model: string,\n alreadyFoldedThisTurn: boolean,\n ): PostUsageDecision {\n const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;\n if (!usage) return { kind: \"none\", promptTokens: 0, ctxMax, ratio: 0 };\n const ratio = usage.promptTokens / ctxMax;\n const base = { promptTokens: usage.promptTokens, ctxMax, ratio };\n if (ratio > FORCE_SUMMARY_THRESHOLD) {\n return { kind: \"exit-with-summary\", ...base };\n }\n if (alreadyFoldedThisTurn) return { kind: \"none\", ...base };\n if (ratio > HISTORY_FOLD_AGGRESSIVE_THRESHOLD) {\n return {\n kind: \"fold\",\n ...base,\n tailBudget: Math.floor(ctxMax * HISTORY_FOLD_AGGRESSIVE_TAIL_FRACTION),\n aggressive: true,\n };\n }\n if (ratio > HISTORY_FOLD_THRESHOLD) {\n return {\n kind: \"fold\",\n ...base,\n tailBudget: Math.floor(ctxMax * HISTORY_FOLD_TAIL_FRACTION),\n aggressive: false,\n };\n }\n return { kind: \"none\", ...base };\n }\n\n /** Local-side preflight before sending a request — catches oversized payloads early. */\n decidePreflight(\n messages: ChatMessage[],\n toolSpecs: ReadonlyArray<unknown> | undefined | null,\n model: string,\n ): PreflightDecision {\n const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;\n const estimate = estimateRequestTokens(messages, toolSpecs ?? null);\n return {\n needsAction: estimate / ctxMax > PREFLIGHT_EMERGENCY_THRESHOLD,\n estimateTokens: estimate,\n ctxMax,\n };\n }\n\n /** Replace older turns with one summary message; keep tail within keepRecentTokens budget. */\n async fold(model: string, opts?: { keepRecentTokens?: number }): Promise<FoldResult> {\n const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;\n const tailBudget = opts?.keepRecentTokens ?? Math.floor(ctxMax * HISTORY_FOLD_TAIL_FRACTION);\n const all = this.deps.log.toMessages();\n const noop: FoldResult = {\n folded: false,\n beforeMessages: all.length,\n afterMessages: all.length,\n summaryChars: 0,\n };\n if (all.length === 0) return noop;\n\n const tokenCounts = all.map((m) => estimateConversationTokens([m]));\n const totalTokens = tokenCounts.reduce((a, b) => a + b, 0);\n\n let cumTokens = 0;\n let boundary = all.length;\n for (let i = all.length - 1; i >= 0; i--) {\n if (cumTokens + tokenCounts[i]! > tailBudget) break;\n cumTokens += tokenCounts[i]!;\n if (all[i]!.role === \"user\") boundary = i;\n }\n if (boundary <= 0) return noop;\n\n const head = all.slice(0, boundary);\n const tail = all.slice(boundary);\n const headTokens = totalTokens - cumTokens;\n if (headTokens < totalTokens * HISTORY_FOLD_MIN_SAVINGS_FRACTION) return noop;\n\n const summary = await this.summarizeForFold(head);\n if (!summary) return noop;\n\n const summaryMsg: ChatMessage = {\n role: \"assistant\",\n content: HISTORY_FOLD_MARKER + summary,\n };\n const replacement = [summaryMsg, ...tail];\n this.deps.log.compactInPlace(replacement);\n this.persistRewrite(replacement);\n return {\n folded: true,\n beforeMessages: all.length,\n afterMessages: replacement.length,\n summaryChars: summary.length,\n };\n }\n\n /** Drop a trailing in-flight assistant-with-tool_calls before a forced summary. Tail-only mutation; prefix cache safe. */\n trimTrailingToolCalls(): boolean {\n const tail = this.deps.log.entries[this.deps.log.entries.length - 1];\n if (\n !tail ||\n tail.role !== \"assistant\" ||\n !Array.isArray(tail.tool_calls) ||\n tail.tool_calls.length === 0\n ) {\n return false;\n }\n const kept = this.deps.log.entries.slice(0, -1);\n this.deps.log.compactInPlace([...kept]);\n this.persistRewrite([...kept]);\n return true;\n }\n\n private async summarizeForFold(messagesToSummarize: ChatMessage[]): Promise<string> {\n const summaryModel = \"deepseek-v4-flash\";\n const systemPrompt =\n \"You compress conversation history for a coding agent. Output one prose recap that preserves: the user's overall goal, decisions and conclusions reached, files inspected or modified, important tool results still relevant to ongoing work, and any open todos. Skip turn-by-turn play-by-play. No tool calls, no markdown headings, no SEARCH/REPLACE blocks — plain prose only.\";\n const healed = healLoadedMessages(messagesToSummarize, DEFAULT_MAX_RESULT_CHARS).messages;\n const messages: ChatMessage[] = [\n { role: \"system\", content: systemPrompt },\n ...healed,\n {\n role: \"user\",\n content:\n \"Summarize the conversation above as plain prose. This summary replaces the original turns to free context — make it self-contained.\",\n },\n ];\n try {\n const resp = await this.deps.client.chat({\n model: summaryModel,\n messages,\n signal: this.deps.getAbortSignal(),\n thinking: thinkingModeForModel(summaryModel),\n reasoningEffort: \"high\",\n });\n this.deps.stats.record(this.deps.getCurrentTurn(), summaryModel, resp.usage ?? new Usage());\n return stripHallucinatedToolMarkup((resp.content ?? \"\").trim());\n } catch {\n return \"\";\n }\n }\n\n private persistRewrite(messages: ChatMessage[]): void {\n if (!this.deps.sessionName) return;\n try {\n rewriteSession(this.deps.sessionName, messages);\n } catch {\n /* disk full / perms — in-memory mutation still applies */\n }\n }\n}\n","/** Authoritative running-id set — cards derive `running` from `has(id)` instead of trusting end-event delivery. Loop adds on dispatch entry, deletes in `finally` so every exit path cleans up. */\n\nexport type InflightSubscriber = () => void;\n\nexport class InflightSet {\n private readonly _set = new Set<string>();\n private readonly _listeners = new Set<InflightSubscriber>();\n\n add(id: string): void {\n if (this._set.has(id)) return;\n this._set.add(id);\n this._notify();\n }\n\n delete(id: string): void {\n if (this._set.delete(id)) this._notify();\n }\n\n has(id: string): boolean {\n return this._set.has(id);\n }\n\n /** Snapshot for diagnostics / tests; live view, do not mutate. */\n get size(): number {\n return this._set.size;\n }\n\n /** Subscribe to add/delete; returns the unsubscribe function. */\n subscribe(fn: InflightSubscriber): () => void {\n this._listeners.add(fn);\n return () => {\n this._listeners.delete(fn);\n };\n }\n\n /** Drop everything — only use at session reset. Notifies once. */\n clear(): void {\n if (this._set.size === 0) return;\n this._set.clear();\n this._notify();\n }\n\n private _notify(): void {\n for (const fn of this._listeners) {\n try {\n fn();\n } catch {\n /* listener errors must not break the gate */\n }\n }\n }\n}\n","import type { OpenAICompatClient } from \"../client.js\";\nimport { t } from \"../i18n/index.js\";\n\nexport interface ProviderProbeResult {\n reachable: boolean;\n}\n\n/**\n * Format an API error for display to the user.\n *\n * Error messages from the client now use `ProviderLabel STATUS: body`\n * format (e.g. \"DeepSeek 401: ...\" or \"SiliconFlow 503: ...\").\n * `ChatRequestOptions` doesn't carry a provider label so we match\n * the general `\"<Label> NNN:\"` pattern.\n */\nexport function formatLoopError(err: Error, probe?: ProviderProbeResult): string {\n const msg = err.message ?? \"\";\n if (msg.includes(\"maximum context length\")) {\n const reqMatch = msg.match(/requested\\s+(\\d+)\\s+tokens/);\n const requested = reqMatch\n ? `${Number(reqMatch[1]).toLocaleString()} tokens`\n : t(\"errors.contextOverflowTooMany\");\n return t(\"errors.contextOverflow\", { requested });\n }\n\n // Match \"<ProviderLabel> NNN: body\" or legacy \"DeepSeek NNN: body\".\n const m = /^(.+?)\\s+(\\d{3}):\\s*([\\s\\S]*)$/.exec(msg);\n if (!m) return msg;\n const provider = m[1] ?? \"\";\n const status = m[2] ?? \"\";\n const body = m[3] ?? \"\";\n const inner = extractErrorMessage(body);\n\n if (status === \"401\") return t(\"errors.auth401\", { inner });\n if (status === \"402\") return t(\"errors.balance402\", { inner });\n if (status === \"422\") return t(\"errors.badparam422\", { inner });\n if (status === \"400\") return t(\"errors.badrequest400\", { inner });\n if (is5xxStatus(status)) return format5xx(provider, status, probe);\n return msg;\n}\n\nexport function is5xxError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n // Match \"ProviderLabel 5NN: ...\" or legacy \"DeepSeek 5NN: ...\".\n const m = /^.+?\\s+(5\\d{2}):/.exec(err.message ?? \"\");\n return m !== null;\n}\n\nexport async function probeProviderReachable(\n client: OpenAICompatClient,\n timeoutMs = 1500,\n): Promise<ProviderProbeResult> {\n const balance = await client.getBalance({ signal: AbortSignal.timeout(timeoutMs) });\n return { reachable: balance !== null };\n}\n\n/**\n * @deprecated Use `probeProviderReachable` instead.\n * Kept for backwards-compat with any code referencing the old name.\n */\nexport async function probeDeepSeekReachable(\n client: OpenAICompatClient,\n timeoutMs = 1500,\n): Promise<ProviderProbeResult> {\n return probeProviderReachable(client, timeoutMs);\n}\n\nfunction is5xxStatus(status: string): boolean {\n return status === \"500\" || status === \"502\" || status === \"503\" || status === \"504\";\n}\n\nfunction format5xx(\n provider: string,\n status: string,\n probe?: ProviderProbeResult,\n): string {\n const head = t(\"errors.deepseek5xxHead\", { status });\n const probeNote =\n probe === undefined\n ? \"\"\n : probe.reachable\n ? t(\"errors.deepseek5xxReachable\")\n : t(\"errors.deepseek5xxUnreachable\");\n const action =\n probe?.reachable === false\n ? t(\"errors.deepseek5xxActionNetwork\")\n : t(\"errors.deepseek5xxActionRetry\");\n return `${head}${probeNote}${action}`;\n}\n\nexport function reasonPrefixFor(\n reason: \"budget\" | \"aborted\" | \"context-guard\" | \"stuck\",\n iterCap: number,\n): string {\n if (reason === \"aborted\") return t(\"errors.reasonAborted\");\n if (reason === \"context-guard\") return t(\"errors.reasonContextGuard\");\n if (reason === \"stuck\") return t(\"errors.reasonStuck\");\n return t(\"errors.reasonBudget\", { iterCap });\n}\n\nexport function errorLabelFor(\n reason: \"budget\" | \"aborted\" | \"context-guard\" | \"stuck\",\n iterCap: number,\n): string {\n if (reason === \"aborted\") return t(\"errors.labelAborted\");\n if (reason === \"context-guard\") return t(\"errors.labelContextGuard\");\n if (reason === \"stuck\") return t(\"errors.labelStuck\");\n return t(\"errors.labelBudget\", { iterCap });\n}\n\nfunction extractErrorMessage(body: string): string {\n const trimmed = body.trim();\n if (!trimmed) return t(\"errors.innerNoMessage\");\n try {\n const parsed = JSON.parse(trimmed);\n if (parsed && typeof parsed === \"object\") {\n const obj = parsed as { error?: { message?: unknown }; message?: unknown };\n if (obj.error && typeof obj.error.message === \"string\") return obj.error.message;\n if (typeof obj.message === \"string\") return obj.message;\n }\n } catch {\n /* not JSON — fall through */\n }\n return trimmed;\n}\n","/** Accepts `<<<NEEDS_PRO>>>` or `<<<NEEDS_PRO: reason>>>` (reason trimmed, may be empty). */\nconst NEEDS_PRO_MARKER_PREFIX = \"<<<NEEDS_PRO\";\nconst NEEDS_PRO_MARKER_RE = /^<<<NEEDS_PRO(?::\\s*([^>]*))?>>>/;\n/** Buffer cap before flushing — must fit `<<<NEEDS_PRO: reason>>>` without premature flush. */\nexport const NEEDS_PRO_BUFFER_CHARS = 256;\n\n/** Anchored to lead — mid-text matches are normal content (user asking about the marker). */\nexport function parseEscalationMarker(content: string): { matched: boolean; reason?: string } {\n const m = NEEDS_PRO_MARKER_RE.exec(content.trimStart());\n if (!m) return { matched: false };\n const reason = m[1]?.trim();\n return { matched: true, reason: reason || undefined };\n}\n\n/** Convenience boolean — same gate the streaming path used to call. */\nexport function isEscalationRequest(content: string): boolean {\n return parseEscalationMarker(content).matched;\n}\n\n/** Drives streaming flush — while plausibly partial, keep accumulating; else flush. */\nexport function looksLikePartialEscalationMarker(buf: string): boolean {\n const t = buf.trimStart();\n if (t.length === 0) return true;\n if (t.length <= NEEDS_PRO_MARKER_PREFIX.length) {\n return NEEDS_PRO_MARKER_PREFIX.startsWith(t);\n }\n if (!t.startsWith(NEEDS_PRO_MARKER_PREFIX)) return false;\n const rest = t.slice(NEEDS_PRO_MARKER_PREFIX.length);\n if (rest[0] !== \">\" && rest[0] !== \":\") return false;\n return true;\n}\n","/** Thinking / reasoning mode helpers — provider-aware. Each provider exposes reasoning differently; this centralises detection logic. */\n\nimport { resolveProvider, defaultProvider } from \"../providers/registry.js\";\nimport type { ProviderProfile } from \"../providers/types.js\";\n\n/**\n * True when the model emits `reasoning_content` and requires it\n * round-tripped on follow-up turns.\n */\nexport function isThinkingModeModel(model: string, provider?: ProviderProfile): boolean {\n const p = provider ?? resolveProvider(model) ?? defaultProvider();\n if (p.thinking.thinkingModels) {\n return p.thinking.thinkingModels.includes(model);\n }\n // Fallback heuristics for unknown models on known providers.\n if (p.id === \"deepseek\") {\n if (model.includes(\"reasoner\")) return true;\n if (model === \"deepseek-v4-flash\" || model === \"deepseek-v4-pro\") return true;\n return false;\n }\n if (p.id === \"openai\") {\n return model.startsWith(\"o\") || model === \"gpt-5\";\n }\n return false;\n}\n\n/**\n * Returns the thinking toggle value the provider expects.\n *\n * - `\"enabled\"` / `\"disabled\"` — for extra_body and include transports\n * - `undefined` — let the reasoningEffort field handle it (OpenAI-style)\n * or the provider doesn't support thinking at all.\n */\nexport function thinkingModeForModel(\n model: string,\n provider?: ProviderProfile,\n): \"enabled\" | \"disabled\" | undefined {\n const p = provider ?? resolveProvider(model) ?? defaultProvider();\n\n if (p.thinking.transport === \"extra_body\") {\n // DeepSeek-style: extra_body.thinking.type\n // Explicitly disabled models (deepseek-chat — needs thinking=disabled).\n if (model === \"deepseek-chat\") return \"disabled\";\n // Check the thinkingModels list first.\n if (p.thinking.thinkingModels?.includes(model)) return \"enabled\";\n // Fallback heuristics for DeepSeek models not in the explicit list.\n if (model.includes(\"reasoner\")) return \"enabled\";\n return undefined;\n }\n\n if (p.thinking.transport === \"reasoning_effort\") {\n // OpenAI-style: doesn't use the thinking toggle — reasoning_effort\n // is handled separately. Only set reasoning_effort for thinking models.\n if (p.thinking.thinkingModels && !p.thinking.thinkingModels.includes(model)) {\n return undefined;\n }\n return undefined; // reasoning_effort is always handled in buildPayload\n }\n\n if (p.thinking.transport === \"include\") {\n // Anthropic-style: thinking.type in the request body\n if (p.thinking.defaultEnabled && isThinkingModeModel(model, p)) {\n return \"enabled\";\n }\n return undefined;\n }\n\n return undefined;\n}\n\n/** Strip hallucinated tool-call envelopes — `tools: undefined` doesn't always force prose. */\nexport function stripHallucinatedToolMarkup(s: string): string {\n let out = s;\n // DeepSeek's DSML envelope (full-width \"|\" is the form R1 emits in practice).\n out = out.replace(/<|DSML|function_calls>[\\s\\S]*?<\\/?|DSML|function_calls>/g, \"\");\n out = out.replace(/<\\|DSML\\|function_calls>[\\s\\S]*?<\\/?\\|DSML\\|function_calls>/g, \"\");\n out = out.replace(/<function_calls>[\\s\\S]*?<\\/function_calls>/g, \"\");\n // Lone unpaired DSML opener left over after R1 truncates mid-call.\n out = out.replace(/<|DSML|[\\s\\S]*$/g, \"\");\n return out.trim();\n}\n","import type { ChatMessage, ToolCall } from \"../types.js\";\nimport { isThinkingModeModel } from \"./thinking.js\";\n\n/** Thinking-mode producer ⇒ reasoning_content MUST be set (even \"\"), or next call 400s. */\nexport function buildAssistantMessage(\n content: string,\n toolCalls: ToolCall[],\n producingModel: string,\n reasoningContent?: string | null,\n): ChatMessage {\n const msg: ChatMessage = { role: \"assistant\", content };\n if (toolCalls.length > 0) msg.tool_calls = toolCalls;\n // V4-era deepseek-chat returns reasoning_content even with thinking.type\n // disabled, and the API rejects round-trips that drop it. Whitelist on\n // model name is too brittle — preserve whenever the producer emitted any.\n if (isThinkingModeModel(producingModel) || (reasoningContent && reasoningContent.length > 0)) {\n msg.reasoning_content = reasoningContent ?? \"\";\n }\n return msg;\n}\n\n/** Abort notices etc — caller passes its current model as the thinking-mode stamp. */\nexport function buildSyntheticAssistantMessage(\n content: string,\n fallbackModel: string,\n): ChatMessage {\n return buildAssistantMessage(content, [], fallbackModel, \"\");\n}\n","import { type DeepSeekClient, Usage } from \"../client.js\";\nimport { t } from \"../i18n/index.js\";\nimport type { TurnStats } from \"../telemetry/stats.js\";\nimport type { ChatMessage } from \"../types.js\";\nimport { errorLabelFor, reasonPrefixFor } from \"./errors.js\";\nimport { buildAssistantMessage } from \"./messages.js\";\nimport { stripHallucinatedToolMarkup, thinkingModeForModel } from \"./thinking.js\";\nimport type { LoopEvent } from \"./types.js\";\n\nexport type ForceSummaryReason = \"budget\" | \"aborted\" | \"context-guard\" | \"stuck\";\n\nexport interface ForceSummaryContext {\n client: DeepSeekClient;\n signal: AbortSignal;\n buildMessages: () => ChatMessage[];\n appendAndPersist: (msg: ChatMessage) => void;\n recordStats: (model: string, usage: Usage) => TurnStats;\n turn: number;\n maxToolIters: number;\n}\n\nexport async function* forceSummaryAfterIterLimit(\n ctx: ForceSummaryContext,\n opts: { reason: ForceSummaryReason } = { reason: \"budget\" },\n): AsyncGenerator<LoopEvent> {\n try {\n // Status bridges the silence — summary call is non-streaming, 30-60s typical.\n yield { turn: ctx.turn, role: \"status\", content: t(\"summary.status\") };\n const messages = ctx.buildMessages();\n // Passing `tools: undefined` was supposed to force a text response,\n // but R1 can still hallucinate tool-call markup (e.g. DSML\n // `<|DSML|function_calls>…`) when primed by prior tool use. An\n // explicit user-role instruction plus post-hoc stripping of known\n // hallucination shapes keeps the user from seeing raw markup.\n messages.push({\n role: \"user\",\n content:\n \"I'm out of tool-call budget for this turn. Summarize in plain prose what you learned from the tool results above. Do NOT emit any tool calls, function-call markup, DSML invocations, or SEARCH/REPLACE edit blocks — they will be silently discarded. Just plain text.\",\n });\n // Pin to flash + effort=high regardless of the main turn's model —\n // pro is 12× overkill for \"paraphrase tool results into prose,\" and\n // budget-exhausted turns are exactly when we don't want to torch the wallet.\n const summaryModel = \"deepseek-v4-flash\";\n const summaryEffort: \"high\" | \"max\" = \"high\";\n const resp = await ctx.client.chat({\n model: summaryModel,\n messages,\n signal: ctx.signal,\n thinking: thinkingModeForModel(summaryModel),\n reasoningEffort: summaryEffort,\n });\n const rawContent = resp.content?.trim() ?? \"\";\n const cleaned = stripHallucinatedToolMarkup(rawContent);\n const summary = cleaned || t(\"summary.hallucinatedFallback\");\n const reasonPrefix = reasonPrefixFor(opts.reason, ctx.maxToolIters);\n const annotated = `${reasonPrefix}\\n\\n${summary}`;\n // Record under the actual model used (flash), so per-turn cost reflects reality.\n const summaryStats = ctx.recordStats(summaryModel, resp.usage ?? new Usage());\n ctx.appendAndPersist(buildAssistantMessage(summary, [], summaryModel, resp.reasoningContent));\n yield {\n turn: ctx.turn,\n role: \"assistant_final\",\n content: annotated,\n stats: summaryStats,\n forcedSummary: true,\n };\n yield { turn: ctx.turn, role: \"done\", content: summary };\n } catch (err) {\n const label = errorLabelFor(opts.reason, ctx.maxToolIters);\n yield {\n turn: ctx.turn,\n role: \"error\",\n content: \"\",\n error: t(\"summary.failedAfterReason\", { label, message: (err as Error).message }),\n };\n yield { turn: ctx.turn, role: \"done\", content: \"\" };\n }\n}\n","import { truncateForModel, truncateForModelByTokens } from \"../mcp/registry.js\";\nimport { countTokens } from \"../tokenizer.js\";\nimport type { ChatMessage } from \"../types.js\";\n\n/** UI progress feedback only — NOT a dispatch gate. */\nexport function looksLikeCompleteJson(s: string): boolean {\n if (!s || !s.trim()) return false;\n try {\n JSON.parse(s);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Tool-role only — truncating user prompts would corrupt authored intent. */\nexport function shrinkOversizedToolResults(\n messages: ChatMessage[],\n maxChars: number,\n): { messages: ChatMessage[]; healedCount: number; healedFrom: number } {\n let healedCount = 0;\n let healedFrom = 0;\n const out = messages.map((msg) => {\n if (msg.role !== \"tool\") return msg;\n const content = typeof msg.content === \"string\" ? msg.content : \"\";\n if (content.length <= maxChars) return msg;\n healedCount += 1;\n healedFrom += content.length;\n return { ...msg, content: truncateForModel(content, maxChars) };\n });\n return { messages: out, healedCount, healedFrom };\n}\n\n/** Token-cap variant — char cap would let CJK slip past at 2× the intended token cost. */\nexport function shrinkOversizedToolResultsByTokens(\n messages: ChatMessage[],\n maxTokens: number,\n): {\n messages: ChatMessage[];\n healedCount: number;\n tokensSaved: number;\n charsSaved: number;\n} {\n let healedCount = 0;\n let tokensSaved = 0;\n let charsSaved = 0;\n const out = messages.map((msg) => {\n if (msg.role !== \"tool\") return msg;\n const content = typeof msg.content === \"string\" ? msg.content : \"\";\n // length ≤ maxTokens ⇒ tokens ≤ maxTokens — skip the per-message tokenize.\n if (content.length <= maxTokens) return msg;\n const beforeTokens = countTokens(content);\n if (beforeTokens <= maxTokens) return msg;\n const truncated = truncateForModelByTokens(content, maxTokens);\n const afterTokens = countTokens(truncated);\n healedCount += 1;\n tokensSaved += Math.max(0, beforeTokens - afterTokens);\n charsSaved += Math.max(0, content.length - truncated.length);\n return { ...msg, content: truncated };\n });\n return { messages: out, healedCount, tokensSaved, charsSaved };\n}\n\n/** Caller must gate on paired tool_calls — in-flight calls would crash mid-turn. */\nexport function shrinkOversizedToolCallArgsByTokens(\n messages: ChatMessage[],\n maxTokens: number,\n): {\n messages: ChatMessage[];\n healedCount: number;\n tokensSaved: number;\n charsSaved: number;\n} {\n let healedCount = 0;\n let tokensSaved = 0;\n let charsSaved = 0;\n const out = messages.map((msg) => {\n if (msg.role !== \"assistant\" || !Array.isArray(msg.tool_calls)) return msg;\n let changed = false;\n const newCalls = msg.tool_calls.map((call) => {\n const args = call.function?.arguments;\n if (typeof args !== \"string\" || args.length <= maxTokens) return call;\n const beforeTokens = countTokens(args);\n if (beforeTokens <= maxTokens) return call;\n const shrunk = shrinkJsonLongStrings(args);\n const afterTokens = countTokens(shrunk);\n // Many-short-strings payloads can come back marginally larger — only swap on real saving.\n if (afterTokens >= beforeTokens) return call;\n changed = true;\n healedCount += 1;\n tokensSaved += beforeTokens - afterTokens;\n charsSaved += args.length - shrunk.length;\n return { ...call, function: { ...call.function, arguments: shrunk } };\n });\n if (!changed) return msg;\n return { ...msg, tool_calls: newCalls };\n });\n return { messages: out, healedCount, tokensSaved, charsSaved };\n}\n\n/** Keeps short keys/values (paths, ids) verbatim; only long string values get a marker. */\nfunction shrinkJsonLongStrings(jsonStr: string): string {\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n const head = jsonStr.slice(0, 200);\n return `${head}…[shrunk: ${jsonStr.length} chars, unparsed]`;\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return jsonStr;\n }\n const LONG_THRESHOLD = 300;\n const input = parsed as Record<string, unknown>;\n const output: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(input)) {\n if (typeof v === \"string\" && v.length > LONG_THRESHOLD) {\n const newlines = v.match(/\\n/g)?.length ?? 0;\n output[k] =\n `[…shrunk: ${v.length} chars, ${newlines} lines — tool already responded, see result]`;\n } else {\n output[k] = v;\n }\n }\n return JSON.stringify(output);\n}\n","import type { ChatMessage, ToolCall } from \"../types.js\";\nimport { shrinkOversizedToolResults, shrinkOversizedToolResultsByTokens } from \"./shrink.js\";\nimport { isThinkingModeModel } from \"./thinking.js\";\n\nlet _stampSeq = 0;\n\n/** DeepSeek 400s on tool_calls missing `id`. Give bare calls a fallback. */\nfunction stampMissingIds(calls: ToolCall[]): ToolCall[] {\n return calls.map((c) => (c.id ? c : { ...c, id: `z-ext-${Date.now()}-${_stampSeq++}` }));\n}\n\n/** Drops both unpaired assistant.tool_calls and stray tool messages — DeepSeek 400s on either. */\nexport function fixToolCallPairing(messages: ChatMessage[]): {\n messages: ChatMessage[];\n droppedAssistantCalls: number;\n droppedStrayTools: number;\n} {\n const out: ChatMessage[] = [];\n let droppedAssistantCalls = 0;\n let droppedStrayTools = 0;\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role === \"assistant\" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {\n // Stamp missing ids before validation — DeepSeek rejects tool_calls without id.\n const calls = stampMissingIds(msg.tool_calls);\n const needed = new Set<string>();\n for (const call of calls) {\n if (call.id) needed.add(call.id);\n }\n const candidates: ChatMessage[] = [];\n let j = i + 1;\n while (j < messages.length && needed.size > 0) {\n const nxt = messages[j]!;\n if (nxt.role !== \"tool\") break;\n const id = nxt.tool_call_id ?? \"\";\n if (!needed.has(id)) break;\n needed.delete(id);\n candidates.push(nxt);\n j++;\n }\n if (needed.size === 0) {\n out.push({ ...msg, tool_calls: calls });\n for (const r of candidates) out.push(r);\n i = j - 1;\n } else {\n droppedAssistantCalls += 1;\n droppedStrayTools += candidates.length;\n i = j - 1;\n }\n continue;\n }\n if (msg.role === \"tool\") {\n droppedStrayTools += 1;\n continue;\n }\n out.push(msg);\n }\n return { messages: out, droppedAssistantCalls, droppedStrayTools };\n}\n\nexport function healLoadedMessages(\n messages: ChatMessage[],\n maxChars: number,\n): { messages: ChatMessage[]; healedCount: number; healedFrom: number } {\n const shrunk = shrinkOversizedToolResults(messages, maxChars);\n const paired = fixToolCallPairing(shrunk.messages);\n const healedCount = shrunk.healedCount + paired.droppedAssistantCalls + paired.droppedStrayTools;\n return { messages: paired.messages, healedCount, healedFrom: shrunk.healedFrom };\n}\n\n/** Back-fills \"\" on bare assistant turns; skipped on non-thinking to avoid prefix-cache churn. */\nexport function stampMissingReasoningForThinkingMode(\n messages: ChatMessage[],\n model: string,\n): { messages: ChatMessage[]; stampedCount: number } {\n if (!isThinkingModeModel(model)) {\n return { messages, stampedCount: 0 };\n }\n let stampedCount = 0;\n const out = messages.map((msg) => {\n if (msg.role !== \"assistant\") return msg;\n if (Object.hasOwn(msg, \"reasoning_content\")) return msg;\n stampedCount += 1;\n return { ...msg, reasoning_content: \"\" };\n });\n return { messages: out, stampedCount };\n}\n\n/** Token-cap variant — char cap would let CJK slip past at 2× the intended token cost. */\nexport function healLoadedMessagesByTokens(\n messages: ChatMessage[],\n maxTokens: number,\n): {\n messages: ChatMessage[];\n healedCount: number;\n tokensSaved: number;\n charsSaved: number;\n} {\n const shrunk = shrinkOversizedToolResultsByTokens(messages, maxTokens);\n const paired = fixToolCallPairing(shrunk.messages);\n const healedCount = shrunk.healedCount + paired.droppedAssistantCalls + paired.droppedStrayTools;\n return {\n messages: paired.messages,\n healedCount,\n tokensSaved: shrunk.tokensSaved,\n charsSaved: shrunk.charsSaved,\n };\n}\n","import { type HookOutcome, formatHookOutcomeMessage } from \"../hooks.js\";\nimport type { LoopEvent } from \"./types.js\";\n\nexport function safeParseToolArgs(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/** Format non-pass hook outcomes as `LoopEvent`s of role `warning`. */\nexport function* hookWarnings(outcomes: HookOutcome[], turn: number): Generator<LoopEvent> {\n for (const o of outcomes) {\n if (o.decision === \"pass\") continue;\n yield { turn, role: \"warning\", content: formatHookOutcomeMessage(o) };\n }\n}\n","/** Default streak length before the loop force-escalates flash→pro on consecutive read-only tool calls (#681). */\nexport const READONLY_LOOP_ESCALATION_THRESHOLD = 8;\n\n/** Streak of consecutive read-only tool calls within a step; mutating call resets. Crossing the threshold lets the loop escalate flash→pro before the user pays for many more reads. */\nexport class ReadOnlyLoopTracker {\n private streak = 0;\n private readonly threshold: number;\n\n constructor(threshold: number = READONLY_LOOP_ESCALATION_THRESHOLD) {\n this.threshold = Math.max(1, threshold);\n }\n\n reset(): void {\n this.streak = 0;\n }\n\n /** True ONLY on the call where the streak crosses the configured threshold. */\n noteAndCrossedThreshold(isReadOnly: boolean): boolean {\n if (!isReadOnly) {\n this.streak = 0;\n return false;\n }\n const before = this.streak;\n this.streak += 1;\n return before < this.threshold && this.streak >= this.threshold;\n }\n\n get currentStreak(): number {\n return this.streak;\n }\n}\n","import type { RepairReport } from \"../repair/index.js\";\n\nexport const FAILURE_ESCALATION_THRESHOLD = 3;\n\nexport class TurnFailureTracker {\n private count = 0;\n private types: Record<string, number> = {};\n private readonly threshold: number;\n\n constructor(threshold: number = FAILURE_ESCALATION_THRESHOLD) {\n this.threshold = threshold;\n }\n\n reset(): void {\n this.count = 0;\n this.types = {};\n }\n\n /** True ONLY on the call where the count crosses the configured threshold. */\n noteAndCrossedThreshold(resultJson: string, repair?: RepairReport): boolean {\n const before = this.count;\n const bump = (kind: string, by = 1): void => {\n this.count += by;\n this.types[kind] = (this.types[kind] ?? 0) + by;\n };\n if (resultJson.includes('\"error\"') && resultJson.includes(\"search text not found\")) {\n bump(\"search-mismatch\");\n }\n if (repair) {\n if (repair.scavenged > 0) bump(\"scavenged\", repair.scavenged);\n if (repair.truncationsFixed > 0) bump(\"truncated\", repair.truncationsFixed);\n if (repair.stormsBroken > 0) bump(\"repeat-loop\", repair.stormsBroken);\n }\n return before < this.threshold && this.count >= this.threshold;\n }\n\n formatBreakdown(): string {\n const parts = Object.entries(this.types)\n .filter(([, n]) => n > 0)\n .map(([kind, n]) => `${n}× ${kind}`);\n return parts.length > 0 ? parts.join(\", \") : `${this.count} repair/error signal(s)`;\n }\n}\n","import { createHash } from \"node:crypto\";\nimport type { ChatMessage, ToolSpec } from \"../types.js\";\n\nexport interface ImmutablePrefixOptions {\n system: string;\n toolSpecs?: readonly ToolSpec[];\n fewShots?: readonly ChatMessage[];\n}\n\nexport class ImmutablePrefix {\n /** Stable across turns; rebuilt only on /new when LUCKERR.md changed on disk. */\n system: string;\n /** Each `addTool` costs one cache-miss turn — DeepSeek's prefix cache is keyed by full tool list. */\n private _toolSpecs: ToolSpec[];\n readonly fewShots: readonly ChatMessage[];\n /** Invalidated by addTool / removeTool / replaceSystem; bypassing any of those leaves cache stale → fingerprint diverges from sent prefix. */\n private _fingerprintCache: string | null = null;\n\n constructor(opts: ImmutablePrefixOptions) {\n this.system = opts.system;\n this._toolSpecs = [...(opts.toolSpecs ?? [])];\n this.fewShots = Object.freeze([...(opts.fewShots ?? [])]);\n }\n\n /** Replaces the system prompt; returns true iff the string actually changed. Caller must accept a cache miss on the next turn. */\n replaceSystem(s: string): boolean {\n if (this.system === s) return false;\n this.system = s;\n this._fingerprintCache = null;\n return true;\n }\n\n get toolSpecs(): readonly ToolSpec[] {\n return this._toolSpecs;\n }\n\n toMessages(): ChatMessage[] {\n return [{ role: \"system\", content: this.system }, ...this.fewShots.map((m) => ({ ...m }))];\n }\n\n tools(): ToolSpec[] {\n return this._toolSpecs.map((t) => structuredClone(t) as ToolSpec);\n }\n\n addTool(spec: ToolSpec): boolean {\n const name = spec.function?.name;\n if (!name) return false;\n if (this._toolSpecs.some((t) => t.function?.name === name)) return false;\n this._toolSpecs.push(spec);\n this._fingerprintCache = null;\n return true;\n }\n\n /** Mirror of addTool for MCP hot-unbridge. Same cache-miss cost — prefix changes shape. */\n removeTool(name: string): boolean {\n const idx = this._toolSpecs.findIndex((t) => t.function?.name === name);\n if (idx < 0) return false;\n this._toolSpecs.splice(idx, 1);\n this._fingerprintCache = null;\n return true;\n }\n\n get fingerprint(): string {\n if (this._fingerprintCache !== null) return this._fingerprintCache;\n this._fingerprintCache = this.computeFingerprint();\n return this._fingerprintCache;\n }\n\n /** Dev/test only — throws on cache drift, which always means a non-`addTool` mutation slipped in. */\n verifyFingerprint(): string {\n const fresh = this.computeFingerprint();\n if (this._fingerprintCache !== null && this._fingerprintCache !== fresh) {\n throw new Error(\n `ImmutablePrefix fingerprint drift: cached=${this._fingerprintCache}, fresh=${fresh}. A mutation path bypassed addTool's cache invalidation — DeepSeek will see prefix churn that the TUI / transcript log don't know about.`,\n );\n }\n this._fingerprintCache = fresh;\n return fresh;\n }\n\n private computeFingerprint(): string {\n const blob = JSON.stringify({\n system: this.system,\n tools: this._toolSpecs,\n shots: this.fewShots,\n });\n return createHash(\"sha256\").update(blob).digest(\"hex\").slice(0, 16);\n }\n}\n\nexport class AppendOnlyLog {\n private _entries: ChatMessage[] = [];\n\n append(message: ChatMessage): void {\n if (!message || typeof message !== \"object\" || !(\"role\" in message)) {\n throw new Error(`invalid log entry: ${JSON.stringify(message)}`);\n }\n this._entries.push(message);\n }\n\n extend(messages: ChatMessage[]): void {\n for (const m of messages) this.append(m);\n }\n\n /** The one append-only-breaking path — reserved for `/compact` + recovery. Use `append()` otherwise. */\n compactInPlace(replacement: ChatMessage[]): void {\n this._entries = [...replacement];\n }\n\n get entries(): readonly ChatMessage[] {\n return this._entries;\n }\n\n toMessages(): ChatMessage[] {\n return this._entries.map((e) => ({ ...e }));\n }\n\n get length(): number {\n return this._entries.length;\n }\n}\n\nexport class VolatileScratch {\n reasoning: string | null = null;\n planState: Record<string, unknown> | null = null;\n notes: string[] = [];\n\n reset(): void {\n this.reasoning = null;\n this.planState = null;\n this.notes = [];\n }\n}\n","/** R1 sometimes emits tool-call JSON inside reasoning_content and forgets `tool_calls`; recover those calls. */\n\nimport type { ToolCall } from \"../types.js\";\n\nexport interface ScavengeOptions {\n /** Names of tools the model may legitimately call. Other names are ignored. */\n allowedNames: ReadonlySet<string>;\n /** Maximum number of calls to scavenge per pass (defence against runaway). */\n maxCalls?: number;\n}\n\nexport interface ScavengeResult {\n calls: ToolCall[];\n notes: string[];\n}\n\n/** Bounds the regex input — DSML matchers are O(n²) on adversarial input per CodeQL js/polynomial-redos. */\nconst MAX_SCAVENGE_INPUT = 100 * 1024;\n\nexport function scavengeToolCalls(\n reasoningContent: string | null | undefined,\n opts: ScavengeOptions,\n): ScavengeResult {\n if (!reasoningContent) return { calls: [], notes: [] };\n if (reasoningContent.length > MAX_SCAVENGE_INPUT) {\n return {\n calls: [],\n notes: [`scavenge skipped: reasoning_content too large (${reasoningContent.length} chars)`],\n };\n }\n const max = opts.maxCalls ?? 4;\n const notes: string[] = [];\n const out: ToolCall[] = [];\n\n // Pattern A: DSML invoke blocks. R1 sometimes emits tool calls as\n // its chat-template markup in the content channel instead of the\n // proper `tool_calls` field. 0.4.3 stripped these from display;\n // here we actually turn them back into proper ToolCalls so the\n // model's intent isn't lost.\n for (const invoke of iterateDsmlInvokes(reasoningContent)) {\n if (out.length >= max) break;\n if (!opts.allowedNames.has(invoke.name)) continue;\n out.push({\n function: {\n name: invoke.name,\n arguments: JSON.stringify(invoke.args),\n },\n });\n notes.push(`scavenged DSML call: ${invoke.name}`);\n }\n\n // Pattern B: raw JSON objects (the original three shapes). Strip\n // any DSML blocks we already processed so parameter JSON buried\n // inside them doesn't get re-scavenged as a standalone call.\n const nonDsml = stripDsmlBlocks(reasoningContent);\n for (const candidate of iterateJsonObjects(nonDsml)) {\n if (out.length >= max) break;\n const call = coerceToToolCall(candidate, opts.allowedNames);\n if (call) {\n out.push(call);\n notes.push(`scavenged call: ${call.function.name}`);\n }\n }\n return { calls: out, notes };\n}\n\ninterface DsmlInvoke {\n name: string;\n args: Record<string, unknown>;\n}\n\n/** Strips DSML invoke blocks so the raw-JSON scanner doesn't re-scavenge their parameter payloads. */\nfunction stripDsmlBlocks(text: string): string {\n let out = text;\n out = out.replace(/<[||]DSML[||]function_calls>[\\s\\S]*?<\\/?[||]DSML[||]function_calls>/g, \"\");\n out = out.replace(/<[||]DSML[||]invoke\\s+[^>]*>[\\s\\S]*?<\\/[||]DSML[||]invoke>/g, \"\");\n return out;\n}\n\nfunction* iterateDsmlInvokes(text: string): Generator<DsmlInvoke> {\n // `|` (U+FF5C) in practice; `|` (ASCII) as a fallback seen in a\n // minority of builds. `[||]` inside the regex covers both.\n const INVOKE_RE = /<[||]DSML[||]invoke\\s+name=\"([^\"]+)\">([\\s\\S]*?)<\\/[||]DSML[||]invoke>/g;\n for (const match of text.matchAll(INVOKE_RE)) {\n const name = match[1];\n const body = match[2];\n if (!name || body === undefined) continue;\n yield { name, args: parseDsmlParameters(body) };\n }\n}\n\n/** Falls back to literal text when `string=\"false\"` JSON parse fails — never lose the parameter. */\nfunction parseDsmlParameters(body: string): Record<string, unknown> {\n const PARAM_RE =\n /<[||]DSML[||]parameter\\s+name=\"([^\"]+)\"(?:\\s+string=\"(true|false)\")?\\s*>([\\s\\S]*?)<\\/[||]DSML[||]parameter>/g;\n const args: Record<string, unknown> = {};\n for (const m of body.matchAll(PARAM_RE)) {\n const key = m[1];\n const stringFlag = m[2];\n const raw = (m[3] ?? \"\").trim();\n if (!key) continue;\n if (stringFlag === \"false\") {\n try {\n args[key] = JSON.parse(raw);\n continue;\n } catch {\n // Fall through — keep as literal so the information isn't lost.\n }\n }\n args[key] = raw;\n }\n return args;\n}\n\n/** Yield every top-level JSON object substring in `text`. */\nfunction* iterateJsonObjects(text: string): Generator<string> {\n for (let i = 0; i < text.length; i++) {\n if (text[i] !== \"{\") continue;\n let depth = 0;\n let inString = false;\n let escaped = false;\n for (let j = i; j < text.length; j++) {\n const c = text[j]!;\n if (escaped) {\n escaped = false;\n continue;\n }\n if (inString) {\n if (c === \"\\\\\") {\n escaped = true;\n continue;\n }\n if (c === '\"') inString = false;\n continue;\n }\n if (c === '\"') inString = true;\n else if (c === \"{\") depth++;\n else if (c === \"}\") {\n depth--;\n if (depth === 0) {\n yield text.slice(i, j + 1);\n i = j;\n break;\n }\n }\n }\n }\n}\n\nfunction coerceToToolCall(\n candidateJson: string,\n allowedNames: ReadonlySet<string>,\n): ToolCall | null {\n let parsed: any;\n try {\n parsed = JSON.parse(candidateJson);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== \"object\") return null;\n\n // Pattern 1: { name, arguments }\n if (typeof parsed.name === \"string\" && allowedNames.has(parsed.name)) {\n const args = parsed.arguments;\n return {\n function: {\n name: parsed.name,\n arguments: typeof args === \"string\" ? args : JSON.stringify(args ?? {}),\n },\n };\n }\n\n // Pattern 2: OpenAI-style { type: \"function\", function: { name, arguments } }\n if (\n parsed.type === \"function\" &&\n parsed.function &&\n typeof parsed.function.name === \"string\" &&\n allowedNames.has(parsed.function.name)\n ) {\n const args = parsed.function.arguments;\n return {\n type: \"function\",\n function: {\n name: parsed.function.name,\n arguments: typeof args === \"string\" ? args : JSON.stringify(args ?? {}),\n },\n };\n }\n\n // Pattern 3: { tool_name, tool_args } (R1 free-form variant)\n if (typeof parsed.tool_name === \"string\" && allowedNames.has(parsed.tool_name)) {\n return {\n function: {\n name: parsed.tool_name,\n arguments: JSON.stringify(parsed.tool_args ?? {}),\n },\n };\n }\n\n return null;\n}\n","import type { ToolCall } from \"../types.js\";\n\n/** Mutating calls clear prior read-only entries so a post-edit re-read isn't flagged as repeat. */\nexport type IsMutating = (call: ToolCall) => boolean;\nexport type IsStormExempt = (call: ToolCall) => boolean;\n\ninterface RecentEntry {\n name: string;\n args: string;\n readOnly: boolean;\n}\n\n/** Tracks (name, args) repeats; mutating calls clear prior read-only entries while still counting amongst themselves. */\nexport class StormBreaker {\n private readonly windowSize: number;\n private readonly threshold: number;\n private readonly isMutating: IsMutating | undefined;\n private readonly isStormExempt: IsStormExempt | undefined;\n private readonly recent: RecentEntry[] = [];\n\n constructor(\n windowSize = 6,\n threshold = 3,\n isMutating?: IsMutating,\n isStormExempt?: IsStormExempt,\n ) {\n this.windowSize = windowSize;\n this.threshold = threshold;\n this.isMutating = isMutating;\n this.isStormExempt = isStormExempt;\n }\n\n inspect(call: ToolCall): { suppress: boolean; reason?: string } {\n const name = call.function?.name;\n if (!name) return { suppress: false };\n if (this.isStormExempt?.(call)) return { suppress: false };\n const args = call.function?.arguments ?? \"\";\n const mutating = this.isMutating ? this.isMutating(call) : false;\n const readOnly = !mutating;\n\n if (mutating) {\n // Drop prior read-only entries — the file/shell state just\n // changed, so a verify-read after this should start with a\n // clean slate. Keep mutator entries: 3 identical edits in a row\n // is still a storm (model in a loop).\n for (let i = this.recent.length - 1; i >= 0; i--) {\n if (this.recent[i]!.readOnly) this.recent.splice(i, 1);\n }\n }\n\n const count = this.recent.reduce((n, e) => (e.name === name && e.args === args ? n + 1 : n), 0);\n if (count >= this.threshold - 1) {\n return {\n suppress: true,\n reason: `${name} called with identical args ${count + 1} times — repeat-loop guard tripped`,\n };\n }\n this.recent.push({ name, args, readOnly });\n while (this.recent.length > this.windowSize) this.recent.shift();\n return { suppress: false };\n }\n\n reset(): void {\n this.recent.length = 0;\n }\n}\n","/** Local-only repair (balance braces, close strings, fill nulls); continuation calls belong to the loop, which owns budgets. */\n\nexport interface TruncationRepairResult {\n repaired: string;\n changed: boolean;\n notes: string[];\n}\n\nexport function repairTruncatedJson(input: string): TruncationRepairResult {\n const notes: string[] = [];\n if (!input || !input.trim()) {\n return { repaired: \"{}\", changed: input !== \"{}\", notes: [\"empty input → {}\"] };\n }\n // Fast path: already parseable.\n try {\n JSON.parse(input);\n return { repaired: input, changed: false, notes: [] };\n } catch {\n /* fall through */\n }\n\n const stack: (\"{\" | \"[\" | '\"')[] = [];\n let escaped = false;\n let inString = false;\n let lastSignificant = -1;\n\n for (let i = 0; i < input.length; i++) {\n const c = input[i]!;\n if (!/\\s/.test(c)) lastSignificant = i;\n if (escaped) {\n escaped = false;\n continue;\n }\n if (inString) {\n if (c === \"\\\\\") {\n escaped = true;\n continue;\n }\n if (c === '\"') {\n inString = false;\n stack.pop();\n }\n continue;\n }\n if (c === '\"') {\n inString = true;\n stack.push('\"');\n continue;\n }\n if (c === \"{\" || c === \"[\") stack.push(c);\n else if (c === \"}\" || c === \"]\") stack.pop();\n }\n\n let s = input.slice(0, lastSignificant + 1);\n\n // Trim a trailing comma which would block re-parse.\n if (/,$/.test(s)) {\n s = s.replace(/,$/, \"\");\n notes.push(\"trimmed trailing comma\");\n }\n\n // If we ended on a key without a value: \"foo\": → \"foo\": null\n if (/\"\\s*:\\s*$/.test(s)) {\n s += \" null\";\n notes.push(\"filled dangling key with null\");\n }\n\n // If we ended inside a string, close it.\n if (inString) {\n s += '\"';\n stack.pop();\n notes.push(\"closed unterminated string\");\n }\n\n // Pop remaining open structures in reverse order.\n while (stack.length > 0) {\n const top = stack.pop();\n if (top === \"{\") s += \"}\";\n else if (top === \"[\") s += \"]\";\n else if (top === '\"') s += '\"';\n }\n\n try {\n JSON.parse(s);\n return { repaired: s, changed: true, notes };\n } catch (err) {\n notes.push(`fallback to {}: ${(err as Error).message}`);\n return { repaired: \"{}\", changed: true, notes };\n }\n}\n","/** Pass order: scavenge → truncation → storm. Schema flatten runs at loop construction, not per-turn. */\n\nimport type { ToolCall } from \"../types.js\";\nimport { scavengeToolCalls } from \"./scavenge.js\";\nimport { type IsMutating, type IsStormExempt, StormBreaker } from \"./storm.js\";\nimport { repairTruncatedJson } from \"./truncation.js\";\n\nexport { analyzeSchema, flattenSchema, nestArguments } from \"./flatten.js\";\nexport type { FlattenDecision } from \"./flatten.js\";\nexport { repairTruncatedJson } from \"./truncation.js\";\nexport type { TruncationRepairResult } from \"./truncation.js\";\nexport { scavengeToolCalls } from \"./scavenge.js\";\nexport type { ScavengeOptions, ScavengeResult } from \"./scavenge.js\";\nexport { StormBreaker } from \"./storm.js\";\n\nexport interface RepairReport {\n scavenged: number;\n truncationsFixed: number;\n stormsBroken: number;\n notes: string[];\n}\n\nexport interface ToolCallRepairOptions {\n allowedToolNames: ReadonlySet<string>;\n stormWindow?: number;\n stormThreshold?: number;\n maxScavenge?: number;\n /** Mutating calls clear the storm window so a post-edit verify-read isn't seen as a repeat. */\n isMutating?: IsMutating;\n /** Cheap state-inspection calls that should never trip repeat-loop suppression. */\n isStormExempt?: IsStormExempt;\n}\n\nexport class ToolCallRepair {\n private readonly storm: StormBreaker;\n private readonly opts: ToolCallRepairOptions;\n\n constructor(opts: ToolCallRepairOptions) {\n this.opts = opts;\n this.storm = new StormBreaker(\n opts.stormWindow ?? 6,\n opts.stormThreshold ?? 3,\n opts.isMutating,\n opts.isStormExempt,\n );\n }\n\n /** Called at start of every user turn — fresh intent shouldn't inherit old repetition state. */\n resetStorm(): void {\n this.storm.reset();\n }\n\n process(\n declaredCalls: ToolCall[],\n reasoningContent: string | null,\n content: string | null = null,\n ): { calls: ToolCall[]; report: RepairReport } {\n const report: RepairReport = {\n scavenged: 0,\n truncationsFixed: 0,\n stormsBroken: 0,\n notes: [],\n };\n\n // 1. Scavenge — only add calls whose (name,args) signature is novel.\n // Scan both channels: reasoning (where R1 leaks JSON calls into\n // <think>) AND content (where it emits DSML markup in regular\n // turns). Joined with a newline so the scanners see the blobs as\n // independent bodies. Dedup below keeps us from inflating if the\n // same call shows up in both — first seen wins.\n const combined = [reasoningContent ?? \"\", content ?? \"\"].filter(Boolean).join(\"\\n\");\n const scavenged = scavengeToolCalls(combined || null, {\n allowedNames: this.opts.allowedToolNames,\n maxCalls: this.opts.maxScavenge ?? 4,\n });\n const seenSignatures = new Set(declaredCalls.map(signature));\n const merged = [...declaredCalls];\n for (const sc of scavenged.calls) {\n if (!seenSignatures.has(signature(sc))) {\n merged.push(sc);\n report.scavenged++;\n seenSignatures.add(signature(sc));\n }\n }\n report.notes.push(...scavenged.notes);\n\n // 2. Truncation repair on argument JSON.\n for (const call of merged) {\n const args = call.function?.arguments ?? \"\";\n const r = repairTruncatedJson(args);\n if (r.changed) {\n call.function.arguments = r.repaired;\n report.truncationsFixed++;\n report.notes.push(...r.notes.map((n) => `[${call.function.name}] ${n}`));\n }\n }\n\n // 3. Storm breaker.\n const filtered: ToolCall[] = [];\n for (const call of merged) {\n const verdict = this.storm.inspect(call);\n if (verdict.suppress) {\n report.stormsBroken++;\n if (verdict.reason) report.notes.push(verdict.reason);\n continue;\n }\n filtered.push(call);\n }\n\n return { calls: filtered, report };\n }\n}\n\nfunction signature(call: ToolCall): string {\n return `${call.function?.name ?? \"\"}::${call.function?.arguments ?? \"\"}`;\n}\n","import { type OpenAICompatClient, Usage } from \"./client.js\";\nimport type { PauseGate } from \"./core/pause-gate.js\";\nimport { pauseGate as defaultPauseGate } from \"./core/pause-gate.js\";\nimport { type HookPayload, type ResolvedHook, runHooks } from \"./hooks.js\";\nimport {\n DEFAULT_MAX_RESULT_CHARS,\n DEFAULT_MAX_RESULT_TOKENS,\n truncateForModel,\n truncateForModelByTokens,\n} from \"./mcp/registry.js\";\n\nimport { ContextManager } from \"./context-manager.js\";\nimport { InflightSet } from \"./core/inflight.js\";\nimport { t } from \"./i18n/index.js\";\nimport { formatLoopError, is5xxError, probeProviderReachable } from \"./loop/errors.js\";\nimport {\n NEEDS_PRO_BUFFER_CHARS,\n isEscalationRequest,\n looksLikePartialEscalationMarker,\n parseEscalationMarker,\n} from \"./loop/escalation.js\";\nimport { type ForceSummaryContext, forceSummaryAfterIterLimit } from \"./loop/force-summary.js\";\nimport {\n fixToolCallPairing,\n healLoadedMessages,\n healLoadedMessagesByTokens,\n stampMissingReasoningForThinkingMode,\n} from \"./loop/healing.js\";\nimport { hookWarnings, safeParseToolArgs } from \"./loop/hook-events.js\";\nimport { buildAssistantMessage, buildSyntheticAssistantMessage } from \"./loop/messages.js\";\nimport {\n READONLY_LOOP_ESCALATION_THRESHOLD,\n ReadOnlyLoopTracker,\n} from \"./loop/read-only-loop-tracker.js\";\nimport {\n looksLikeCompleteJson,\n shrinkOversizedToolCallArgsByTokens,\n shrinkOversizedToolResults,\n shrinkOversizedToolResultsByTokens,\n} from \"./loop/shrink.js\";\nimport {\n isThinkingModeModel,\n stripHallucinatedToolMarkup,\n thinkingModeForModel,\n} from \"./loop/thinking.js\";\nimport { FAILURE_ESCALATION_THRESHOLD, TurnFailureTracker } from \"./loop/turn-failure-tracker.js\";\nimport type { LoopEvent } from \"./loop/types.js\";\nimport { AppendOnlyLog, type ImmutablePrefix, VolatileScratch } from \"./memory/runtime.js\";\nimport {\n appendSessionMessage,\n archiveSession,\n loadSessionMessages,\n loadSessionMeta,\n rewriteSession,\n} from \"./memory/session.js\";\nimport { type RepairReport, ToolCallRepair } from \"./repair/index.js\";\nimport { SessionStats, type TurnStats } from \"./telemetry/stats.js\";\nimport { countTokens } from \"./tokenizer.js\";\nimport { ToolRegistry } from \"./tools.js\";\nimport type { ChatMessage, ToolCall } from \"./types.js\";\n\n/** Default escalation model — used when the provider doesn't specify one. */\nconst DEFAULT_ESCALATION_MODEL = \"deepseek-v4-pro\";\n/** Iters-from-cap at which the parent loop starts injecting a remaining-budget tail into tool results. Subagent uses 3 against a 16-cap; parent's default 64-cap means this fires only at iter ≥ 60. */\nconst PARENT_BUDGET_WARN_THRESHOLD = 5;\n\nexport {\n fixToolCallPairing,\n formatLoopError,\n healLoadedMessages,\n healLoadedMessagesByTokens,\n isThinkingModeModel,\n looksLikeCompleteJson,\n shrinkOversizedToolCallArgsByTokens,\n shrinkOversizedToolResults,\n shrinkOversizedToolResultsByTokens,\n stampMissingReasoningForThinkingMode,\n stripHallucinatedToolMarkup,\n thinkingModeForModel,\n};\nexport type { EventRole, LoopEvent } from \"./loop/types.js\";\n\nexport interface CacheFirstLoopOptions {\n client: OpenAICompatClient;\n prefix: ImmutablePrefix;\n tools?: ToolRegistry;\n model?: string;\n maxToolIters?: number;\n stream?: boolean;\n reasoningEffort?: \"high\" | \"max\";\n autoEscalate?: boolean;\n /** Soft USD cap — warns at 80%, refuses next turn at 100%. Opt-in (default no cap). */\n budgetUsd?: number;\n /** Per-turn repair/error signal count required to escalate flash→pro. Defaults to FAILURE_ESCALATION_THRESHOLD. Out-of-range values warn + fall back. */\n failureThreshold?: number;\n session?: string;\n /** PreToolUse + PostToolUse only — UserPromptSubmit / Stop live at the App boundary. */\n hooks?: ResolvedHook[];\n /** `cwd` reported to hooks; `luckerr code` sets this to the sandbox root, not shell home. */\n hookCwd?: string;\n /** PauseGate bridge — defaults to singleton, injectable for tests. */\n confirmationGate?: PauseGate;\n /** Re-runs the prompt builder (applyMemoryStack / codeSystemPrompt) on /new so LUCKERR.md edits take effect without a restart. Accepting a cache miss is the price. */\n rebuildSystem?: () => string;\n}\n\nexport interface ReconfigurableOptions {\n model?: string;\n stream?: boolean;\n /** V4 thinking mode only; deepseek-chat ignores. */\n reasoningEffort?: \"high\" | \"max\";\n /** `false` pins to `model` — kills both NEEDS_PRO marker scavenge and failure-count threshold. */\n autoEscalate?: boolean;\n}\n\nexport class CacheFirstLoop {\n readonly client: OpenAICompatClient;\n readonly prefix: ImmutablePrefix;\n readonly tools: ToolRegistry;\n readonly maxToolIters: number;\n readonly log = new AppendOnlyLog();\n readonly scratch = new VolatileScratch();\n readonly stats = new SessionStats();\n readonly repair: ToolCallRepair;\n\n // Mutable via configure() — slash commands in the TUI / library callers tweak\n // these mid-session so users don't have to restart.\n model: string;\n stream: boolean;\n reasoningEffort: \"high\" | \"max\";\n autoEscalate = true;\n budgetUsd: number | null;\n /** One-shot 80% warning latch — cleared by setBudget so a bump re-arms at the new boundary. */\n private _budgetWarned = false;\n sessionName: string | null;\n\n hooks: ResolvedHook[];\n hookCwd: string;\n\n /** PauseGate bridge — defaults to singleton, injectable for tests. */\n readonly confirmationGate: PauseGate;\n\n /** Number of messages that were pre-loaded from the session file. */\n readonly resumedMessageCount: number;\n\n private readonly _rebuildSystem: (() => string) | null;\n\n private _turn = 0;\n private _streamPreference: boolean;\n /** Threaded through HTTP + every tool dispatch so Esc cancels in-flight work, not after. */\n private _turnAbort: AbortController = new AbortController();\n /** Authoritative running-id set — UI cards consult this instead of trusting end-event delivery. Insert at dispatch entry, delete in finally. */\n private readonly _inflight = new InflightSet();\n\n private _proArmedForNextTurn = false;\n private _escalateThisTurn = false;\n private readonly _turnFailures: TurnFailureTracker;\n private readonly _readOnlyLoop: ReadOnlyLoopTracker;\n private _turnSelfCorrected = false;\n private _foldedThisTurn = false;\n private _toolDispatchesThisStep = 0;\n private context!: ContextManager;\n\n /** Subscribe API so UI hooks can derive `running` from finally-guaranteed insertions. */\n get inflight(): InflightSet {\n return this._inflight;\n }\n\n get currentTurn(): number {\n return this._turn;\n }\n\n constructor(opts: CacheFirstLoopOptions) {\n this.client = opts.client;\n this.prefix = opts.prefix;\n this.tools = opts.tools ?? new ToolRegistry();\n this.model = opts.model ?? \"deepseek-v4-flash\";\n this.reasoningEffort = opts.reasoningEffort ?? \"max\";\n if (opts.autoEscalate !== undefined) this.autoEscalate = opts.autoEscalate;\n this.budgetUsd =\n typeof opts.budgetUsd === \"number\" && opts.budgetUsd > 0 ? opts.budgetUsd : null;\n this._turnFailures = new TurnFailureTracker(\n resolveFailureThreshold(opts.failureThreshold, FAILURE_ESCALATION_THRESHOLD),\n );\n this._readOnlyLoop = new ReadOnlyLoopTracker(\n parsePositiveIntEnv(process.env.LUCKERR_READONLY_LOOP_THRESHOLD) ??\n READONLY_LOOP_ESCALATION_THRESHOLD,\n );\n // Last-resort backstop — primary stop is the token-context guard inside step().\n this.maxToolIters = opts.maxToolIters ?? 64;\n this.hooks = opts.hooks ?? [];\n this.hookCwd = opts.hookCwd ?? process.cwd();\n this.confirmationGate = opts.confirmationGate ?? defaultPauseGate;\n this._rebuildSystem = opts.rebuildSystem ?? null;\n\n this._streamPreference = opts.stream ?? true;\n this.stream = this._streamPreference;\n\n const allowedNames = new Set([...this.prefix.toolSpecs.map((s) => s.function.name)]);\n // Storm breaker clears its window on mutating calls so read → edit → verify isn't a storm.\n const registry = this.tools;\n const isStormExempt = (call: ToolCall): boolean => {\n const name = call.function?.name;\n if (!name) return false;\n return registry.get(name)?.stormExempt === true;\n };\n this.repair = new ToolCallRepair({\n allowedToolNames: allowedNames,\n isMutating: (call) => this.isMutating(call),\n isStormExempt,\n stormThreshold: parsePositiveIntEnv(process.env.LUCKERR_STORM_THRESHOLD),\n stormWindow: parsePositiveIntEnv(process.env.LUCKERR_STORM_WINDOW),\n });\n\n // Inject a remaining-iter hint into tool results when closing in on the per-turn cap. Subagent's child registry pre-installs its own augmenter before constructing the child loop — preserve it instead of clobbering.\n if (!this.tools.hasResultAugmenter) {\n this.tools.setResultAugmenter((_name, _args, result) => {\n this._toolDispatchesThisStep++;\n const remaining = this.maxToolIters - this._toolDispatchesThisStep;\n if (remaining <= 0) {\n return `${result}\\n\\n[budget: 0 of ${this.maxToolIters} tool calls left this turn — finalize NOW; the next iter forces a summary]`;\n }\n if (remaining <= PARENT_BUDGET_WARN_THRESHOLD) {\n return `${result}\\n\\n[budget: ${remaining} of ${this.maxToolIters} tool calls left this turn — wrap up soon]`;\n }\n return result;\n });\n }\n\n // Heal-on-load: oversized tool results would 400 the next call before the user types.\n this.sessionName = opts.session ?? null;\n if (this.sessionName) {\n const prior = loadSessionMessages(this.sessionName);\n const shrunk = healLoadedMessagesByTokens(prior, DEFAULT_MAX_RESULT_TOKENS);\n // Thinking-mode sessions: API 400s if any historical assistant turn lacks reasoning_content.\n const stamped = stampMissingReasoningForThinkingMode(shrunk.messages, this.model);\n const messages = stamped.messages;\n const healedCount = shrunk.healedCount + stamped.stampedCount;\n const tokensSaved = shrunk.tokensSaved;\n for (const msg of messages) this.log.append(msg);\n this.resumedMessageCount = messages.length;\n this._turn = messages.reduce((n, m) => (m.role === \"assistant\" ? n + 1 : n), 0);\n // Carry forward cumulative cost / turn count so the TUI's session\n // total continues across resumes; otherwise each restart resets to $0.\n if (messages.length > 0) {\n const meta = loadSessionMeta(this.sessionName);\n this.stats.seedCarryover({\n totalCostUsd: meta.totalCostUsd,\n turnCount: meta.turnCount,\n cacheHitTokens: meta.cacheHitTokens,\n cacheMissTokens: meta.cacheMissTokens,\n lastPromptTokens: meta.lastPromptTokens,\n });\n }\n if (healedCount > 0) {\n // Persist healed log so the same break isn't re-noticed every restart.\n try {\n rewriteSession(this.sessionName, messages);\n } catch {\n /* disk full / perms — skip, in-memory heal still applies */\n }\n process.stderr.write(\n `▸ session \"${this.sessionName}\": healed ${healedCount} entr${healedCount === 1 ? \"y\" : \"ies\"}${tokensSaved > 0 ? ` (shrunk ${tokensSaved.toLocaleString()} tokens of oversized tool output)` : \" (dropped dangling tool_calls tail)\"}. Rewrote session file.\\n`,\n );\n }\n } else {\n this.resumedMessageCount = 0;\n }\n\n this.context = new ContextManager({\n client: this.client,\n log: this.log,\n stats: this.stats,\n sessionName: this.sessionName,\n getAbortSignal: () => this._turnAbort.signal,\n getCurrentTurn: () => this._turn,\n });\n }\n\n /** Replace older turns with one summary message; keep tail within keepRecentTokens budget. */\n async compactHistory(opts?: { keepRecentTokens?: number }): Promise<{\n folded: boolean;\n beforeMessages: number;\n afterMessages: number;\n summaryChars: number;\n }> {\n return this.context.fold(this.model, opts);\n }\n\n appendAndPersist(message: ChatMessage): void {\n this.log.append(message);\n if (this.sessionName) {\n try {\n appendSessionMessage(this.sessionName, message);\n } catch {\n /* disk full or permission denied shouldn't kill the chat */\n }\n }\n }\n\n /** Swap the just-appended assistant entry — used by self-correction to restore the original tool_calls without dropping reasoning_content. */\n private replaceTailAssistantMessage(message: ChatMessage): void {\n const entries = this.log.entries;\n const tail = entries[entries.length - 1];\n if (!tail || tail.role !== \"assistant\") return;\n const kept = entries.slice(0, -1);\n kept.push(message);\n this.log.compactInPlace(kept);\n if (this.sessionName) {\n try {\n rewriteSession(this.sessionName, kept);\n } catch {\n /* disk issue shouldn't block the in-memory swap */\n }\n }\n }\n\n /** \"New chat\" — drops in-memory messages, archives the on-disk transcript so it survives in Sessions, keeps sessionName so the prefix cache stays warm. Re-runs the system-prompt builder if one was wired (issue #778: LUCKERR.md edits otherwise need a restart). */\n clearLog(): { dropped: number; archived: string | null; systemRebuilt: boolean } {\n const dropped = this.log.length;\n this.log.compactInPlace([]);\n let archived: string | null = null;\n if (this.sessionName) {\n try {\n archived = archiveSession(this.sessionName);\n if (archived === null) rewriteSession(this.sessionName, []);\n } catch {\n /* disk issue shouldn't block the in-memory clear */\n }\n }\n this.scratch.reset();\n this._inflight.clear();\n let systemRebuilt = false;\n if (this._rebuildSystem) {\n try {\n systemRebuilt = this.prefix.replaceSystem(this._rebuildSystem());\n } catch {\n /* builder threw — keep prior system rather than crash /new */\n }\n }\n return { dropped, archived, systemRebuilt };\n }\n\n configure(opts: ReconfigurableOptions): void {\n if (opts.model !== undefined) this.model = opts.model;\n if (opts.stream !== undefined) {\n this._streamPreference = opts.stream;\n this.stream = opts.stream;\n }\n if (opts.reasoningEffort !== undefined) this.reasoningEffort = opts.reasoningEffort;\n if (opts.autoEscalate !== undefined) this.autoEscalate = opts.autoEscalate;\n }\n\n /** `null` disables the cap; any change re-arms the 80% warning. */\n setBudget(usd: number | null): void {\n this.budgetUsd = typeof usd === \"number\" && usd > 0 ? usd : null;\n this._budgetWarned = false;\n }\n\n /** Single-turn upgrade consumed at next step() — distinct from `/preset max` (persistent). */\n armProForNextTurn(): void {\n this._proArmedForNextTurn = true;\n }\n /** Cancel `/pro` arming before the next turn starts. */\n disarmPro(): void {\n this._proArmedForNextTurn = false;\n }\n /** UI surface — true while `/pro` is queued but hasn't fired yet. */\n get proArmed(): boolean {\n return this._proArmedForNextTurn;\n }\n /** UI surface — true while the current turn is running on pro (armed or auto-escalated). */\n get escalatedThisTurn(): boolean {\n return this._escalateThisTurn;\n }\n\n /** UI surface — model id of the call about to run (or running) right now, including escalation. */\n get currentCallModel(): string {\n return this.modelForCurrentCall();\n }\n\n /** The escalation model for the current provider. */\n get escalationModel(): string {\n return this.client.provider.escalationModel ?? DEFAULT_ESCALATION_MODEL;\n }\n\n private modelForCurrentCall(): string {\n return this._escalateThisTurn ? this.escalationModel : this.model;\n }\n\n /** Returns true ONLY on the tipping call — caller surfaces a one-shot warning. */\n private noteToolFailureSignal(resultJson: string, repair?: RepairReport): boolean {\n if (!this._turnFailures.noteAndCrossedThreshold(resultJson, repair)) return false;\n if (this._escalateThisTurn || !this.autoEscalate) return false;\n this._escalateThisTurn = true;\n return true;\n }\n\n /** Returns true ONLY on the call where the read-only streak crosses the threshold (#681). */\n private noteReadOnlyToolCall(call: ToolCall): boolean {\n const isReadOnly = !this.isMutating(call);\n if (!this._readOnlyLoop.noteAndCrossedThreshold(isReadOnly)) return false;\n if (this._escalateThisTurn || !this.autoEscalate) return false;\n this._escalateThisTurn = true;\n return true;\n }\n\n /** A call counts as mutating when its definition reports `readOnly !== true` and any dynamic `readOnlyCheck` doesn't override that for these args. */\n private isMutating(call: ToolCall): boolean {\n const name = call.function?.name;\n if (!name) return false;\n const def = this.tools.get(name);\n if (!def) return false;\n if (def.readOnlyCheck) {\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(call.function?.arguments ?? \"{}\") ?? {};\n } catch {\n // Malformed args → fall through to the static flag below; the\n // dynamic check would've thrown anyway.\n }\n try {\n if (def.readOnlyCheck(args as never)) return false;\n } catch (err) {\n // Mirror tools.ts: surface buggy readOnlyCheck instead of silently\n // falling through to the static flag.\n process.stderr.write(`readOnlyCheck for ${name} threw: ${(err as Error).message}\\n`);\n }\n }\n return def.readOnly !== true;\n }\n\n private async runOneToolCall(\n call: ToolCall,\n signal: AbortSignal,\n ): Promise<{ preWarnings: LoopEvent[]; postWarnings: LoopEvent[]; result: string }> {\n const name = call.function?.name ?? \"\";\n const args = call.function?.arguments ?? \"{}\";\n const parsedArgs = safeParseToolArgs(args);\n this._inflight.add(this.inflightIdFor(call));\n try {\n const preReport = await runHooks({\n hooks: this.hooks,\n payload: {\n event: \"PreToolUse\",\n cwd: this.hookCwd,\n toolName: name,\n toolArgs: parsedArgs,\n },\n });\n const preWarnings = [...hookWarnings(preReport.outcomes, this._turn)];\n\n if (preReport.blocked) {\n const blocking = preReport.outcomes[preReport.outcomes.length - 1];\n const reason = (\n blocking?.stderr ||\n blocking?.stdout ||\n \"blocked by PreToolUse hook\"\n ).trim();\n return {\n preWarnings,\n postWarnings: [],\n result: `[hook block] ${blocking?.hook.command ?? \"<unknown>\"}\\n${reason}`,\n };\n }\n\n const result = await this.tools.dispatch(name, args, {\n signal,\n maxResultTokens: DEFAULT_MAX_RESULT_TOKENS,\n confirmationGate: this.confirmationGate,\n });\n\n const postReport = await runHooks({\n hooks: this.hooks,\n payload: {\n event: \"PostToolUse\",\n cwd: this.hookCwd,\n toolName: name,\n toolArgs: parsedArgs,\n toolResult: result,\n },\n });\n const postWarnings = [...hookWarnings(postReport.outcomes, this._turn)];\n\n return { preWarnings, postWarnings, result };\n } finally {\n this._inflight.delete(this.inflightIdFor(call));\n }\n }\n\n /** Stable per-call id used as the inflight key AND threaded into tool_start / tool events so the UI matches them up. */\n private inflightIdFor(call: ToolCall): string {\n if (call.id) return call.id;\n const fallback = (call as { _inflightFallback?: string })._inflightFallback;\n if (fallback) return fallback;\n const generated = `inflight-${++this._inflightCounter}`;\n (call as { _inflightFallback?: string })._inflightFallback = generated;\n return generated;\n }\n private _inflightCounter = 0;\n\n private buildMessages(pendingUser: string | null): ChatMessage[] {\n // DeepSeek 400s on either unpaired tool_calls or stray tool entries — heal before sending.\n const healed = healLoadedMessages(this.log.toMessages(), DEFAULT_MAX_RESULT_CHARS);\n const msgs: ChatMessage[] = [...this.prefix.toMessages(), ...healed.messages];\n if (pendingUser !== null) msgs.push({ role: \"user\", content: pendingUser });\n return msgs;\n }\n\n abort(): void {\n this._turnAbort.abort();\n }\n\n /** Drop the last user message + everything after; caller re-sends. Persists to session file. */\n retryLastUser(): string | null {\n const entries = this.log.entries;\n let lastUserIdx = -1;\n for (let i = entries.length - 1; i >= 0; i--) {\n if (entries[i]!.role === \"user\") {\n lastUserIdx = i;\n break;\n }\n }\n if (lastUserIdx < 0) return null;\n const raw = entries[lastUserIdx]!.content;\n const userText = typeof raw === \"string\" ? raw : \"\";\n const preserved = entries.slice(0, lastUserIdx).map((m) => ({ ...m }));\n this.log.compactInPlace(preserved);\n if (this.sessionName) {\n try {\n rewriteSession(this.sessionName, preserved);\n } catch {\n /* disk-full / perms — in-memory compaction still applies */\n }\n }\n return userText;\n }\n\n async *step(userInput: string): AsyncGenerator<LoopEvent> {\n // Budget gate runs FIRST, before any per-turn state mutation, so a\n // refusal leaves the loop unchanged and the user can correct the\n // cap and re-issue. Default `null` short-circuits the whole check\n // so the no-budget path is one comparison, no behavior delta.\n if (this.budgetUsd !== null) {\n const spent = this.stats.totalCost;\n if (spent >= this.budgetUsd) {\n yield {\n turn: this._turn,\n role: \"error\",\n content: \"\",\n error: t(\"loop.budgetExhausted\", {\n spent: spent.toFixed(4),\n cap: this.budgetUsd.toFixed(2),\n }),\n };\n return;\n }\n if (!this._budgetWarned && spent >= this.budgetUsd * 0.8) {\n this._budgetWarned = true;\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.budget80Pct\", {\n spent: spent.toFixed(4),\n cap: this.budgetUsd.toFixed(2),\n }),\n };\n }\n }\n this._turn++;\n this.scratch.reset();\n // A fresh user turn is a new intent — don't let StormBreaker's\n // old sliding window of (name, args) signatures keep blocking\n // calls that are now legitimately on-task. The window repopulates\n // naturally as this turn's tool calls flow through.\n this.repair.resetStorm();\n // Per-turn escalation state: reset both flags at turn start, then\n // consume the /pro armed flag into `_escalateThisTurn` (so the\n // armed intent is one-shot — next turn starts fresh on flash\n // unless the user re-arms or mid-turn escalation triggers).\n this._turnFailures.reset();\n this._readOnlyLoop.reset();\n this._turnSelfCorrected = false;\n this._escalateThisTurn = false;\n this._foldedThisTurn = false;\n this._toolDispatchesThisStep = 0;\n let armedConsumed = false;\n if (this._proArmedForNextTurn) {\n this._escalateThisTurn = true;\n this._proArmedForNextTurn = false;\n armedConsumed = true;\n }\n // Fresh controller for this turn: the prior step's signal has\n // already fired (or stayed clean); either way we don't want its\n // state to bleed into the new turn.\n //\n // Edge case — `loop.abort()` may have been called BEFORE step()\n // ran (race: caller fires abort during async setup, but step()\n // hadn't been awaited yet). Naively reassigning _turnAbort would\n // silently drop that abort. Forward the prior aborted state into\n // the fresh controller so the iter-0 check still bails out. This\n // is load-bearing for subagents: the parent's onParentAbort\n // listener calls childLoop.abort(), which can fire before\n // childLoop.step() has reached the `for await` line below.\n const carryAbort = this._turnAbort.signal.aborted;\n this._turnAbort = new AbortController();\n if (carryAbort) this._turnAbort.abort();\n const signal = this._turnAbort.signal;\n if (armedConsumed) {\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.proArmed\"),\n };\n }\n let pendingUser: string | null = userInput;\n const toolSpecs = this.prefix.tools();\n // 70% of the iter budget is the \"you're getting close\" threshold. We\n // only warn once per step so the user sees a single signal, not a\n // string of identical yellow lines stacked up.\n const warnAt = Math.max(1, Math.floor(this.maxToolIters * 0.7));\n let warnedForIterBudget = false;\n\n for (let iter = 0; iter < this.maxToolIters; iter++) {\n if (signal.aborted) {\n // Esc means \"stop now\" — not \"stop and force another 30-90s\n // reasoner call to produce a summary I didn't ask for\". The\n // user's mental model of cancel is immediate. We emit a\n // synthetic assistant_final (tagged forcedSummary so the\n // code-mode applier ignores it) with a short stopped\n // message, then done. The prior tool outputs are still in\n // the log if the user wants to continue — asking again\n // will hit a warm cache and be cheap.\n //\n // Budget / context-guard still call forceSummaryAfterIterLimit\n // because there the USER didn't choose to stop — we did —\n // and leaving them staring at nothing is worse than one extra\n // call.\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.abortedAtIter\", { iter, cap: this.maxToolIters }),\n };\n const stoppedMsg =\n \"[aborted by user (Esc) — no summary produced. Ask again or /retry when ready; prior tool output is still in the log.]\";\n // Synthetic assistant turn — no real model output exists. For\n // reasoner sessions R1 still demands `reasoning_content` on\n // every assistant message, so we attach an empty-string\n // placeholder to satisfy the validator without inventing\n // reasoning we don't have. V3 gets a plain message as before.\n this.appendAndPersist(buildSyntheticAssistantMessage(stoppedMsg, this.model));\n yield {\n turn: this._turn,\n role: \"assistant_final\",\n content: stoppedMsg,\n forcedSummary: true,\n };\n yield { turn: this._turn, role: \"done\", content: stoppedMsg };\n // Reset to a fresh, non-aborted controller before returning.\n // Without this the carry-abort logic above sees the still-\n // aborted controller on the NEXT step() entry and immediately\n // re-aborts at iter 0, locking the session: every subsequent\n // user message produces \"stopped without producing a summary\"\n // before any work happens. A user-initiated Esc is a discrete\n // event tied to ONE turn; it must not bleed into the next.\n // (The race scenario the carry-abort handles — abort fired in\n // the async window before step() entry — still works: a fresh\n // abort() between turns aborts the new controller below.)\n this._turnAbort = new AbortController();\n return;\n }\n // Bridge the silence between the PREVIOUS iter's tool result and\n // THIS iter's first streaming byte. R1 can spend 20-90s reasoning\n // about tool output before the first delta lands, and prior to\n // this hint the UI had nothing to render. Only emit on iter > 0\n // because iter 0's \"thinking\" phase is already covered by the\n // streaming row / StreamingAssistant's placeholder.\n //\n // Wording is explicit about the two things happening: the tool\n // result IS being uploaded (it's now part of the next prompt) and\n // the model IS thinking. Users were reading \"thinking about the\n // tool result\" as the model-only phase, but the wait also covers\n // the upload round-trip.\n if (iter > 0) {\n yield {\n turn: this._turn,\n role: \"status\",\n content: t(\"loop.toolUploadStatus\"),\n };\n }\n if (!warnedForIterBudget && iter >= warnAt) {\n warnedForIterBudget = true;\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.toolBudgetWarning\", { iter, cap: this.maxToolIters }),\n };\n }\n let messages = this.buildMessages(pendingUser);\n\n // Preflight context check. Local estimate of the outgoing payload\n // catches cases where prior usage didn't warn us (fresh resume, one\n // huge tool result). Above 95% we attempt a fold as a last resort —\n // it costs one summary call but stays cache-friendly. If the fold\n // can't shrink anything, we surface a warning and let the request\n // go (and likely 400) so the user knows to /clear.\n {\n const decision = this.context.decidePreflight(messages, this.prefix.toolSpecs, this.model);\n if (decision.needsAction) {\n const { estimateTokens: estimate, ctxMax } = decision;\n yield {\n turn: this._turn,\n role: \"status\",\n content: t(\"loop.preflightFoldStatus\"),\n };\n const result = await this.context.fold(this.model);\n if (result.folded) {\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.preflightFolded\", {\n estimate: estimate.toLocaleString(),\n ctxMax: ctxMax.toLocaleString(),\n pct: Math.round((estimate / ctxMax) * 100),\n beforeMessages: result.beforeMessages,\n afterMessages: result.afterMessages,\n summaryChars: result.summaryChars,\n }),\n };\n // Rebuild with the folded log so we send the smaller payload.\n messages = this.buildMessages(pendingUser);\n } else {\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.preflightNoFold\", {\n estimate: estimate.toLocaleString(),\n ctxMax: ctxMax.toLocaleString(),\n pct: Math.round((estimate / ctxMax) * 100),\n }),\n };\n }\n }\n }\n\n let assistantContent = \"\";\n let reasoningContent = \"\";\n let toolCalls: ToolCall[] = [];\n let usage: TurnStats[\"usage\"] | null = null;\n\n try {\n if (this.stream) {\n const callBuf: Map<number, ToolCall> = new Map();\n // Indices whose accumulated args have parsed as valid JSON at\n // least once. Purely informational — we don't dispatch until\n // the stream ends (that's the eager-dispatch feature we\n // intentionally punted) but the UI shows \"N ready\" so the\n // user sees progress on long multi-tool turns instead of a\n // stagnant \"building tool call\" spinner.\n const readyIndices = new Set<number>();\n const callModel = this.modelForCurrentCall();\n // Escalation-marker buffer: delay the first few assistant_delta\n // yields so a \"<<<NEEDS_PRO>>>\" lead-in never flashes on-screen\n // before we abort + retry. Only active on flash AND when the\n // user hasn't disabled auto-escalation (the `flash` preset\n // turns this off — model output flows through verbatim, no\n // marker handling). pro never requests its own escalation.\n const bufferForEscalation = this.autoEscalate && callModel !== this.escalationModel;\n let escalationBuf = \"\";\n let escalationBufFlushed = false;\n for await (const chunk of this.client.stream({\n model: callModel,\n messages,\n tools: toolSpecs.length ? toolSpecs : undefined,\n signal,\n thinking: thinkingModeForModel(callModel),\n reasoningEffort: this.reasoningEffort,\n })) {\n if (chunk.contentDelta) {\n assistantContent += chunk.contentDelta;\n if (bufferForEscalation && !escalationBufFlushed) {\n escalationBuf += chunk.contentDelta;\n // Early exit: marker matches — break and let the\n // post-call retry path take over. No delta was yielded\n // so the user sees nothing flicker.\n if (isEscalationRequest(escalationBuf)) {\n break;\n }\n // Flush once we have enough content to rule out the\n // marker (clearly not a partial match anymore, or past\n // the look-ahead window).\n if (\n escalationBuf.length >= NEEDS_PRO_BUFFER_CHARS ||\n !looksLikePartialEscalationMarker(escalationBuf)\n ) {\n escalationBufFlushed = true;\n yield {\n turn: this._turn,\n role: \"assistant_delta\",\n content: escalationBuf,\n };\n escalationBuf = \"\";\n }\n } else {\n yield {\n turn: this._turn,\n role: \"assistant_delta\",\n content: chunk.contentDelta,\n };\n }\n }\n if (chunk.reasoningDelta) {\n reasoningContent += chunk.reasoningDelta;\n yield {\n turn: this._turn,\n role: \"assistant_delta\",\n content: \"\",\n reasoningDelta: chunk.reasoningDelta,\n };\n }\n if (chunk.toolCallDelta) {\n const d = chunk.toolCallDelta;\n const cur = callBuf.get(d.index) ?? {\n id: d.id,\n type: \"function\" as const,\n function: { name: \"\", arguments: \"\" },\n };\n if (d.id) cur.id = d.id;\n if (d.name) cur.function.name = (cur.function.name ?? \"\") + d.name;\n if (d.argumentsDelta)\n cur.function.arguments = (cur.function.arguments ?? \"\") + d.argumentsDelta;\n callBuf.set(d.index, cur);\n\n // Mark this index \"ready\" once its args first parse as\n // valid JSON. JSON.parse is sub-millisecond on typical\n // tool-call payloads; skip the check once already ready.\n if (\n !readyIndices.has(d.index) &&\n cur.function.name &&\n looksLikeCompleteJson(cur.function.arguments ?? \"\")\n ) {\n readyIndices.add(d.index);\n }\n\n // Skip the id-only opener: name is empty until the next chunk.\n if (cur.function.name) {\n yield {\n turn: this._turn,\n role: \"tool_call_delta\",\n content: \"\",\n toolName: cur.function.name,\n toolCallArgsChars: (cur.function.arguments ?? \"\").length,\n toolCallIndex: d.index,\n toolCallReadyCount: readyIndices.size,\n };\n }\n }\n if (chunk.usage) usage = chunk.usage;\n }\n toolCalls = [...callBuf.values()];\n // Stream ended before the escalation buffer got flushed —\n // either a short response or a partial marker match. If the\n // buffer ISN'T the marker, flush it as the final delta so\n // the user sees it. Marker-match is handled post-call.\n if (bufferForEscalation && !escalationBufFlushed && escalationBuf.length > 0) {\n if (!isEscalationRequest(escalationBuf)) {\n yield {\n turn: this._turn,\n role: \"assistant_delta\",\n content: escalationBuf,\n };\n }\n }\n } else {\n const callModel = this.modelForCurrentCall();\n const resp = await this.client.chat({\n model: callModel,\n messages,\n tools: toolSpecs.length ? toolSpecs : undefined,\n signal,\n thinking: thinkingModeForModel(callModel),\n reasoningEffort: this.reasoningEffort,\n });\n assistantContent = resp.content;\n reasoningContent = resp.reasoningContent ?? \"\";\n toolCalls = resp.toolCalls;\n usage = resp.usage;\n }\n } catch (err) {\n // An aborted signal here is almost always our own doing —\n // either Esc, or App.tsx calling `loop.abort()` to switch to a\n // queued synthetic input (ShellConfirm \"always allow\", PlanConfirm\n // approve, etc.). The DeepSeek client's fetch path translates\n // the abort into a generic `AbortError(\"This operation was\n // aborted\")`, which used to bubble up here and render as a\n // scary red \"error\" row even though nothing actually broke.\n // Treat it as a clean early-exit instead: the next turn (queued\n // synthetic OR user re-prompt) starts immediately and gets to\n // produce its own answer.\n if (signal.aborted) {\n yield { turn: this._turn, role: \"done\", content: \"\" };\n // Reset the controller so the carry-abort check at the top of\n // the NEXT step() doesn't inherit this turn's aborted state.\n // Without this, a queued-submit triggered by App.tsx (e.g.\n // ShellConfirm \"run once\" → loop.abort() + setQueuedSubmit)\n // produces a spurious \"aborted at iter 0/64\" the moment the\n // synthetic message starts processing, locking the session.\n this._turnAbort = new AbortController();\n return;\n }\n const probe = is5xxError(err) ? await probeProviderReachable(this.client) : undefined;\n yield {\n turn: this._turn,\n role: \"error\",\n content: \"\",\n error: formatLoopError(err as Error, probe),\n };\n return;\n }\n\n // Self-reported escalation: the model (flash) emitted the\n // NEEDS_PRO marker as its lead-in. Abort this call's accounting,\n // flip the turn to pro, and re-enter the iter without advancing\n // the counter — next attempt runs on v4-pro with the same\n // messages. Only triggers when the call was on a model OTHER\n // than the escalation model; if the user already configured\n // v4-pro (via /preset max etc.), the marker is taken as a\n // no-op content and passed through verbatim, so there's no\n // infinite-retry loop.\n if (\n this.autoEscalate &&\n this.modelForCurrentCall() !== this.escalationModel &&\n isEscalationRequest(assistantContent)\n ) {\n const { reason } = parseEscalationMarker(assistantContent);\n this._escalateThisTurn = true;\n const reasonSuffix = reason ? ` — ${reason}` : \"\";\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.flashEscalation\", { model: this.escalationModel, reasonSuffix }),\n };\n // Reset per-iter state. We don't record stats for the rejected\n // flash call (cost is small — a ~20-token lead-in that we broke\n // out of early on streaming) — recording would attribute a\n // phantom call to the session total.\n assistantContent = \"\";\n reasoningContent = \"\";\n toolCalls = [];\n usage = null;\n // Redo this iter on pro — `iter--` cancels the `iter++` the\n // for loop runs on `continue`.\n iter--;\n continue;\n }\n\n // Attribute under the actual model used (escalated → pro, else\n // this.model) so cost/usage logs reflect reality.\n const turnStats = this.stats.record(\n this._turn,\n this.modelForCurrentCall(),\n usage ?? new Usage(),\n );\n\n // Commit the user turn to the log only on success of the first round-trip.\n if (pendingUser !== null) {\n this.appendAndPersist({ role: \"user\", content: pendingUser });\n pendingUser = null;\n }\n\n this.scratch.reasoning = reasoningContent || null;\n\n const { calls: repairedCalls, report } = this.repair.process(\n toolCalls,\n reasoningContent || null,\n assistantContent || null,\n );\n\n this.appendAndPersist(\n buildAssistantMessage(\n assistantContent,\n repairedCalls,\n this.modelForCurrentCall(),\n reasoningContent,\n ),\n );\n\n yield {\n turn: this._turn,\n role: \"assistant_final\",\n content: assistantContent,\n stats: turnStats,\n repair: report,\n };\n\n // Cost-aware escalation: repair fires (scavenge / truncation /\n // storm) are visible \"model struggled\" signals. Feed them into\n // the turn failure counter — if we hit the threshold, the\n // remainder of this turn's model calls use pro.\n if (this.noteToolFailureSignal(\"\", report)) {\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.autoEscalation\", {\n model: this.escalationModel,\n breakdown: this._turnFailures.formatBreakdown(),\n fallback: this.model,\n }),\n };\n }\n\n const allSuppressed =\n report.stormsBroken > 0 && repairedCalls.length === 0 && toolCalls.length > 0;\n\n // First all-suppressed storm: rewrite tail with the original tool_calls\n // (so the next prompt shows what was attempted), stub tool responses to\n // keep the API contract, and continue the iter — model gets one shot to\n // self-correct before the loud-warning path takes over.\n if (allSuppressed && !this._turnSelfCorrected) {\n this._turnSelfCorrected = true;\n this.replaceTailAssistantMessage(\n buildAssistantMessage(\n assistantContent,\n toolCalls,\n this.modelForCurrentCall(),\n reasoningContent,\n ),\n );\n for (const call of toolCalls) {\n this.appendAndPersist({\n role: \"tool\",\n tool_call_id: call.id ?? \"\",\n name: call.function?.name ?? \"\",\n content:\n \"[repeat-loop guard] this call was suppressed because it was identical to a previous call in this turn. Earlier results for it are above — try a meaningfully different approach, or stop and answer if you have enough.\",\n });\n }\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.repeatToolCallWarning\"),\n };\n continue;\n }\n\n if (report.stormsBroken > 0) {\n const noteTail = report.notes.length ? ` — ${report.notes[report.notes.length - 1]}` : \"\";\n const phrase = allSuppressed\n ? t(\"loop.stormStuck\")\n : t(\"loop.stormSuppressed\", { count: report.stormsBroken });\n yield {\n turn: this._turn,\n role: \"warning\",\n content: `${phrase}${noteTail}`,\n };\n }\n\n if (repairedCalls.length === 0) {\n if (allSuppressed) {\n yield* forceSummaryAfterIterLimit(this.summaryContext(), { reason: \"stuck\" });\n return;\n }\n yield { turn: this._turn, role: \"done\", content: assistantContent };\n return;\n }\n\n // Context-management decision after each turn's response.\n // ContextManager owns the policy; loop renders the events.\n const decision = this.context.decideAfterUsage(usage, this.model, this._foldedThisTurn);\n if (decision.kind === \"fold\") {\n this._foldedThisTurn = true;\n const before = decision.promptTokens;\n const ctxMax = decision.ctxMax;\n const aggressiveTag = decision.aggressive ? t(\"loop.aggressiveTag\") : \"\";\n yield {\n turn: this._turn,\n role: \"status\",\n content: t(\"loop.compactingHistoryStatus\", { aggressiveTag }),\n };\n const result = await this.compactHistory({ keepRecentTokens: decision.tailBudget });\n if (result.folded) {\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\n decision.aggressive ? \"loop.aggressivelyFoldedHistory\" : \"loop.foldedHistory\",\n {\n before: before.toLocaleString(),\n ctxMax: ctxMax.toLocaleString(),\n pct: Math.round((before / ctxMax) * 100),\n beforeMessages: result.beforeMessages,\n afterMessages: result.afterMessages,\n summaryChars: result.summaryChars,\n },\n ),\n };\n }\n } else if (decision.kind === \"exit-with-summary\") {\n const before = decision.promptTokens;\n const ctxMax = decision.ctxMax;\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.forcingSummary\", {\n before: before.toLocaleString(),\n ctxMax: ctxMax.toLocaleString(),\n pct: Math.round((before / ctxMax) * 100),\n }),\n };\n this.context.trimTrailingToolCalls();\n yield* forceSummaryAfterIterLimit(this.summaryContext(), { reason: \"context-guard\" });\n return;\n }\n\n const dispatchSerial =\n (process.env.LUCKERR_TOOL_DISPATCH ?? \"auto\").toLowerCase() === \"serial\";\n const parallelMaxParsed = Number.parseInt(process.env.LUCKERR_PARALLEL_MAX ?? \"\", 10);\n const parallelMax =\n Number.isFinite(parallelMaxParsed) && parallelMaxParsed >= 1\n ? Math.min(parallelMaxParsed, 16)\n : 3;\n\n let callIdx = 0;\n while (callIdx < repairedCalls.length) {\n // Group consecutive parallel-safe calls; an unsafe call breaks\n // the chunk and runs alone (serial barrier).\n const chunk: ToolCall[] = [];\n if (!dispatchSerial) {\n while (\n callIdx < repairedCalls.length &&\n chunk.length < parallelMax &&\n this.tools.isParallelSafe(repairedCalls[callIdx]?.function?.name ?? \"\")\n ) {\n chunk.push(repairedCalls[callIdx++]!);\n }\n }\n if (chunk.length === 0) {\n chunk.push(repairedCalls[callIdx++]!);\n }\n\n // tool_start announces every call in the chunk BEFORE any\n // dispatch awaits — TUI shows live indicators for each, and the\n // gap between assistant_final and the first tool_result yield is\n // never silent. Pre-add to the inflight set so the spinner is\n // already correct on the very first card render — runOneToolCall's\n // own add is then idempotent and its finally is the cleanup contract.\n for (const call of chunk) {\n const callId = this.inflightIdFor(call);\n this._inflight.add(callId);\n yield {\n turn: this._turn,\n role: \"tool_start\",\n content: \"\",\n toolName: call.function?.name ?? \"\",\n toolArgs: call.function?.arguments ?? \"{}\",\n callId,\n };\n }\n\n // Race the chunk; collect outcomes in declared order so history\n // append + tool yields are deterministic regardless of which\n // call settles first.\n const settled = await Promise.allSettled(chunk.map((c) => this.runOneToolCall(c, signal)));\n\n for (let k = 0; k < chunk.length; k++) {\n const call = chunk[k]!;\n const name = call.function?.name ?? \"\";\n const args = call.function?.arguments ?? \"{}\";\n const s = settled[k]!;\n\n let result: string;\n let preWarnings: LoopEvent[] = [];\n let postWarnings: LoopEvent[] = [];\n if (s.status === \"fulfilled\") {\n preWarnings = s.value.preWarnings;\n postWarnings = s.value.postWarnings;\n result = s.value.result;\n } else {\n const err = s.reason instanceof Error ? s.reason : new Error(String(s.reason));\n result = JSON.stringify({ error: `${err.name}: ${err.message}` });\n }\n\n for (const w of preWarnings) yield w;\n for (const w of postWarnings) yield w;\n\n this.appendAndPersist({\n role: \"tool\",\n tool_call_id: call.id ?? \"\",\n name,\n content: result,\n });\n\n if (this.noteToolFailureSignal(result)) {\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.autoEscalation\", {\n model: this.escalationModel,\n breakdown: this._turnFailures.formatBreakdown(),\n fallback: this.model,\n }),\n };\n }\n if (this.noteReadOnlyToolCall(call)) {\n yield {\n turn: this._turn,\n role: \"warning\",\n content: t(\"loop.readOnlyLoopEscalation\", {\n model: this.escalationModel,\n n: this._readOnlyLoop.currentStreak,\n fallback: this.model,\n }),\n };\n }\n\n yield {\n turn: this._turn,\n role: \"tool\",\n content: result,\n toolName: name,\n toolArgs: args,\n callId: this.inflightIdFor(call),\n };\n }\n }\n }\n\n // We exhausted the tool-call budget while the model still wanted to\n // call more tools. Rather than stopping silently (which leaves the\n // user staring at a blank prompt), force one final no-tools call so\n // the model must produce a text summary from everything it has\n // already seen.\n yield* forceSummaryAfterIterLimit(this.summaryContext(), { reason: \"budget\" });\n }\n\n private summaryContext(): ForceSummaryContext {\n return {\n client: this.client,\n signal: this._turnAbort.signal,\n buildMessages: () => this.buildMessages(null),\n appendAndPersist: (m) => this.appendAndPersist(m),\n recordStats: (model, usage) => this.stats.record(this._turn, model, usage),\n turn: this._turn,\n maxToolIters: this.maxToolIters,\n };\n }\n\n async run(userInput: string, onEvent?: (ev: LoopEvent) => void): Promise<string> {\n let final = \"\";\n for await (const ev of this.step(userInput)) {\n onEvent?.(ev);\n if (ev.role === \"assistant_final\") final = ev.content;\n if (ev.role === \"done\") break;\n }\n return final;\n }\n}\n\nfunction parsePositiveIntEnv(raw: string | undefined): number | undefined {\n if (!raw) return undefined;\n const n = Number.parseInt(raw, 10);\n return Number.isFinite(n) && n > 0 ? n : undefined;\n}\n\n/** Sane-range bounds for the flash→pro escalation threshold. */\nconst FAILURE_THRESHOLD_MIN = 1;\nconst FAILURE_THRESHOLD_MAX = 20;\n\nfunction resolveFailureThreshold(raw: number | undefined, fallback: number): number {\n if (raw === undefined) return fallback;\n if (!Number.isInteger(raw) || raw < FAILURE_THRESHOLD_MIN || raw > FAILURE_THRESHOLD_MAX) {\n process.stderr.write(\n `▲ ignoring escalation failureThreshold=${raw} (must be an integer in [${FAILURE_THRESHOLD_MIN},${FAILURE_THRESHOLD_MAX}]) — using default ${fallback}\\n`,\n );\n return fallback;\n }\n return raw;\n}\n","/** Expand `@path` mentions inline. Paths must resolve inside rootDir; escapes / oversize get a skip note, not content. */\n\nimport { type Dirent, existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { isAbsolute, join, relative, resolve } from \"node:path\";\nimport {\n type GitignoreLayer,\n ignoredByLayers,\n loadGitignoreAt,\n loadGitignoreAtSync,\n} from \"./gitignore.js\";\n\n/** Caps match tool-result dispatch truncation (0.5.2). */\nexport const DEFAULT_AT_MENTION_MAX_BYTES = 64 * 1024;\n\n/** Cap on entries returned for a `@<dir>` listing. ~200 paths × ~50 chars ≈ 10 KB — fits inside DEFAULT_AT_MENTION_MAX_BYTES with room for the rest of the prompt. */\nexport const DEFAULT_AT_DIR_MAX_ENTRIES = 200;\n\n/** Universally-uninteresting build / VCS dirs. Framework-specific dirs (Pods, target, …) live in .gitignore. */\nexport const DEFAULT_PICKER_IGNORE_DIRS: readonly string[] = [\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \"out\",\n \"coverage\",\n \".cache\",\n \".vscode\",\n \".idea\",\n \"target\",\n \".venv\",\n \"venv\",\n \"__pycache__\",\n];\n\nexport interface ListFilesOptions {\n /** Cap the walk once we've collected this many entries. Default 2000. */\n maxResults?: number;\n /** Directory names to skip entirely. Defaults to {@link DEFAULT_PICKER_IGNORE_DIRS}. */\n ignoreDirs?: readonly string[];\n /** Walk nested .gitignores (root + every subdir). Default true. */\n respectGitignore?: boolean;\n}\n\n/** Sync on purpose — fits the TUI's single-turn-per-tick model. Skips dot-DIRS but keeps dotfiles. */\nexport function listFilesSync(root: string, opts: ListFilesOptions = {}): string[] {\n return listFilesWithStatsSync(root, opts).map((e) => e.path);\n}\n\nexport interface FileWithStats {\n /** Relative path with forward-slash separator. */\n path: string;\n /** Modification time (Date.getTime() / ms since epoch). 0 when stat failed. */\n mtimeMs: number;\n}\n\n/** Stat failures kept as `mtimeMs: 0` — entry still appears, sinks to bottom of recency sort. */\nexport function listFilesWithStatsSync(root: string, opts: ListFilesOptions = {}): FileWithStats[] {\n const maxResults = Math.max(1, opts.maxResults ?? 2000);\n const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);\n const rootAbs = resolve(root);\n const respectGi = opts.respectGitignore !== false;\n const out: FileWithStats[] = [];\n\n const walk = (dirAbs: string, dirRel: string, layers: readonly GitignoreLayer[]) => {\n if (out.length >= maxResults) return;\n let effectiveLayers = layers;\n if (respectGi) {\n const ig = loadGitignoreAtSync(dirAbs);\n if (ig) effectiveLayers = [...layers, { dirAbs, ig }];\n }\n let entries: Dirent[];\n try {\n entries = readdirSync(dirAbs, { withFileTypes: true });\n } catch {\n return;\n }\n entries.sort((a, b) => a.name.localeCompare(b.name));\n for (const ent of entries) {\n if (out.length >= maxResults) return;\n const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;\n const absPath = join(dirAbs, ent.name);\n if (ent.isDirectory()) {\n if (ent.name.startsWith(\".\") || ignoreDirs.has(ent.name)) continue;\n if (ignoredByLayers(effectiveLayers, absPath, true)) continue;\n walk(absPath, relPath, effectiveLayers);\n } else if (ent.isFile()) {\n if (ignoredByLayers(effectiveLayers, absPath, false)) continue;\n let mtimeMs = 0;\n try {\n mtimeMs = statSync(absPath).mtimeMs;\n } catch {\n /* stat failed (permission / EAGAIN) — keep the entry with mtime=0 */\n }\n out.push({ path: relPath, mtimeMs });\n } else if (ent.isSymbolicLink()) {\n // Dirent.isFile() returns false for symlinks even when they point at\n // regular files — stat the target to recover them. Symlinks-to-dirs\n // are not followed (cycle risk).\n let target: ReturnType<typeof statSync> | null = null;\n try {\n target = statSync(absPath);\n } catch {\n continue;\n }\n if (!target.isFile()) continue;\n if (ignoredByLayers(effectiveLayers, absPath, false)) continue;\n out.push({ path: relPath, mtimeMs: target.mtimeMs });\n }\n }\n };\n\n walk(rootAbs, \"\", []);\n return out;\n}\n\n/** Parallel stat per directory — Windows stat syscalls are 3-5× slower than Linux. */\nexport async function listFilesWithStatsAsync(\n root: string,\n opts: ListFilesOptions = {},\n): Promise<FileWithStats[]> {\n const out: FileWithStats[] = [];\n const maxResults = Math.max(1, opts.maxResults ?? 2000);\n await walkFilesStream(root, {\n ...opts,\n onEntry: (e) => {\n out.push(e);\n return out.length < maxResults;\n },\n });\n return out;\n}\n\nexport interface StreamWalkOptions {\n ignoreDirs?: readonly string[];\n respectGitignore?: boolean;\n signal?: AbortSignal;\n /** Called per file entry. Return false to halt the walk. */\n onEntry: (entry: FileWithStats) => boolean | undefined;\n /** Called periodically with the running file-count. */\n onProgress?: (scanned: number) => void;\n /** Default 100ms — minimum gap between onProgress calls. */\n progressIntervalMs?: number;\n}\n\n/** Cancelable, streaming walker. Drives `listFilesWithStatsAsync` and the picker's search-mode walk. */\nexport async function walkFilesStream(\n root: string,\n opts: StreamWalkOptions,\n): Promise<{ scanned: number; cancelled: boolean }> {\n const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);\n const respectGi = opts.respectGitignore !== false;\n const rootAbs = resolve(root);\n const progressGap = Math.max(0, opts.progressIntervalMs ?? 100);\n let scanned = 0;\n let halted = false;\n let lastProgress = 0;\n\n const reportProgress = (force: boolean) => {\n if (!opts.onProgress) return;\n const now = Date.now();\n if (force || now - lastProgress >= progressGap) {\n lastProgress = now;\n opts.onProgress(scanned);\n }\n };\n\n const emit = (entry: FileWithStats) => {\n scanned++;\n if (halted) return;\n if (opts.onEntry(entry) === false) halted = true;\n reportProgress(false);\n };\n\n const walk = async (\n dirAbs: string,\n dirRel: string,\n layers: readonly GitignoreLayer[],\n ): Promise<void> => {\n if (halted || opts.signal?.aborted) return;\n let effectiveLayers = layers;\n if (respectGi) {\n const ig = await loadGitignoreAt(dirAbs);\n if (ig) effectiveLayers = [...layers, { dirAbs, ig }];\n }\n let entries: Dirent[];\n try {\n entries = await readdir(dirAbs, { withFileTypes: true });\n } catch {\n return;\n }\n entries.sort((a, b) => a.name.localeCompare(b.name));\n const fileEnts: Dirent[] = [];\n for (const ent of entries) {\n if (halted || opts.signal?.aborted) break;\n const absPath = join(dirAbs, ent.name);\n if (ent.isDirectory()) {\n if (ent.name.startsWith(\".\") || ignoreDirs.has(ent.name)) continue;\n if (ignoredByLayers(effectiveLayers, absPath, true)) continue;\n if (fileEnts.length > 0) {\n await flushFiles(fileEnts, dirAbs, dirRel, effectiveLayers, emit);\n fileEnts.length = 0;\n if (halted || opts.signal?.aborted) return;\n }\n await walk(absPath, dirRel ? `${dirRel}/${ent.name}` : ent.name, effectiveLayers);\n } else if (ent.isFile() || ent.isSymbolicLink()) {\n fileEnts.push(ent);\n }\n }\n if (fileEnts.length > 0 && !halted && !opts.signal?.aborted) {\n await flushFiles(fileEnts, dirAbs, dirRel, effectiveLayers, emit);\n }\n };\n\n await walk(rootAbs, \"\", []);\n reportProgress(true);\n return { scanned, cancelled: !!opts.signal?.aborted };\n}\n\nasync function flushFiles(\n ents: readonly Dirent[],\n dirAbs: string,\n dirRel: string,\n layers: readonly GitignoreLayer[],\n emit: (e: FileWithStats) => void,\n): Promise<void> {\n const accepted = ents.filter((e) => !ignoredByLayers(layers, join(dirAbs, e.name), false));\n const stats = await Promise.all(\n accepted.map((e) =>\n stat(join(dirAbs, e.name))\n .then((s) => ({ mtimeMs: s.mtimeMs, isFile: s.isFile() }))\n .catch(() => null),\n ),\n );\n for (let i = 0; i < accepted.length; i++) {\n const ent = accepted[i]!;\n const s = stats[i];\n if (ent.isSymbolicLink() && (!s || !s.isFile)) continue;\n emit({\n path: dirRel ? `${dirRel}/${ent.name}` : ent.name,\n mtimeMs: s?.mtimeMs ?? 0,\n });\n }\n}\n\nexport interface DirEntry {\n name: string;\n /** Relative-to-root path (forward slashes). For dirs, no trailing slash. */\n path: string;\n isDir: boolean;\n /** 0 for directories (no stat), real mtime for files. */\n mtimeMs: number;\n}\n\nexport interface ListDirectoryOptions {\n ignoreDirs?: readonly string[];\n respectGitignore?: boolean;\n}\n\n/** One-level browse for the @-picker. Folders first then files, alpha within each group. Resolves outside-root to []. */\nexport async function listDirectory(\n root: string,\n relDir: string,\n opts: ListDirectoryOptions = {},\n): Promise<DirEntry[]> {\n const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);\n const respectGi = opts.respectGitignore !== false;\n const rootAbs = resolve(root);\n const dirAbs = resolve(rootAbs, relDir);\n const rel = relative(rootAbs, dirAbs);\n if (rel.startsWith(\"..\") || isAbsolute(rel)) return [];\n\n const layers: GitignoreLayer[] = [];\n if (respectGi) {\n const segs = rel ? rel.split(/[\\\\/]/) : [];\n let cursor = rootAbs;\n const ig = await loadGitignoreAt(cursor);\n if (ig) layers.push({ dirAbs: cursor, ig });\n for (const seg of segs) {\n cursor = join(cursor, seg);\n const igSeg = await loadGitignoreAt(cursor);\n if (igSeg) layers.push({ dirAbs: cursor, ig: igSeg });\n }\n }\n\n let raw: Dirent[];\n try {\n raw = await readdir(dirAbs, { withFileTypes: true });\n } catch {\n return [];\n }\n const dirRel = rel.split(/[\\\\/]/).join(\"/\");\n const dirs: DirEntry[] = [];\n const files: Dirent[] = [];\n for (const ent of raw) {\n const absPath = join(dirAbs, ent.name);\n if (ent.isDirectory()) {\n if (ent.name.startsWith(\".\") || ignoreDirs.has(ent.name)) continue;\n if (ignoredByLayers(layers, absPath, true)) continue;\n dirs.push({\n name: ent.name,\n path: dirRel ? `${dirRel}/${ent.name}` : ent.name,\n isDir: true,\n mtimeMs: 0,\n });\n } else if (ent.isFile() || ent.isSymbolicLink()) {\n if (ignoredByLayers(layers, absPath, false)) continue;\n files.push(ent);\n }\n }\n const stats = await Promise.all(\n files.map((e) =>\n stat(join(dirAbs, e.name))\n .then((s) => ({ mtimeMs: s.mtimeMs, isFile: s.isFile() }))\n .catch(() => null),\n ),\n );\n const fileEntries: DirEntry[] = [];\n for (let i = 0; i < files.length; i++) {\n const ent = files[i]!;\n const s = stats[i];\n if (ent.isSymbolicLink() && (!s || !s.isFile)) continue;\n fileEntries.push({\n name: ent.name,\n path: dirRel ? `${dirRel}/${ent.name}` : ent.name,\n isDir: false,\n mtimeMs: s?.mtimeMs ?? 0,\n });\n }\n dirs.sort((a, b) => a.name.localeCompare(b.name));\n fileEntries.sort((a, b) => a.name.localeCompare(b.name));\n return [...dirs, ...fileEntries];\n}\n\nexport interface ParsedAtQuery {\n /** Directory portion (rel from root, no trailing slash). Empty = root. */\n dir: string;\n /** Filter portion — chars after the last slash. Empty if query ended in `/`. */\n filter: string;\n /** True if the query ended in `/` — caller knows to browse `dir`. */\n trailingSlash: boolean;\n}\n\n/** Split `src/auth/log` → `{dir: \"src/auth\", filter: \"log\"}`; trailing slash sets `trailingSlash` and clears filter. */\nexport function parseAtQuery(query: string): ParsedAtQuery {\n const normalized = query.replace(/\\\\/g, \"/\");\n const trailingSlash = normalized.endsWith(\"/\");\n const trimmed = trailingSlash ? normalized.slice(0, -1) : normalized;\n const lastSlash = trimmed.lastIndexOf(\"/\");\n if (trailingSlash) return { dir: trimmed, filter: \"\", trailingSlash: true };\n if (lastSlash < 0) return { dir: \"\", filter: trimmed, trailingSlash: false };\n return {\n dir: trimmed.slice(0, lastSlash),\n filter: trimmed.slice(lastSlash + 1),\n trailingSlash: false,\n };\n}\n\n/** Trailing-token only, anchored at end-of-input — distinct from `AT_MENTION_PATTERN` which scans all. `\\p{L}\\p{N}` for CJK and other non-ASCII filenames. */\nexport const AT_PICKER_PREFIX = /(?:^|\\s)@([\\p{L}\\p{N}_./\\\\-]*)$/u;\n\nexport function detectAtPicker(input: string): { query: string; atOffset: number } | null {\n const m = AT_PICKER_PREFIX.exec(input);\n if (!m) return null;\n const query = m[1] ?? \"\";\n // `m.index` is the offset of the capture group's SURROUNDING match —\n // which starts at either ^ or the preceding whitespace. The `@`\n // itself is at `end-of-input - query.length - 1`.\n const atOffset = input.length - query.length - 1;\n return { query, atOffset };\n}\n\n/** A candidate accepted by the picker ranker — either a bare path or a path with mtime. */\nexport type PickerCandidate = string | FileWithStats;\n\nexport interface RankPickerOptions {\n /** Upper bound on returned entries. Default 40. */\n limit?: number;\n recentlyUsed?: readonly string[];\n}\n\nexport function rankPickerCandidates(\n files: readonly PickerCandidate[],\n query: string,\n limitOrOpts?: number | RankPickerOptions,\n): string[] {\n const opts: RankPickerOptions =\n typeof limitOrOpts === \"number\" ? { limit: limitOrOpts } : (limitOrOpts ?? {});\n const limit = opts.limit ?? 40;\n const recent = new Set(opts.recentlyUsed ?? []);\n\n const entries: FileWithStats[] = files.map((f) =>\n typeof f === \"string\" ? { path: f, mtimeMs: 0 } : f,\n );\n\n if (!query) {\n // Only re-sort when we actually have signal to sort by. If input\n // is bare strings (mtime = 0 everywhere) AND there's no recent-\n // used list, preserve input order so callers keep their existing\n // layout. Passing FileWithStats or a non-empty recentlyUsed opts\n // you into mtime+recency ranking.\n const anyMtime = entries.some((e) => e.mtimeMs > 0);\n if (!anyMtime && recent.size === 0) {\n return entries.slice(0, limit).map((e) => e.path);\n }\n const sorted = [...entries].sort((a, b) => {\n const aRecent = recent.has(a.path) ? 1 : 0;\n const bRecent = recent.has(b.path) ? 1 : 0;\n if (aRecent !== bRecent) return bRecent - aRecent;\n if (a.mtimeMs !== b.mtimeMs) return b.mtimeMs - a.mtimeMs;\n return a.path.localeCompare(b.path);\n });\n return sorted.slice(0, limit).map((e) => e.path);\n }\n\n const needle = query.toLowerCase();\n const scored: Array<{ path: string; score: number; mtimeMs: number; recent: boolean }> = [];\n for (const e of entries) {\n const lower = e.path.toLowerCase();\n const hit = lower.indexOf(needle);\n if (hit >= 0) {\n const slash = lower.lastIndexOf(\"/\");\n const base = slash >= 0 ? lower.slice(slash + 1) : lower;\n let cls = 2;\n if (base.startsWith(needle)) cls = 0;\n else if (lower.startsWith(needle)) cls = 1;\n scored.push({\n path: e.path,\n score: cls * 10_000 + Math.min(hit, 9999),\n mtimeMs: e.mtimeMs,\n recent: recent.has(e.path),\n });\n continue;\n }\n const fuzzy = fuzzySubseqScore(needle, lower);\n if (fuzzy === null) continue;\n scored.push({\n path: e.path,\n score: 30_000 + fuzzy,\n mtimeMs: e.mtimeMs,\n recent: recent.has(e.path),\n });\n }\n scored.sort((a, b) => {\n if (a.score !== b.score) return a.score - b.score;\n // Tie-break: recently-used, then mtime (newer first).\n if (a.recent !== b.recent) return a.recent ? -1 : 1;\n return b.mtimeMs - a.mtimeMs;\n });\n return scored.slice(0, limit).map((s) => s.path);\n}\n\nfunction fuzzySubseqScore(needle: string, target: string): number | null {\n if (needle.length === 0) return 0;\n const slashIdx = target.lastIndexOf(\"/\");\n const basenameStart = slashIdx >= 0 ? slashIdx + 1 : 0;\n let qi = 0;\n let lastMatchIdx = -2;\n let consecutive = 0;\n let basenameMatches = 0;\n let totalGap = 0;\n for (let ti = 0; ti < target.length && qi < needle.length; ti++) {\n if (target[ti] !== needle[qi]) continue;\n if (ti === lastMatchIdx + 1) consecutive++;\n else if (lastMatchIdx >= 0) totalGap += ti - lastMatchIdx - 1;\n if (ti >= basenameStart) basenameMatches++;\n lastMatchIdx = ti;\n qi++;\n }\n if (qi < needle.length) return null;\n const quality = Math.max(0, totalGap - consecutive * 10 - basenameMatches * 5);\n const lengthPenalty = Math.floor(target.length / 4);\n return quality + lengthPenalty;\n}\n\n/** Word-boundary anchor rejects `@` embedded in emails / social handles; trailing `.` stripped before lookup. */\nexport const AT_MENTION_PATTERN = /(?<=^|\\s)@([\\p{L}\\p{N}_./\\\\-]+)/gu;\n\nexport interface AtMentionExpansion {\n /** The raw `@path` token as it appeared in the text. */\n token: string;\n /** The relative path, as resolved against rootDir. */\n path: string;\n /** True if the content was inlined. False = skipped (reason in `skip`). */\n ok: boolean;\n /** Bytes read (only for ok=true and isDirectory=false). */\n bytes?: number;\n /** True when the mention resolved to a directory (ok=true). Block uses `<directory>` instead of `<file>`. */\n isDirectory?: boolean;\n /** Number of files listed when isDirectory=true. */\n entries?: number;\n /** True iff the directory listing was clipped at maxDirEntries. */\n truncated?: boolean;\n /** Why the mention was skipped. Set when ok=false. */\n skip?: \"missing\" | \"not-file\" | \"too-large\" | \"escape\" | \"read-error\";\n}\n\nexport interface AtMentionOptions {\n /** Max file size in bytes before a mention is skipped. */\n maxBytes?: number;\n /** Cap on entries returned for a `@<dir>` listing. Default {@link DEFAULT_AT_DIR_MAX_ENTRIES}. */\n maxDirEntries?: number;\n fs?: {\n exists: (path: string) => boolean;\n isFile: (path: string) => boolean;\n /** Optional — when omitted, directories are skipped as `not-file`. */\n isDir?: (path: string) => boolean;\n /** Optional — receives the directory's absolute path and the project root, returns relative paths and a truncated flag. */\n listDir?: (\n dirAbs: string,\n root: string,\n max: number,\n ) => { files: string[]; truncated: boolean };\n size: (path: string) => number;\n read: (path: string) => string;\n };\n}\n\nexport function expandAtMentions(\n text: string,\n rootDir: string,\n opts: AtMentionOptions = {},\n): { text: string; expansions: AtMentionExpansion[] } {\n const maxBytes = opts.maxBytes ?? DEFAULT_AT_MENTION_MAX_BYTES;\n const maxDirEntries = Math.max(1, opts.maxDirEntries ?? DEFAULT_AT_DIR_MAX_ENTRIES);\n const fs = opts.fs ?? defaultFs;\n const root = resolve(rootDir);\n // De-dupe by token so `@file.ts` referenced twice inlines once.\n const seen = new Map<string, AtMentionExpansion>();\n const expansions: AtMentionExpansion[] = [];\n const dirListings = new Map<string, string[]>();\n\n for (const match of text.matchAll(AT_MENTION_PATTERN)) {\n const rawPath = match[1] ?? \"\";\n // Strip trailing dot (sentence terminator): `@foo.ts.` → `@foo.ts`.\n // Keep internal dots intact. Manual loop instead of `/\\.+$/` — the\n // regex is O(n²) on dot-heavy non-matches per CodeQL js/polynomial-redos.\n let cleaned = rawPath;\n while (cleaned.endsWith(\".\")) cleaned = cleaned.slice(0, -1);\n // Strip a single trailing slash so `@docs/` and `@docs` resolve identically.\n if (cleaned.endsWith(\"/\") || cleaned.endsWith(\"\\\\\")) cleaned = cleaned.slice(0, -1);\n if (!cleaned) continue;\n const token = `@${cleaned}`;\n if (seen.has(token)) continue;\n\n const expansion = resolveMention(cleaned, root, maxBytes, maxDirEntries, fs, dirListings);\n seen.set(token, expansion);\n expansions.push(expansion);\n }\n\n if (expansions.length === 0) return { text, expansions };\n\n // Build the trailing \"Referenced files\" block. Keep successful\n // inlines and skipped ones (with their reason) so the model sees\n // both what's here and what's missing.\n const blocks: string[] = [];\n for (const ex of expansions) {\n if (ex.ok && ex.isDirectory) {\n const files = dirListings.get(ex.path) ?? [];\n const truncAttr = ex.truncated ? ' truncated=\"true\"' : \"\";\n const body = files.length > 0 ? `\\n${files.join(\"\\n\")}\\n` : \"\\n\";\n blocks.push(\n `<directory path=\"${ex.path}\" entries=\"${ex.entries ?? files.length}\"${truncAttr}>${body}</directory>`,\n );\n } else if (ex.ok) {\n const content = readSafe(root, ex.path, fs);\n blocks.push(`<file path=\"${ex.path}\">\\n${content}\\n</file>`);\n } else {\n blocks.push(`<file path=\"${ex.path}\" skipped=\"${ex.skip}\" />`);\n }\n }\n const augmented = `${text}\\n\\n[Referenced files]\\n${blocks.join(\"\\n\\n\")}`;\n return { text: augmented, expansions };\n}\n\nfunction resolveMention(\n rawPath: string,\n root: string,\n maxBytes: number,\n maxDirEntries: number,\n fs: NonNullable<AtMentionOptions[\"fs\"]>,\n dirListings: Map<string, string[]>,\n): AtMentionExpansion {\n // Reject absolute paths — `@/etc/passwd` should not inline.\n if (isAbsolute(rawPath)) {\n return { token: `@${rawPath}`, path: rawPath, ok: false, skip: \"escape\" };\n }\n const resolved = resolve(root, rawPath);\n // Sandbox escape: after resolution the path must still be inside root.\n const rel = relative(root, resolved);\n if (rel.startsWith(\"..\") || isAbsolute(rel)) {\n return { token: `@${rawPath}`, path: rawPath, ok: false, skip: \"escape\" };\n }\n if (!fs.exists(resolved)) {\n return { token: `@${rawPath}`, path: rawPath, ok: false, skip: \"missing\" };\n }\n if (fs.isFile(resolved)) {\n const size = fs.size(resolved);\n if (size > maxBytes) {\n return { token: `@${rawPath}`, path: rawPath, ok: false, skip: \"too-large\", bytes: size };\n }\n return { token: `@${rawPath}`, path: rawPath, ok: true, bytes: size };\n }\n // Not a file — try the directory branch. listDir is optional; without it,\n // fall back to the legacy not-file skip so test fixtures don't break.\n if (fs.isDir?.(resolved) && fs.listDir) {\n const { files, truncated } = fs.listDir(resolved, root, maxDirEntries);\n dirListings.set(rawPath, files);\n return {\n token: `@${rawPath}`,\n path: rawPath,\n ok: true,\n isDirectory: true,\n entries: files.length,\n truncated,\n };\n }\n return { token: `@${rawPath}`, path: rawPath, ok: false, skip: \"not-file\" };\n}\n\nfunction readSafe(root: string, rawPath: string, fs: NonNullable<AtMentionOptions[\"fs\"]>): string {\n const resolved = resolve(root, rawPath);\n try {\n return fs.read(resolved);\n } catch {\n return \"(read failed)\";\n }\n}\n\nconst defaultFs: NonNullable<AtMentionOptions[\"fs\"]> = {\n exists: (p) => existsSync(p),\n isFile: (p) => {\n try {\n return statSync(p).isFile();\n } catch {\n return false;\n }\n },\n isDir: (p) => {\n try {\n return statSync(p).isDirectory();\n } catch {\n return false;\n }\n },\n listDir: (dirAbs, root, max) => {\n // Walk from project root and filter to entries under dirAbs so the\n // listing inherits the parent .gitignore layers. Walking dirAbs alone\n // would miss the project-root rules above it.\n const dirRel = relative(root, dirAbs).split(/[\\\\/]/).join(\"/\");\n const walkCap = Math.max(max * 4, 5000);\n const all = listFilesSync(root, { maxResults: walkCap });\n const prefix = dirRel ? `${dirRel}/` : \"\";\n const filtered = dirRel ? all.filter((f) => f === dirRel || f.startsWith(prefix)) : all;\n return {\n files: filtered.slice(0, max),\n truncated: filtered.length > max,\n };\n },\n size: (p) => {\n try {\n return statSync(p).size;\n } catch {\n return 0;\n }\n },\n read: (p) => readFileSync(p, \"utf8\"),\n};\n\nexport {\n AT_URL_PATTERN,\n DEFAULT_AT_URL_MAX_CHARS,\n expandAtUrls,\n stripUrlTail,\n} from \"./at-mentions-url.js\";\nexport type { AtUrlExpansion, AtUrlOptions } from \"./at-mentions-url.js\";\n","/** Nested .gitignore evaluation — shared by the at-mention picker walker and the semantic chunker. */\n\nimport { readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\n\nexport interface GitignoreLayer {\n /** Absolute dir the .gitignore lives in. Patterns evaluate relative to this. */\n dirAbs: string;\n ig: Ignore;\n}\n\nexport async function loadGitignoreAt(dirAbs: string): Promise<Ignore | null> {\n try {\n return ignore().add(await readFile(path.join(dirAbs, \".gitignore\"), \"utf8\"));\n } catch {\n return null;\n }\n}\n\nexport function loadGitignoreAtSync(dirAbs: string): Ignore | null {\n try {\n return ignore().add(readFileSync(path.join(dirAbs, \".gitignore\"), \"utf8\"));\n } catch {\n return null;\n }\n}\n\n/** True if any layer — outermost to innermost — ignores this path. */\nexport function ignoredByLayers(\n layers: readonly GitignoreLayer[],\n abs: string,\n isDir: boolean,\n): boolean {\n for (const layer of layers) {\n const rel = path.relative(layer.dirAbs, abs).split(path.sep).join(\"/\");\n if (!rel || rel.startsWith(\"..\")) continue;\n if (layer.ig.ignores(isDir ? `${rel}/` : rel)) return true;\n }\n return false;\n}\n","/** Reads LUCKERR.md → AGENTS.md → AGENT.md (first that exists); writes prefer the file already on disk. */\n\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\n\n/** Default WRITE target — created when no candidate exists yet. */\nexport const PROJECT_MEMORY_FILE = \"LUCKERR.md\";\n\n/** READ candidates, in priority order. AGENTS.md is the open spec at agents.md (Linux Foundation). */\nexport const PROJECT_MEMORY_FILES = [\"LUCKERR.md\", \"AGENTS.md\", \"AGENT.md\"] as const;\n\nexport const PROJECT_MEMORY_MAX_CHARS = 8000;\n\nconst FOREIGN_PLATFORM_FILE_MARKERS = [\"SOUL.md\", \"PERSONA.md\"] as const;\n\n/** Returns the marker(s) that flagged rootDir as a foreign agent-platform data dir; null on a normal coding project. */\nexport function detectForeignAgentPlatform(rootDir: string): string[] | null {\n const hits: string[] = [];\n for (const name of FOREIGN_PLATFORM_FILE_MARKERS) {\n if (existsSync(join(rootDir, name))) hits.push(name);\n }\n if (isDir(join(rootDir, \"skills\")) && isDir(join(rootDir, \"memories\"))) {\n hits.push(\"skills/ + memories/\");\n }\n return hits.length > 0 ? hits : null;\n}\n\nfunction isDir(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** Absolute path of the first PROJECT_MEMORY_FILES candidate that exists at rootDir, or null. */\nexport function findProjectMemoryPath(rootDir: string): string | null {\n for (const name of PROJECT_MEMORY_FILES) {\n const path = join(rootDir, name);\n if (existsSync(path)) return path;\n }\n return null;\n}\n\n/** Path callers should write to: an existing candidate wins, otherwise rootDir/LUCKERR.md. */\nexport function resolveProjectMemoryWritePath(rootDir: string): string {\n return findProjectMemoryPath(rootDir) ?? join(rootDir, PROJECT_MEMORY_FILE);\n}\n\nexport interface ProjectMemory {\n /** Absolute path the memory was read from. */\n path: string;\n /** Post-truncation content (may include a \"… (truncated N chars)\" marker). */\n content: string;\n /** Original byte length before truncation. */\n originalChars: number;\n /** True iff `originalChars > PROJECT_MEMORY_MAX_CHARS`. */\n truncated: boolean;\n}\n\n/** Empty / whitespace-only files return null so they don't perturb the cache prefix. */\nexport function readProjectMemory(rootDir: string): ProjectMemory | null {\n const path = findProjectMemoryPath(rootDir);\n if (!path) return null;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n const trimmed = raw.trim();\n if (!trimmed) return null;\n const originalChars = trimmed.length;\n const truncated = originalChars > PROJECT_MEMORY_MAX_CHARS;\n const content = truncated\n ? `${trimmed.slice(0, PROJECT_MEMORY_MAX_CHARS)}\\n… (truncated ${\n originalChars - PROJECT_MEMORY_MAX_CHARS\n } chars)`\n : trimmed;\n return { path, content, originalChars, truncated };\n}\n\nexport function memoryEnabled(): boolean {\n const env = process.env.LUCKERR_MEMORY;\n if (env === \"off\" || env === \"false\" || env === \"0\") return false;\n return true;\n}\n\n/** Deterministic — same memory file always yields the same prefix hash. */\nexport function applyProjectMemory(basePrompt: string, rootDir: string): string {\n if (!memoryEnabled()) return basePrompt;\n const mem = readProjectMemory(rootDir);\n if (!mem) return basePrompt;\n const filename = basename(mem.path);\n return `${basePrompt}\n\n# Project memory (${filename})\n\nThe user pinned these notes about this project — treat them as authoritative context for every turn:\n\n\\`\\`\\`\n${mem.content}\n\\`\\`\\`\n`;\n}\n","/** User-private memory pinned into the immutable prefix; distinct from committable LUCKERR.md. */\n\nimport { createHash } from \"node:crypto\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { type LuckerrConfig, memoryTypeDefaults } from \"../config.js\";\nimport { parseFrontmatter } from \"../frontmatter.js\";\nimport { applySkillsIndex } from \"../skills.js\";\nimport { applyProjectMemory, memoryEnabled } from \"./project.js\";\n\nexport const USER_MEMORY_DIR = \"memory\";\nexport const MEMORY_INDEX_FILE = \"MEMORY.md\";\n/** Cap on the index file content loaded into the prefix, per scope. */\nexport const MEMORY_INDEX_MAX_CHARS = 4000;\n\nexport const BUILTIN_MEMORY_TYPES = [\"user\", \"feedback\", \"project\", \"reference\"] as const;\nexport type BuiltinMemoryType = (typeof BUILTIN_MEMORY_TYPES)[number];\n/** Built-ins plus any string declared in `config.memory.customTypes`. Unknown values are accepted (round-tripped verbatim). */\nexport type MemoryType = BuiltinMemoryType | (string & {});\nexport type MemoryScope = \"global\" | \"project\";\nexport type MemoryPriority = \"low\" | \"medium\" | \"high\";\nexport type MemoryExpires = \"project_end\";\n\nexport interface MemoryEntry {\n name: string;\n type: MemoryType;\n scope: MemoryScope;\n description: string;\n body: string;\n /** ISO date string (YYYY-MM-DD). */\n createdAt: string;\n /** Explicit per-entry priority; absent → resolve from config default for `type`, else \"medium\". */\n priority?: MemoryPriority;\n /** Lifecycle hint. `project_end` → cleared by `/memory clear project`. */\n expires?: MemoryExpires;\n}\n\nexport interface MemoryStoreOptions {\n /** Override `~/.luckerr` — tests set this to a tmpdir. */\n homeDir?: string;\n /** Absolute sandbox root. Required to use `scope: \"project\"`. */\n projectRoot?: string;\n}\n\nexport interface WriteInput {\n name: string;\n type: MemoryType;\n scope: MemoryScope;\n description: string;\n body: string;\n priority?: MemoryPriority;\n expires?: MemoryExpires;\n}\n\nconst VALID_NAME = /^[a-zA-Z0-9_-][a-zA-Z0-9_.-]{1,38}[a-zA-Z0-9]$/;\n\n/** Throws on path-injection (../, /, leading dot). Allowed: 3-40 chars, alnum/_/-, interior `.`. */\nexport function sanitizeMemoryName(raw: string): string {\n const trimmed = String(raw ?? \"\").trim();\n if (!VALID_NAME.test(trimmed)) {\n throw new Error(\n `invalid memory name: ${JSON.stringify(raw)} — must be 3-40 chars, alnum/_/-, no path separators`,\n );\n }\n return trimmed;\n}\n\n/** Stable 16-hex-char hash of an absolute sandbox root path. */\nexport function projectHash(rootDir: string): string {\n const abs = resolve(rootDir);\n return createHash(\"sha1\").update(abs).digest(\"hex\").slice(0, 16);\n}\n\nfunction scopeDir(opts: { homeDir: string; scope: MemoryScope; projectRoot?: string }): string {\n if (opts.scope === \"global\") {\n return join(opts.homeDir, USER_MEMORY_DIR, \"global\");\n }\n if (!opts.projectRoot) {\n throw new Error(\"scope=project requires a projectRoot on MemoryStore\");\n }\n return join(opts.homeDir, USER_MEMORY_DIR, projectHash(opts.projectRoot));\n}\n\nfunction ensureDir(p: string): void {\n if (!existsSync(p)) mkdirSync(p, { recursive: true });\n}\n\nfunction formatFrontmatter(e: WriteInput & { createdAt: string }): string {\n const lines = [\n \"---\",\n `name: ${e.name}`,\n `description: ${e.description.replace(/\\n/g, \" \")}`,\n `type: ${e.type}`,\n `scope: ${e.scope}`,\n `created: ${e.createdAt}`,\n ];\n if (e.priority) lines.push(`priority: ${e.priority}`);\n if (e.expires) lines.push(`expires: ${e.expires}`);\n lines.push(\"---\", \"\");\n return lines.join(\"\\n\");\n}\n\nfunction coercePriority(v: unknown): MemoryPriority | undefined {\n return v === \"low\" || v === \"medium\" || v === \"high\" ? v : undefined;\n}\n\nfunction coerceExpires(v: unknown): MemoryExpires | undefined {\n return v === \"project_end\" ? v : undefined;\n}\n\nfunction todayIso(): string {\n const d = new Date();\n return d.toISOString().slice(0, 10);\n}\n\nfunction indexLine(e: Pick<MemoryEntry, \"name\" | \"description\">): string {\n const safeDesc = e.description.replace(/\\n/g, \" \").trim();\n const max = 130 - e.name.length;\n const clipped = safeDesc.length > max ? `${safeDesc.slice(0, Math.max(1, max - 1))}…` : safeDesc;\n return `- [${e.name}](${e.name}.md) — ${clipped}`;\n}\n\nexport class MemoryStore {\n private readonly homeDir: string;\n private readonly projectRoot: string | undefined;\n\n constructor(opts: MemoryStoreOptions = {}) {\n this.homeDir = opts.homeDir ?? join(homedir(), \".luckerr\");\n this.projectRoot = opts.projectRoot ? resolve(opts.projectRoot) : undefined;\n }\n\n /** Directory this store writes `scope` files into, creating it if needed. */\n dir(scope: MemoryScope): string {\n const d = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });\n ensureDir(d);\n return d;\n }\n\n /** Absolute path to a memory file (no existence check). */\n pathFor(scope: MemoryScope, name: string): string {\n return join(this.dir(scope), `${sanitizeMemoryName(name)}.md`);\n }\n\n /** True iff this store is configured with a project scope available. */\n hasProjectScope(): boolean {\n return this.projectRoot !== undefined;\n }\n\n loadIndex(\n scope: MemoryScope,\n ): { content: string; originalChars: number; truncated: boolean } | null {\n if (scope === \"project\" && !this.projectRoot) return null;\n const file = join(\n scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot }),\n MEMORY_INDEX_FILE,\n );\n if (!existsSync(file)) return null;\n let raw: string;\n try {\n raw = readFileSync(file, \"utf8\");\n } catch {\n return null;\n }\n const trimmed = raw.trim();\n if (!trimmed) return null;\n const originalChars = trimmed.length;\n const truncated = originalChars > MEMORY_INDEX_MAX_CHARS;\n const content = truncated\n ? `${trimmed.slice(0, MEMORY_INDEX_MAX_CHARS)}\\n… (truncated ${originalChars - MEMORY_INDEX_MAX_CHARS} chars)`\n : trimmed;\n return { content, originalChars, truncated };\n }\n\n /** Read one memory file's body (frontmatter stripped). Throws if missing. */\n read(scope: MemoryScope, name: string): MemoryEntry {\n const file = this.pathFor(scope, name);\n if (!existsSync(file)) {\n throw new Error(`memory not found: scope=${scope} name=${name}`);\n }\n const raw = readFileSync(file, \"utf8\");\n const { data, body } = parseFrontmatter(raw);\n const entry: MemoryEntry = {\n name: data.name ?? name,\n type: (data.type as MemoryType) ?? \"project\",\n scope: (data.scope as MemoryScope) ?? scope,\n description: data.description ?? \"\",\n body: body.trim(),\n createdAt: data.created ?? \"\",\n };\n const priority = coercePriority(data.priority);\n if (priority) entry.priority = priority;\n const expires = coerceExpires(data.expires);\n if (expires) entry.expires = expires;\n return entry;\n }\n\n /** Skips malformed files — index stays queryable even if one file is hand-edited into nonsense. */\n list(): MemoryEntry[] {\n const out: MemoryEntry[] = [];\n const scopes: MemoryScope[] = this.projectRoot ? [\"global\", \"project\"] : [\"global\"];\n for (const scope of scopes) {\n const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });\n if (!existsSync(dir)) continue;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (entry === MEMORY_INDEX_FILE) continue;\n if (!entry.endsWith(\".md\")) continue;\n const name = entry.slice(0, -3);\n try {\n out.push(this.read(scope, name));\n } catch {\n // malformed file — skip rather than fail the whole list\n }\n }\n }\n return out;\n }\n\n write(input: WriteInput): string {\n if (input.scope === \"project\" && !this.projectRoot) {\n throw new Error(\"cannot write project-scoped memory: no projectRoot configured\");\n }\n const name = sanitizeMemoryName(input.name);\n const desc = String(input.description ?? \"\").trim();\n if (!desc) throw new Error(\"memory description cannot be empty\");\n const body = String(input.body ?? \"\").trim();\n if (!body) throw new Error(\"memory body cannot be empty\");\n const entry: WriteInput & { createdAt: string } = {\n ...input,\n name,\n description: desc,\n body,\n createdAt: todayIso(),\n };\n if (input.priority) entry.priority = input.priority;\n if (input.expires) entry.expires = input.expires;\n const dir = this.dir(input.scope);\n const file = join(dir, `${name}.md`);\n const content = `${formatFrontmatter(entry)}${body}\\n`;\n writeFileSync(file, content, \"utf8\");\n this.regenerateIndex(input.scope);\n return file;\n }\n\n /** Delete one memory + its index line. No-op if the file is already gone. */\n delete(scope: MemoryScope, rawName: string): boolean {\n if (scope === \"project\" && !this.projectRoot) {\n throw new Error(\"cannot delete project-scoped memory: no projectRoot configured\");\n }\n const file = this.pathFor(scope, rawName);\n if (!existsSync(file)) return false;\n unlinkSync(file);\n this.regenerateIndex(scope);\n return true;\n }\n\n /** Sorted by name — same file set must produce byte-identical MEMORY.md for stable prefix hashing. */\n private regenerateIndex(scope: MemoryScope): void {\n const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });\n if (!existsSync(dir)) return;\n let files: string[];\n try {\n files = readdirSync(dir);\n } catch {\n return;\n }\n const mdFiles = files\n .filter((f) => f !== MEMORY_INDEX_FILE && f.endsWith(\".md\"))\n .sort((a, b) => a.localeCompare(b));\n const indexPath = join(dir, MEMORY_INDEX_FILE);\n if (mdFiles.length === 0) {\n if (existsSync(indexPath)) unlinkSync(indexPath);\n return;\n }\n const lines: string[] = [];\n for (const f of mdFiles) {\n const name = f.slice(0, -3);\n try {\n const entry = this.read(scope, name);\n lines.push(indexLine({ name: entry.name || name, description: entry.description }));\n } catch {\n // Malformed: still surface it in the index so the user notices.\n lines.push(`- [${name}](${name}.md) — (malformed, check frontmatter)`);\n }\n }\n writeFileSync(indexPath, `${lines.join(\"\\n\")}\\n`, \"utf8\");\n }\n}\n\n/** Freeform `#g` destination, distinct from MEMORY.md's curated index of named files. */\nexport function readGlobalLuckerrMemory(\n homeDir: string = join(homedir(), \".luckerr\"),\n): { path: string; content: string; originalChars: number; truncated: boolean } | null {\n const path = join(homeDir, \"LUCKERR.md\");\n if (!existsSync(path)) return null;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n const trimmed = raw.trim();\n if (!trimmed) return null;\n const originalChars = trimmed.length;\n // Reuse the project-memory cap so both freeform files have the same\n // headroom (8000 chars ≈ 2k tokens). They serve the same purpose at\n // different scopes.\n const truncated = originalChars > 8000;\n const content = truncated\n ? `${trimmed.slice(0, 8000)}\\n… (truncated ${originalChars - 8000} chars)`\n : trimmed;\n return { path, content, originalChars, truncated };\n}\n\nexport function applyGlobalLuckerrMemory(basePrompt: string, homeDir?: string): string {\n if (!memoryEnabled()) return basePrompt;\n const dir = homeDir ?? join(homedir(), \".luckerr\");\n const mem = readGlobalLuckerrMemory(dir);\n if (!mem) return basePrompt;\n return [\n basePrompt,\n \"\",\n \"# Global memory (~/.luckerr/LUCKERR.md)\",\n \"\",\n \"Cross-project notes the user pinned via the `#g` prompt prefix. Treat as authoritative — same level of trust as project memory.\",\n \"\",\n \"```\",\n mem.content,\n \"```\",\n ].join(\"\\n\");\n}\n\n/** Effective priority: entry's own field wins, else the config default for its type, else undefined. */\nexport function effectivePriority(\n entry: MemoryEntry,\n cfg?: LuckerrConfig,\n): MemoryPriority | undefined {\n if (entry.priority) return entry.priority;\n return memoryTypeDefaults(entry.type, cfg).priority;\n}\n\nfunction highPriorityBlock(entries: MemoryEntry[], cfg?: LuckerrConfig): string | null {\n const high = entries.filter((e) => effectivePriority(e, cfg) === \"high\");\n if (high.length === 0) return null;\n const lines: string[] = [\n \"# HIGH PRIORITY constraints (must observe)\",\n \"\",\n \"These memories were declared `priority: high` (via config.memory.customTypes or the memory file itself). Treat them as hard rules — violations override any other guidance below.\",\n \"\",\n ];\n for (const e of high) {\n const head = `!!! [${e.scope}/${e.type}/${e.name}] ${e.description || \"(no description)\"}`;\n lines.push(head);\n if (e.body) lines.push(\"\", e.body);\n lines.push(\"\");\n }\n return lines.join(\"\\n\").trimEnd();\n}\n\n/** Empty index → omit the whole block (otherwise we'd add bytes to the prefix hash for nothing). */\nexport function applyUserMemory(\n basePrompt: string,\n opts: { homeDir?: string; projectRoot?: string; cfg?: LuckerrConfig } = {},\n): string {\n if (!memoryEnabled()) return basePrompt;\n const store = new MemoryStore(opts);\n const global = store.loadIndex(\"global\");\n const project = store.hasProjectScope() ? store.loadIndex(\"project\") : null;\n const high = highPriorityBlock(store.list(), opts.cfg);\n if (!global && !project && !high) return basePrompt;\n const parts: string[] = [basePrompt];\n if (high) parts.push(\"\", high);\n if (global) {\n parts.push(\n \"\",\n \"# User memory — global (~/.luckerr/memory/global/MEMORY.md)\",\n \"\",\n \"Cross-project facts and preferences the user has told you in prior sessions. TREAT AS AUTHORITATIVE — don't re-verify via filesystem or web. One-liners index detail files; call `recall_memory` for full bodies only when the one-liner isn't enough.\",\n \"\",\n \"```\",\n global.content,\n \"```\",\n );\n }\n if (project) {\n parts.push(\n \"\",\n \"# User memory — this project\",\n \"\",\n \"Per-project facts the user established in prior sessions (not committed to the repo). TREAT AS AUTHORITATIVE. Same recall pattern as global memory.\",\n \"\",\n \"```\",\n project.content,\n \"```\",\n );\n }\n return parts.join(\"\\n\");\n}\n\nexport function applyMemoryStack(basePrompt: string, rootDir: string): string {\n const withProject = applyProjectMemory(basePrompt, rootDir);\n const withGlobal = applyGlobalLuckerrMemory(withProject);\n const withMemory = applyUserMemory(withGlobal, { projectRoot: rootDir });\n return applySkillsIndex(withMemory, { projectRoot: rootDir });\n}\n","/** Tiny YAML-frontmatter parser shared by skills / memory loaders. Single source so BOM + folded + quoted handling stay consistent. */\n\nconst KEY_RE = /^([a-zA-Z_][a-zA-Z0-9_-]*):\\s*(.*)$/;\n/** Bracket-write guard — regex permits these as identifiers, but writing them would mutate Object.prototype. */\nconst FORBIDDEN_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nfunction stripQuotes(s: string): string {\n if (s.length < 2) return s;\n const first = s[0];\n const last = s[s.length - 1];\n if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n return s.slice(1, -1);\n }\n return s;\n}\n\nexport function parseFrontmatter(raw: string): { data: Record<string, string>; body: string } {\n const stripped = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;\n const lines = stripped.split(/\\r?\\n/);\n if (lines[0] !== \"---\") return { data: {}, body: stripped };\n const end = lines.indexOf(\"---\", 1);\n if (end < 0) return { data: {}, body: stripped };\n const entries = new Map<string, string>();\n let currentKey: string | null = null;\n for (let i = 1; i < end; i++) {\n const line = lines[i] ?? \"\";\n if (line.trim() === \"\") {\n currentKey = null;\n continue;\n }\n const m = line.match(KEY_RE);\n if (m?.[1] && !FORBIDDEN_KEYS.has(m[1])) {\n currentKey = m[1];\n entries.set(currentKey, (m[2] ?? \"\").trim());\n } else if (currentKey) {\n const cont = line.trim();\n const prev = entries.get(currentKey) ?? \"\";\n entries.set(currentKey, prev ? `${prev} ${cont}` : cont);\n }\n }\n const data: Record<string, string> = Object.create(null);\n for (const [k, v] of entries) {\n if (FORBIDDEN_KEYS.has(k)) continue;\n data[k] = stripQuotes(v);\n }\n return {\n data,\n body: lines\n .slice(end + 1)\n .join(\"\\n\")\n .replace(/^\\n+/, \"\"),\n };\n}\n","/** Project scope wins over global. Only names+descriptions enter the prefix; bodies load lazily into the append-only log. */\n\nimport { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { parseFrontmatter } from \"./frontmatter.js\";\nimport { NEGATIVE_CLAIM_RULE, TUI_FORMATTING_RULES } from \"./prompt-fragments.js\";\n\nexport const SKILLS_DIRNAME = \"skills\";\nexport const SKILL_FILE = \"SKILL.md\";\n/** Cap on the pinned skills-index block, mirrors memory-index cap. */\nexport const SKILLS_INDEX_MAX_CHARS = 4000;\n/** Skill identifier shape — alnum + `_` + `-` + interior `.`, 1-64 chars. */\nconst VALID_SKILL_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;\n\nexport type SkillScope = \"project\" | \"global\" | \"builtin\";\n\n/** inline = body enters parent log; subagent = isolated child loop, only final answer returns. */\nexport type SkillRunAs = \"inline\" | \"subagent\";\n\nexport interface Skill {\n /** Canonical name — sanitized, matches the directory / filename stem. */\n name: string;\n /** One-line description shown in the pinned index. */\n description: string;\n /** Full markdown body (post-frontmatter). Loaded on demand. */\n body: string;\n /** Which scope this skill was loaded from. */\n scope: SkillScope;\n /** Absolute path to the SKILL.md (or {name}.md) file, or \"(builtin)\" for shipped defaults. */\n path: string;\n /** Parsed `allowed-tools` frontmatter — when present, the spawned subagent's registry is scoped to these literal tool names. */\n allowedTools?: readonly string[];\n runAs: SkillRunAs;\n /** Subagent model override; only meaningful when `runAs === \"subagent\"`. */\n model?: string;\n /** Subagent tool-call budget; only meaningful when `runAs === \"subagent\"`. Clamped to [1, 32]. */\n maxToolIters?: number;\n}\n\nexport interface SkillStoreOptions {\n /** Override `$HOME` — tests point this at a tmpdir. */\n homeDir?: string;\n /** Required for project-scope skills; omit to read only the global scope. */\n projectRoot?: string;\n /** Suppress bundled built-ins — for tests asserting exact list contents. */\n disableBuiltins?: boolean;\n}\n\n/** Reject skill files that would silently disappear from the prefix index — `description:` is what `applySkillsIndex` keys on. */\nexport function validateSkillFrontmatter(raw: string): { ok: true } | { error: string } {\n const { data } = parseFrontmatter(raw);\n const desc = (data.description ?? \"\").trim();\n if (!desc) {\n return {\n error:\n 'skill frontmatter is missing a non-empty \"description:\" line — without it the skill will not appear in the model\\'s skills index',\n };\n }\n return { ok: true };\n}\n\nfunction isValidSkillName(name: string): boolean {\n return VALID_SKILL_NAME.test(name);\n}\n\nfunction parseAllowedTools(raw: string | undefined): readonly string[] | undefined {\n if (raw === undefined) return undefined;\n const names = raw\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n return names.length > 0 ? Object.freeze(names) : undefined;\n}\n\n/** Match subagent's own bounds (src/tools/subagent.ts MIN_MAX_ITERS / MAX_MAX_ITERS). */\nconst SKILL_MAX_ITERS_MIN = 1;\nconst SKILL_MAX_ITERS_MAX = 32;\n\nfunction parseMaxToolIters(raw: string | undefined): number | undefined {\n if (raw === undefined) return undefined;\n const n = Number.parseInt(raw.trim(), 10);\n if (!Number.isFinite(n)) return undefined;\n return Math.min(SKILL_MAX_ITERS_MAX, Math.max(SKILL_MAX_ITERS_MIN, n));\n}\n\nexport class SkillStore {\n private readonly homeDir: string;\n private readonly projectRoot: string | undefined;\n private readonly disableBuiltins: boolean;\n\n constructor(opts: SkillStoreOptions = {}) {\n this.homeDir = opts.homeDir ?? homedir();\n this.projectRoot = opts.projectRoot ? resolve(opts.projectRoot) : undefined;\n this.disableBuiltins = opts.disableBuiltins === true;\n }\n\n /** True iff this store was configured with a project root. */\n hasProjectScope(): boolean {\n return this.projectRoot !== undefined;\n }\n\n /** Project scope first so per-repo skill overrides a global with the same name. */\n roots(): Array<{ dir: string; scope: SkillScope }> {\n const out: Array<{ dir: string; scope: SkillScope }> = [];\n if (this.projectRoot) {\n out.push({\n dir: join(this.projectRoot, \".luckerr\", SKILLS_DIRNAME),\n scope: \"project\",\n });\n }\n out.push({ dir: join(this.homeDir, \".luckerr\", SKILLS_DIRNAME), scope: \"global\" });\n return out;\n }\n\n /** Higher-priority root wins on collision (project > global > builtin); sorted for stable prefix hash. */\n list(): Skill[] {\n const byName = new Map<string, Skill>();\n for (const { dir, scope } of this.roots()) {\n if (!existsSync(dir)) continue;\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n const skill = this.readEntry(dir, scope, entry);\n if (!skill) continue;\n if (!byName.has(skill.name)) byName.set(skill.name, skill);\n }\n }\n // Builtins last so user/project files override on name collision.\n if (!this.disableBuiltins) {\n for (const skill of BUILTIN_SKILLS) {\n if (!byName.has(skill.name)) byName.set(skill.name, skill);\n }\n }\n return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /** Scaffold a new skill stub at the chosen scope. Refuses to overwrite. */\n create(name: string, scope: \"project\" | \"global\"): { path: string } | { error: string } {\n return this.createWithContent(name, scope, skillStubBody(name));\n }\n\n /** Like `create` but writes caller-supplied file contents instead of the stub — used by the scaffold tool. */\n createWithContent(\n name: string,\n scope: \"project\" | \"global\",\n content: string,\n ): { path: string } | { error: string } {\n if (!isValidSkillName(name)) {\n return { error: `invalid skill name: \"${name}\" — use letters, digits, _, -, .` };\n }\n if (scope === \"project\" && !this.projectRoot) {\n return { error: \"project scope requires a workspace — run from `luckerr code`\" };\n }\n const root =\n scope === \"project\"\n ? join(this.projectRoot ?? \"\", \".luckerr\", SKILLS_DIRNAME)\n : join(this.homeDir, \".luckerr\", SKILLS_DIRNAME);\n const flat = join(root, `${name}.md`);\n const folder = join(root, name, SKILL_FILE);\n if (existsSync(folder)) {\n return { error: `skill \"${name}\" already exists at ${folder}` };\n }\n mkdirSync(dirname(flat), { recursive: true });\n try {\n writeFileSync(flat, content, { encoding: \"utf8\", flag: \"wx\" });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"EEXIST\") {\n return { error: `skill \"${name}\" already exists at ${flat}` };\n }\n throw err;\n }\n return { path: flat };\n }\n\n /** Resolve one skill by name. Returns `null` if not found or malformed. */\n read(name: string): Skill | null {\n if (!isValidSkillName(name)) return null;\n for (const { dir, scope } of this.roots()) {\n if (!existsSync(dir)) continue;\n const dirCandidate = join(dir, name, SKILL_FILE);\n if (existsSync(dirCandidate) && statSync(dirCandidate).isFile()) {\n return this.parse(dirCandidate, name, scope);\n }\n const flatCandidate = join(dir, `${name}.md`);\n if (existsSync(flatCandidate) && statSync(flatCandidate).isFile()) {\n return this.parse(flatCandidate, name, scope);\n }\n }\n if (!this.disableBuiltins) {\n for (const skill of BUILTIN_SKILLS) {\n if (skill.name === name) return skill;\n }\n }\n return null;\n }\n\n private readEntry(dir: string, scope: SkillScope, entry: import(\"node:fs\").Dirent): Skill | null {\n if (entry.isDirectory()) {\n if (!isValidSkillName(entry.name)) return null;\n const file = join(dir, entry.name, SKILL_FILE);\n if (!existsSync(file)) return null;\n return this.parse(file, entry.name, scope);\n }\n if (entry.isFile() && entry.name.endsWith(\".md\")) {\n const stem = entry.name.slice(0, -3);\n if (!isValidSkillName(stem)) return null;\n return this.parse(join(dir, entry.name), stem, scope);\n }\n return null;\n }\n\n private parse(path: string, stem: string, scope: SkillScope): Skill | null {\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n const { data, body } = parseFrontmatter(raw);\n const name = data.name && isValidSkillName(data.name) ? data.name : stem;\n return {\n name,\n description: (data.description ?? \"\").trim(),\n body: body.trim(),\n scope,\n path,\n allowedTools: parseAllowedTools(data[\"allowed-tools\"]),\n runAs: parseRunAs(data.runAs),\n model: data.model?.startsWith(\"deepseek-\") ? data.model : undefined,\n maxToolIters: parseMaxToolIters(data[\"max-iters\"]),\n };\n }\n}\n\n/** Unknown values default to the safe (non-spawning) `inline` mode. */\nfunction parseRunAs(raw: string | undefined): SkillRunAs {\n return raw?.trim() === \"subagent\" ? \"subagent\" : \"inline\";\n}\n\n/** Stub markdown for `/skill new` — minimal frontmatter + scaffolding the user fills in. */\nfunction skillStubBody(name: string): string {\n return `---\nname: ${name}\ndescription: One-liner — what does this skill do?\n---\n\n# ${name}\n\nReplace this body with the playbook the model should follow when this skill is invoked.\n\nTips:\n- Reference tools by name (run_command, edit_file, search_content, ...)\n- Add \\`runAs: subagent\\` to frontmatter to spawn an isolated subagent loop\n- Add \\`allowed-tools: read_file, search_content\\` to scope a subagent's tools\n- Add \\`max-iters: 32\\` to raise the subagent's tool-call budget (default 16, max 32)\n`;\n}\n\n/** Subagent tag goes AFTER the name in brackets — leading-marker tags get copied into `name` arg verbatim. */\nfunction skillIndexLine(s: Pick<Skill, \"name\" | \"description\" | \"runAs\">): string {\n const safeDesc = s.description.replace(/\\n/g, \" \").trim();\n const tag = s.runAs === \"subagent\" ? \" [🧬 subagent]\" : \"\";\n const max = 130 - s.name.length - tag.length;\n const clipped = safeDesc.length > max ? `${safeDesc.slice(0, Math.max(1, max - 1))}…` : safeDesc;\n return clipped ? `- ${s.name}${tag} — ${clipped}` : `- ${s.name}${tag}`;\n}\n\nconst MISSING_DESCRIPTION_PLACEHOLDER =\n '(no description — frontmatter is missing a \"description:\" line; tell the user to add one)';\n\n/** Bodies stay out — prefix must stay short + cacheable; bodies load on demand. */\nexport function applySkillsIndex(basePrompt: string, opts: SkillStoreOptions = {}): string {\n const store = new SkillStore(opts);\n const skills = store.list();\n if (skills.length === 0) return basePrompt;\n const lines = skills.map((s) =>\n skillIndexLine(s.description ? s : { ...s, description: MISSING_DESCRIPTION_PLACEHOLDER }),\n );\n const joined = lines.join(\"\\n\");\n const truncated =\n joined.length > SKILLS_INDEX_MAX_CHARS\n ? `${joined.slice(0, SKILLS_INDEX_MAX_CHARS)}\\n… (truncated ${\n joined.length - SKILLS_INDEX_MAX_CHARS\n } chars)`\n : joined;\n return [\n basePrompt,\n \"\",\n \"# Skills — playbooks you can invoke\",\n \"\",\n 'One-liner index. Each entry is either a built-in or a user-authored playbook. Call `run_skill({ name: \"<skill-name>\", arguments: \"<task>\" })` — the `name` is JUST the skill identifier (e.g. `\"explore\"`), NOT the `[🧬 subagent]` tag that appears after it. Entries tagged `[🧬 subagent]` spawn an **isolated subagent** — its tool calls and reasoning never enter your context, only its final answer does. Use subagent skills for tasks that would otherwise flood your context (deep exploration, multi-step research, anything where you only need the conclusion). Plain skills are inlined: their body becomes a tool result you read and act on directly. The user can also invoke a skill via `/skill <name>`.',\n \"\",\n \"```\",\n truncated,\n \"```\",\n ].join(\"\\n\");\n}\n\nconst BUILTIN_EXPLORE_BODY = `You are running as an exploration subagent. Your job is to investigate the codebase the parent agent pointed you at, then return one focused, distilled answer.\n\nHow to operate:\n- Use read_file, search_files, search_content, directory_tree, list_directory, get_file_info as your primary tools. Stay read-only.\n- For \"find all places that call / reference / use X\" questions, use \\`search_content\\` (content grep) — NOT \\`search_files\\` (which only matches file names). This is the most common subagent mistake; using the wrong tool gives empty results and you waste your iter budget chasing a phantom.\n- Cast a wide net first (search_content for symbol references, directory_tree for structure) to map the territory; then read the 3-10 most relevant files in full.\n- Don't read every file — be selective. Aim for breadth on the first pass, depth only where the question demands it.\n- Stop exploring as soon as you can answer the question. The parent doesn't see your tool calls, so over-exploration is pure waste.\n\nYour final answer:\n- One paragraph (or a few short bullets). Lead with the conclusion.\n- Cite specific file paths + line ranges when they support the answer.\n- If the question can't be answered from what you found, say so plainly and suggest where to look next.\n- No follow-up offers, no \"let me know if you need more.\" The parent will ask again if they need more.\n\n${NEGATIVE_CLAIM_RULE}\n\n${TUI_FORMATTING_RULES}\n\nThe 'task' the parent gave you is the question you must answer. Treat any other reading of it as scope creep.`;\n\nconst BUILTIN_RESEARCH_BODY = `You are running as a research subagent. Your job is to gather information from code AND the web, synthesize it, and return one focused conclusion.\n\nHow to operate:\n- Combine code reading (read_file, search_files) with web tools (web_search, web_fetch) as appropriate to the question.\n- For \"how does X work\" / \"is Y supported\" questions: web first to find the canonical reference, then verify against the local code.\n- For \"what's our policy on Z\" / \"where do we use Q\": local code first, web only if you need to compare against external standards.\n- Cap yourself at ~10 tool calls. If you can't converge in 10, return what you have plus a note about what's missing.\n\nYour final answer:\n- One paragraph (or short bullets). Lead with the conclusion.\n- Cite both code (file:line) AND web sources (URL) when they back the answer.\n- Distinguish \"I verified this in code\" from \"I read this on a docs page\" — the parent will trust the former more.\n- If the answer is uncertain, say so. Don't invent confidence.\n\n${NEGATIVE_CLAIM_RULE}\n\n${TUI_FORMATTING_RULES}\n\nThe 'task' the parent gave you is the research question. Stay on it.`;\n\nconst BUILTIN_REVIEW_BODY = `You are running as a code-review subagent. Your job is to inspect the changes the user is about to ship — usually the current git branch vs its upstream — and produce a focused review the parent can hand back to the user.\n\nHow to operate:\n- Default scope: the current branch's diff vs the default branch. If the user's task names a specific commit range or files, honor that instead.\n- Discover scope first: \\`run_command git status\\`, \\`git diff --stat\\`, \\`git log --oneline\\` to see what changed. Then \\`git diff\\` (or \\`git diff <base>...HEAD\\`) for the actual hunks.\n- Read the touched files (\\`read_file\\`) when the diff alone doesn't carry enough context — function signatures, surrounding invariants, callers.\n- For \"any callers depending on this?\" questions: \\`search_content\\` against the symbol BEFORE asserting impact.\n- Stay read-only. Never \\`run_command git commit\\`, never write files, never propose SEARCH/REPLACE blocks. The parent decides whether to act on your findings.\n- Cap yourself at ~12 tool calls. If the diff is too big to review in one pass, pick the riskiest 2-3 files and say so explicitly.\n\nWhat to look for, in priority order:\n1. **Correctness bugs** — off-by-one, null/undefined handling, race conditions, wrong sign / wrong operator, edge cases the code doesn't handle.\n2. **Security** — injection (SQL, shell, path traversal), secrets in code, missing authz checks, unsafe deserialization.\n3. **Behavior changes the diff hides** — renames that miss callers, removed branches that were load-bearing, error-handling that now swallows what used to surface.\n4. **Tests** — does the change have tests for the new behavior? Are existing tests still meaningful, or did the change make them tautological?\n5. **Style + consistency** — only flag deviations that matter (unsafe \\`any\\`, missing types in TypeScript, inconsistent error shape). Don't pile on cosmetic nits if the substance is clean.\n\nYour final answer:\n- Lead with a one-sentence verdict: \"ship as-is\" / \"minor nits, OK to ship after\" / \"blocking issues, do not ship\".\n- Then a short bulleted list of issues, each with: file:line citation + the problem in one sentence + what to change.\n- Group by severity if you have more than 4 items: **Blocking**, **Should-fix**, **Nits**.\n- If everything looks clean, say so plainly. Don't manufacture concerns.\n\n${NEGATIVE_CLAIM_RULE}\n\n${TUI_FORMATTING_RULES}\n\nThe 'task' the parent gave you describes WHAT to review (a branch, a file set, or \"the pending changes\"). Stay on it; don't redesign the feature.`;\n\nconst BUILTIN_SECURITY_REVIEW_BODY = `You are running as a security-review subagent. Your job is to inspect the changes the user is about to ship — usually the current git branch vs its upstream — through a security lens specifically, and report exploitable issues.\n\nHow to operate:\n- Default scope: the current branch's diff vs the default branch. If the user names a different range or a directory, honor that.\n- Discover scope first: \\`git status\\`, \\`git diff --stat\\`, \\`git diff <base>...HEAD\\`. Read touched files (\\`read_file\\`) when the diff alone doesn't carry security context — auth checks, input validation, the actual handler that calls into the changed function.\n- Use \\`search_content\\` to verify \"is this user-controlled input ever sanitized later?\" / \"are there other call sites that depend on this validation?\" before asserting impact.\n- Stay read-only. Never write, never run destructive commands, never propose SEARCH/REPLACE blocks. The parent decides what to act on.\n- Cap yourself at ~12 tool calls. If the diff is too big, focus on the riskiest 2-3 files and say so explicitly.\n\nThreat model — flag with severity:\n\n**CRITICAL** (do-not-ship):\n- SQL / NoSQL / shell / template injection — user input concatenated into a query, command, or template without parameterization.\n- Path traversal — user-controlled filenames touching the filesystem without canonicalization + sandbox check.\n- Authentication / authorization missing — endpoints / actions that should require a session check but don't.\n- Hardcoded secrets — API keys, passwords, signing tokens visible in the diff.\n- Deserialization of untrusted input — \\`pickle.loads\\`, \\`yaml.load\\` (non-safe), \\`eval\\`, \\`Function()\\`, \\`unserialize()\\`.\n- Cryptographic mistakes — homemade crypto, weak hashes (MD5/SHA-1) for passwords, missing IVs, ECB mode, predictable nonces.\n\n**HIGH**:\n- XSS — user input rendered into HTML without escaping (or wrong escaping context).\n- SSRF — fetching URLs from user input without an allowlist.\n- Race conditions in security-relevant code — TOCTOU on auth/file checks.\n- Open redirects — user-controlled URL passed to a redirect helper.\n- Insufficient logging on security events (login failure, permission denial) — only flag if the codebase clearly DOES log elsewhere.\n\n**MEDIUM**:\n- Verbose error messages leaking internal paths / stack traces / SQL.\n- Missing rate limiting on a credential / token endpoint.\n- Cross-origin / cookie-flag issues (missing \\`Secure\\` / \\`HttpOnly\\` / \\`SameSite\\`).\n\nThings to NOT pile on (out of scope here — the regular /review covers them):\n- Style, formatting, naming.\n- Performance, refactor opportunities, test coverage gaps that aren't security-relevant.\n- \"Should be a constant\" / \"extract this helper\" — irrelevant to ship-blocking.\n\nYour final answer:\n- Lead with a one-sentence verdict: \"no security issues found\", \"minor concerns\", or \"blocking issues\".\n- Then a list grouped by severity. Each item: file:line + 1-sentence threat + 1-sentence fix direction (no full SEARCH/REPLACE — the user / parent agent will write that).\n- If clean, say so plainly. Don't manufacture findings.\n\n${NEGATIVE_CLAIM_RULE}\n\n${TUI_FORMATTING_RULES}\n\nThe 'task' the parent gave you names what to review. Stay on it; don't redesign the feature.`;\n\nconst BUILTIN_TEST_BODY = `You are running as the parent agent — this skill is INLINED, not a subagent. The user invoked /test (or asked you to \"run the tests and fix failures\"). Your job: run the project's test suite, diagnose any failure, propose fixes as SEARCH/REPLACE edit blocks, then re-run. Repeat until green or you hit a wall you should escalate.\n\nHow to operate:\n\n1. **Detect the test command**.\n - Look for \\`package.json\\` → \\`scripts.test\\` first (most common: \\`npm test\\`, \\`pnpm test\\`, \\`yarn test\\`).\n - If no package.json or no test script: try \\`pytest\\`, \\`go test ./...\\`, \\`cargo test\\` based on what files exist (pyproject.toml/requirements.txt → pytest; go.mod → go test; Cargo.toml → cargo test).\n - If you can't tell, ASK the user for the command — don't guess. One question, one tool call to confirm.\n\n2. **Run it via run_command** (typical timeout 120s, bigger if the suite is large). Capture stdout + stderr.\n\n3. **Read the failures**. Pull out: which test names failed, the actual error/traceback, the file + line that threw. Don't just paraphrase — locate the exact assertion or stack frame.\n\n4. **Propose fixes**. For each distinct failure:\n - If the failure is in PRODUCTION code (test catches a real bug) → propose a SEARCH/REPLACE that fixes the production code.\n - If the failure is in TEST code (test is wrong, codebase is right) → propose a SEARCH/REPLACE that updates the test, AND say so explicitly: \"This is a test bug, not a production bug — updating the assertion.\"\n - If the failure is environmental (missing dep, wrong node version, missing fixture file) → say so and stop. Don't try to install packages or change config without checking with the user.\n\n5. **Apply + re-run**. After the user accepts the edit blocks, run the test command again. Iterate.\n\n6. **Stop conditions**:\n - All tests pass → report green, summarize what changed.\n - Same test still failing after 2 fix attempts on the same line → STOP. Tell the user \"I've tried twice, it's still failing — here's what I think is happening, want me to try a different angle?\". Don't loop indefinitely.\n - 3+ unrelated failures → fix one at a time, smallest first, so each pass narrows the surface.\n\nDon't:\n- Run \\`npm install\\` / \\`pip install\\` / \\`cargo update\\` without asking — those mutate lockfiles and have global effects.\n- Disable, skip, or delete failing tests to \"make it green\". If a test seems wrong, update its assertion with a one-sentence explanation, but never add \\`.skip\\` / \\`it.skip\\` / \\`@pytest.mark.skip\\`.\n- Modify the test runner config (vitest.config, jest.config, etc.) to silence failures.\n\nLead each turn with a one-line status: \"▸ running \\`npm test\\` ...\" → \"▸ 2 failures in tests/foo.test.ts — first is …\" → so the user always knows where you are without scrolling tool output.`;\n\nconst BUILTIN_SKILLS: readonly Skill[] = Object.freeze([\n Object.freeze<Skill>({\n name: \"explore\",\n description:\n \"Explore the codebase in an isolated subagent — wide-net read-only investigation that returns one distilled answer. Best for: 'find all places that...', 'how does X work across the project', 'survey the code for Y'.\",\n body: BUILTIN_EXPLORE_BODY,\n scope: \"builtin\",\n path: \"(builtin)\",\n runAs: \"subagent\",\n }),\n Object.freeze<Skill>({\n name: \"research\",\n description:\n \"Research a question by combining web search + code reading in an isolated subagent. Best for: 'is X feature supported by lib Y', 'what's the canonical way to do Z', 'compare our impl against the spec'.\",\n body: BUILTIN_RESEARCH_BODY,\n scope: \"builtin\",\n path: \"(builtin)\",\n runAs: \"subagent\",\n }),\n Object.freeze<Skill>({\n name: \"review\",\n description:\n \"Review the pending changes (current branch diff by default) in an isolated subagent — flags correctness, security, missing tests, hidden behavior changes; reports verdict + per-issue file:line. Read-only; the parent decides what to act on.\",\n body: BUILTIN_REVIEW_BODY,\n scope: \"builtin\",\n path: \"(builtin)\",\n runAs: \"subagent\",\n }),\n Object.freeze<Skill>({\n name: \"security-review\",\n description:\n \"Security-focused review of the current branch diff in an isolated subagent — flags injection/authz/secrets/deserialization/path-traversal/crypto issues, severity-tagged. Read-only. Use when shipping changes that touch auth, input parsing, file IO, or external requests.\",\n body: BUILTIN_SECURITY_REVIEW_BODY,\n scope: \"builtin\",\n path: \"(builtin)\",\n runAs: \"subagent\",\n }),\n Object.freeze<Skill>({\n name: \"test\",\n description:\n \"Run the project's test suite, diagnose failures, propose SEARCH/REPLACE fixes, re-run until green (or stop after 2 fix attempts on the same failure). Inlined — runs in the parent loop so you see the edit blocks and can /apply them. Detects npm/pnpm/yarn/pytest/go/cargo.\",\n body: BUILTIN_TEST_BODY,\n scope: \"builtin\",\n path: \"(builtin)\",\n runAs: \"inline\",\n }),\n]);\n","/** Shared prompt fragments — single source so house-style rules can't drift across agent/subagent/skill prompts. */\n\n/** Embedded literally — no interpolation, so prefix-cache hash stays stable across sessions. */\nexport const TUI_FORMATTING_RULES = `Formatting (rendered in a TUI with a real markdown renderer):\n- Tabular data → GitHub-Flavored Markdown tables with ASCII pipes (\\`| col | col |\\` header + \\`| --- | --- |\\` separator). Never use Unicode box-drawing characters (│ ─ ┼ ┌ ┐ └ ┘ ├ ┤) — they look intentional but break terminal word-wrap and render as garbled columns at narrow widths.\n- Keep table cells short (one phrase each). If a cell needs a paragraph, use bullets below the table instead.\n- Code, file paths with line ranges, and shell commands → fenced code blocks (\\`\\`\\`).\n- Do NOT draw decorative frames around content with \\`┌──┐ │ └──┘\\` characters. The renderer adds its own borders; extra ASCII art adds noise and shatters at narrow widths.\n- For flow charts and diagrams: a plain bullet list with \\`→\\` or \\`↓\\` between steps. Don't try to draw boxes-and-arrows in ASCII; it never survives word-wrap.`;\n\n/** Pro is the top tier — escalation is a no-op for it; flash + others get the full ladder. */\nexport function escalationContract(modelId: string): string {\n if (modelId === \"deepseek-v4-pro\") {\n return `Cost-aware escalation note: you are running on \\`${modelId}\\` — the escalation tier. There is no higher tier to escalate to, so the \\`<<<NEEDS_PRO>>>\\` marker is a no-op for you; deliver the strongest answer you can directly. If asked which model you are, answer \\`${modelId}\\`.`;\n }\n return `Cost-aware escalation (you are running on \\`${modelId}\\`):\n\nIf a task CLEARLY exceeds what this tier can do well — complex cross-file architecture refactors, subtle concurrency / security / correctness invariants you can't resolve with confidence, or a design trade-off you'd be guessing at — output the marker as the FIRST line of your response (nothing before it, not even whitespace on a separate line). This aborts the current call and retries this turn on deepseek-v4-pro, one shot.\n\nTwo accepted forms:\n- \\`<<<NEEDS_PRO>>>\\` — bare marker, no rationale.\n- \\`<<<NEEDS_PRO: <one-sentence reason>>>>\\` — preferred. The reason text appears in the user-visible warning (\"⇧ flash requested escalation — <your reason>\"), so they understand WHY a more expensive call is happening. Keep it under ~150 chars, no newlines, no nested \\`>\\` characters. Examples: \\`<<<NEEDS_PRO: cross-file refactor across 6 modules with circular imports>>>\\` or \\`<<<NEEDS_PRO: subtle session-token race; flash would likely miss the locking invariant>>>\\`.\n\nDo NOT emit any other content in the same response when you request escalation. Use this sparingly: normal tasks — reading files, small edits, clear bug fixes, straightforward feature additions — stay on this tier. Request escalation ONLY when you would otherwise produce a guess or a visibly-mediocre answer. If in doubt, attempt the task here first; the system also escalates automatically if you hit 3+ repair / SEARCH-mismatch errors in a single turn (the user sees a typed breakdown). If asked which model you are, answer \\`${modelId}\\`.`;\n}\n\n/** Backward-compat — pre-#582 callers (and the `CODE_SYSTEM_PROMPT` public-API const) keep the historical flash phrasing. */\nexport const ESCALATION_CONTRACT = escalationContract(\"deepseek-v4-flash\");\n\nexport const NEGATIVE_CLAIM_RULE = `Negative claims (\"X is missing\", \"Y isn't implemented\", \"there's no Z\") are the #1 hallucination shape. They feel safe to write because no citation seems possible — but that's exactly why you must NOT write them on instinct.\n\nIf you have a search tool (\\`search_content\\`, \\`grep\\`, web search), call it FIRST before asserting absence:\n- Returns matches → you were wrong; correct yourself and cite the matches.\n- Returns nothing → state the absence WITH the search query as evidence: \\`No callers of \\\\\\`foo()\\\\\\` found (search_content \"foo\").\\`\n\nIf you have no search tool, qualify hard: \"I haven't verified — this is a guess.\" Never assert absence with fake authority.`;\n","/** Native FS tools — sandbox enforced here, not delegated. `edit_file` takes a single SEARCH/REPLACE string. */\n\nimport { promises as fs } from \"node:fs\";\nimport * as pathMod from \"node:path\";\nimport picomatch from \"picomatch\";\nimport { addProjectPathAllowed, loadProjectPathAllowed } from \"../config.js\";\nimport { type ConfirmationChoice, pauseGate as defaultPauseGate } from \"../core/pause-gate.js\";\nimport { DEFAULT_INDEX_EXCLUDES } from \"../index/config.js\";\nimport type { ToolCallContext, ToolRegistry } from \"../tools.js\";\nimport { applyEdit, applyMultiEdit } from \"./fs/edit.js\";\nimport { globFiles } from \"./fs/glob.js\";\nimport { extractOutline, formatOutline } from \"./fs/outline.js\";\nimport { searchContent, searchFiles } from \"./fs/search.js\";\n\nexport { lineDiff } from \"./fs/edit.js\";\n\nexport interface FilesystemToolsOptions {\n /** Absolute directory the tools may read/write. Paths outside this are refused. */\n rootDir: string;\n /** false → register only read-side tools. Default true. */\n allowWriting?: boolean;\n /** Per-read byte cap; floor against OOM on a multi-GB blob. */\n maxReadBytes?: number;\n /** Cap on total bytes from listing/grep tools — bounds tree-as-one-string accidents. */\n maxListBytes?: number;\n}\n\nconst DEFAULT_MAX_READ_BYTES = 2 * 1024 * 1024;\nconst DEFAULT_MAX_LIST_BYTES = 256 * 1024;\n\n/** Auto-preview threshold — files above this force the model to scope (range/head/tail). */\nconst DEFAULT_AUTO_PREVIEW_LINES = 200;\nconst AUTO_PREVIEW_HEAD_LINES = 80;\nconst AUTO_PREVIEW_TAIL_LINES = 40;\nconst OUTLINE_MAX_ENTRIES = 30;\n\n/** Skipped unless `include_deps:true` — shared with the semantic indexer via DEFAULT_INDEX_EXCLUDES. */\nconst SKIP_DIR_NAMES: ReadonlySet<string> = new Set(DEFAULT_INDEX_EXCLUDES.dirs);\n\n/** First line of binary defense; NUL-byte sniff is the second (catches mislabeled `.txt`). */\nconst BINARY_EXTENSIONS: ReadonlySet<string> = new Set(DEFAULT_INDEX_EXCLUDES.exts);\n\nexport function displayRel(rootDir: string, full: string): string {\n return pathMod.relative(rootDir, full).replaceAll(\"\\\\\", \"/\");\n}\n\nconst GLOB_METACHARS = /[*?{[]/;\n\n/** Glob via picomatch when metachars present, else case-insensitive substring — keeps `.ts` / `test` callers working. Slash in pattern → match rel-path; otherwise basename. */\nexport function compileNameFilter(\n filter: string | null | undefined,\n): ((name: string, rel: string) => boolean) | null {\n if (!filter) return null;\n if (!GLOB_METACHARS.test(filter)) {\n const needle = filter.toLowerCase();\n return (name) => name.toLowerCase().includes(needle);\n }\n const matchPath = filter.includes(\"/\");\n const isMatch = picomatch(filter, { dot: true, nocase: true });\n return matchPath ? (_n, rel) => isMatch(rel) : (name) => isMatch(name);\n}\n\nfunction isLikelyBinaryByName(name: string): boolean {\n const dot = name.lastIndexOf(\".\");\n if (dot < 0) return false;\n return BINARY_EXTENSIONS.has(name.slice(dot).toLowerCase());\n}\n\nexport function registerFilesystemTools(\n registry: ToolRegistry,\n opts: FilesystemToolsOptions,\n): ToolRegistry {\n const rootDir = pathMod.resolve(opts.rootDir);\n const allowWriting = opts.allowWriting !== false;\n const maxReadBytes = opts.maxReadBytes ?? DEFAULT_MAX_READ_BYTES;\n const maxListBytes = opts.maxListBytes ?? DEFAULT_MAX_LIST_BYTES;\n\n const normRoot = pathMod.resolve(rootDir);\n /** Approved-this-session directory prefixes — `run_once` keeps the user from being asked twice for follow-up reads in the same dir. Wiped on process exit, not persisted. */\n const sessionApproved = new Set<string>();\n /** In-flight gate prompts keyed by `allowPrefix` so parallel reads under the same dir only fire one modal. */\n const inflightGate = new Map<string, Promise<ConfirmationChoice>>();\n\n function pathIsUnder(child: string, parent: string): boolean {\n const rel = pathMod.relative(parent, child);\n return rel === \"\" || (!rel.startsWith(\"..\") && !pathMod.isAbsolute(rel));\n }\n\n /** Heuristic: does the raw input express \"I really mean this absolute system path\" rather than the model-convention sandbox-relative form? Windows drive-letter prefixes always count; POSIX absolutes only count when their first segment is a known system root. */\n function looksLikeAbsoluteSystemPath(raw: string): boolean {\n if (/^[A-Za-z]:[\\\\/]/.test(raw)) return true;\n return /^\\/(?:home|Users|etc|var|opt|tmp|usr|mnt|Library|Volumes|proc|sys|dev|run|srv|media|Applications|System|root|boot|private)(?:[/\\\\]|$)/.test(\n raw,\n );\n }\n\n async function ensureOutsideSandboxAllowed(\n abs: string,\n intent: \"read\" | \"write\",\n toolName: string,\n ctx: ToolCallContext | undefined,\n ): Promise<void> {\n for (const dir of loadProjectPathAllowed(rootDir)) {\n if (pathIsUnder(abs, dir)) return;\n }\n for (const dir of sessionApproved) {\n if (pathIsUnder(abs, dir)) return;\n }\n const stat = await safeLstat(abs);\n const allowPrefix = stat?.isDirectory() ? abs : pathMod.dirname(abs);\n let pending = inflightGate.get(allowPrefix);\n if (!pending) {\n const gate = ctx?.confirmationGate ?? defaultPauseGate;\n pending = gate.ask({\n kind: \"path_access\",\n payload: { path: abs, intent, toolName, sandboxRoot: normRoot, allowPrefix },\n });\n inflightGate.set(allowPrefix, pending);\n void pending.finally(() => inflightGate.delete(allowPrefix));\n }\n const choice = await pending;\n if (choice.type === \"deny\") {\n throw new Error(\n `user denied access to ${abs}${choice.denyContext ? ` — ${choice.denyContext}` : \"\"}`,\n );\n }\n if (choice.type === \"always_allow\") {\n addProjectPathAllowed(rootDir, choice.prefix);\n } else {\n sessionApproved.add(allowPrefix);\n }\n }\n\n /** Resolve path, route outside-sandbox access through the approval gate, return absolute. */\n const safePath = async (\n raw: unknown,\n toolName: string,\n ctx: ToolCallContext | undefined,\n intent: \"read\" | \"write\" = \"read\",\n ): Promise<string> => {\n if (typeof raw !== \"string\" || raw.length === 0) {\n throw new Error(\"path must be a non-empty string\");\n }\n if (looksLikeAbsoluteSystemPath(raw)) {\n const abs = pathMod.resolve(raw);\n if (pathIsUnder(abs, normRoot)) return abs;\n await ensureOutsideSandboxAllowed(abs, intent, toolName, ctx);\n return abs;\n }\n // Sandbox-root semantics: leading `/` or `\\` means \"from project root\", not \"from filesystem root\".\n // Model routinely writes `path: \"/src/foo.ts\"` intending rootDir-relative.\n let normalized = raw;\n while (normalized.startsWith(\"/\") || normalized.startsWith(\"\\\\\")) {\n normalized = normalized.slice(1);\n }\n if (normalized.length === 0) normalized = \".\";\n const resolved = pathMod.resolve(rootDir, normalized);\n if (!pathIsUnder(resolved, normRoot)) {\n throw new Error(\n `path escapes sandbox root (${normRoot}): ${raw} — use an absolute system path like /Users/foo or C:\\\\Users\\\\foo to request approved outside-sandbox access`,\n );\n }\n return resolved;\n };\n\n /** lstat that swallows ENOENT so we can still gate writes to brand-new paths. */\n async function safeLstat(p: string): Promise<import(\"node:fs\").Stats | null> {\n try {\n return await fs.lstat(p);\n } catch {\n return null;\n }\n }\n\n registry.register({\n name: \"read_file\",\n parallelSafe: true,\n description: `Read a file under the sandbox root. To save context, PREFER to scope the read instead of pulling the whole file:\n - head: N → first N lines (imports, public API, small configs)\n - tail: N → last N lines (recently-added code, log tails)\n - range: \"A-B\" → inclusive line range A..B, 1-indexed (e.g. \"120-180\" around an edit site)\nWhen none of these is given AND the file is longer than ${DEFAULT_AUTO_PREVIEW_LINES} lines, the tool auto-returns a head+tail preview with an \"N lines omitted\" marker, plus a top-level symbol outline (TS/JS exports, Python def/class, Go func/type, Rust fn/struct/impl/trait, Markdown headings, with line numbers, capped at ${OUTLINE_MAX_ENTRIES}) so you can pick a smart range without a follow-up grep. If you need the middle, re-call with a range. Prefer search_content to locate a symbol first only when the outline doesn't have what you want — one scoped read beats three full-file reads.`,\n readOnly: true,\n stormExempt: true,\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Path to read (relative to rootDir or absolute).\" },\n head: { type: \"integer\", description: \"If set, return only the first N lines.\" },\n tail: { type: \"integer\", description: \"If set, return only the last N lines.\" },\n range: {\n type: \"string\",\n description:\n 'Inclusive line range like \"50-100\" or \"50-50\". 1-indexed. Takes precedence over head/tail when all three are set. Out-of-range requests clamp to file bounds.',\n },\n },\n required: [\"path\"],\n },\n fn: async (\n args: { path: string; head?: number; tail?: number; range?: string },\n ctx?: ToolCallContext,\n ) => {\n const abs = await safePath(args.path, \"read_file\", ctx);\n // Open once and reuse the fd so the directory check and the read\n // bind to the same inode — closes the stat→read TOCTOU race.\n const fh = await fs.open(abs, \"r\");\n let raw: Buffer;\n try {\n const stat = await fh.stat();\n if (stat.isDirectory()) {\n throw new Error(`not a file: ${args.path} (it's a directory)`);\n }\n raw = await fh.readFile();\n } finally {\n await fh.close();\n }\n if (raw.length > maxReadBytes) {\n const headBytes = raw.slice(0, maxReadBytes).toString(\"utf8\");\n return `${headBytes}\\n\\n[…truncated ${raw.length - maxReadBytes} bytes — file is ${raw.length} B, cap ${maxReadBytes} B. Retry with head/tail/range for targeted view.]`;\n }\n const text = raw.toString(\"utf8\");\n let lines = text.split(/\\r?\\n/);\n // Most files end with '\\n' which splits into an empty trailing\n // entry; drop it so head/tail/range counts match the user's\n // visible line numbers in an editor.\n if (lines.length > 0 && lines[lines.length - 1] === \"\") lines = lines.slice(0, -1);\n const totalLines = lines.length;\n\n // range wins over head/tail when set — the most precise ask\n // should dominate. Parse \"A-B\" strictly; bad formats fall through\n // to head/tail / auto-preview instead of erroring.\n if (typeof args.range === \"string\" && /^\\d+\\s*-\\s*\\d+$/.test(args.range)) {\n const [rawStart, rawEnd] = args.range.split(\"-\").map((s) => Number.parseInt(s, 10));\n const start = Math.max(1, rawStart ?? 1);\n const end = Math.min(totalLines, Math.max(start, rawEnd ?? totalLines));\n const slice = lines.slice(start - 1, end);\n const label = `[range ${start}-${end} of ${totalLines} lines]`;\n return `${label}\\n${slice.join(\"\\n\")}`;\n }\n if (typeof args.head === \"number\" && args.head > 0) {\n const count = Math.min(args.head, totalLines);\n const slice = lines.slice(0, count);\n const marker =\n count < totalLines\n ? `\\n\\n[…head ${count} of ${totalLines} lines — call again with range / tail for more]`\n : \"\";\n return slice.join(\"\\n\") + marker;\n }\n if (typeof args.tail === \"number\" && args.tail > 0) {\n const count = Math.min(args.tail, totalLines);\n const slice = lines.slice(totalLines - count);\n const marker =\n count < totalLines\n ? `[…tail ${count} of ${totalLines} lines — call again with range / head for more]\\n\\n`\n : \"\";\n return marker + slice.join(\"\\n\");\n }\n\n // No explicit scope + file is small → full content.\n if (totalLines <= DEFAULT_AUTO_PREVIEW_LINES) return lines.join(\"\\n\");\n\n // No explicit scope + file is large → head + tail preview plus\n // a marker telling the model how much it missed and how to get\n // it. This is the single biggest lever on read_file token cost —\n // historically a 500-line file dumped ~4K tokens into the turn\n // even when the model only needed 20 of them.\n const head = lines.slice(0, AUTO_PREVIEW_HEAD_LINES).join(\"\\n\");\n const tail = lines.slice(totalLines - AUTO_PREVIEW_TAIL_LINES).join(\"\\n\");\n const omitted = totalLines - AUTO_PREVIEW_HEAD_LINES - AUTO_PREVIEW_TAIL_LINES;\n const outline = formatOutline(extractOutline(abs, lines));\n const parts: string[] = [\n `[auto-preview: head ${AUTO_PREVIEW_HEAD_LINES} + tail ${AUTO_PREVIEW_TAIL_LINES} of ${totalLines} lines]`,\n head,\n ];\n if (outline) parts.push(\"\", outline);\n parts.push(\n `\\n[… ${omitted} lines omitted — call read_file again with range:\"A-B\" (1-indexed) or head / tail to get the middle]\\n`,\n tail,\n );\n return parts.join(\"\\n\");\n },\n });\n\n registry.register({\n name: \"list_directory\",\n parallelSafe: true,\n description:\n \"List entries in a directory under the sandbox root. Returns one line per entry, marking directories with a trailing slash. Not recursive — use directory_tree for that.\",\n readOnly: true,\n stormExempt: true,\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Directory to list (default: root).\" },\n },\n },\n fn: async (args: { path?: string }, ctx?: ToolCallContext) => {\n const abs = await safePath(args.path ?? \".\", \"list_directory\", ctx);\n const entries = await fs.readdir(abs, { withFileTypes: true });\n const lines: string[] = [];\n for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n lines.push(e.isDirectory() ? `${e.name}/` : e.name);\n }\n return lines.join(\"\\n\") || \"(empty directory)\";\n },\n });\n\n registry.register({\n name: \"directory_tree\",\n parallelSafe: true,\n description: `Recursively list entries in a directory. Shows indented tree structure with directories marked '/'. Budget-aware by default:\n - maxDepth defaults to 2 (root + one level). A depth-4 tree on a real repo blew ~5K tokens in one call. If you truly need deeper, pass maxDepth:N explicitly.\n - Skips ${[...SKIP_DIR_NAMES].sort().join(\", \")} unless include_deps:true. Traversing into node_modules / .git / dist is almost always token-waste.\n - Large subtrees (>50 children) auto-collapse to \"[N files, M dirs hidden — list_directory <path> to inspect]\" so one huge folder can't dominate the output.\nPrefer \\`list_directory\\` for a single-level view, \\`search_files\\` to find specific paths, and \\`search_content\\` to find code.`,\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Root of the tree (default: sandbox root).\" },\n maxDepth: {\n type: \"integer\",\n description:\n \"Max recursion depth (default 2). Depth 0 shows only the top-level entries; depth 2 is usually enough to see module structure.\",\n },\n include_deps: {\n type: \"boolean\",\n description:\n \"When true, also traverse node_modules / .git / dist / build / etc. Off by default — most exploration questions are about the user's own code.\",\n },\n },\n },\n fn: async (\n args: { path?: string; maxDepth?: number; include_deps?: boolean },\n ctx?: ToolCallContext,\n ) => {\n const startAbs = await safePath(args.path ?? \".\", \"directory_tree\", ctx);\n const maxDepth = typeof args.maxDepth === \"number\" ? args.maxDepth : 2;\n const includeDeps = args.include_deps === true;\n const lines: string[] = [];\n let totalBytes = 0;\n let truncated = false;\n // Per-directory child cap — long fixture / asset folders (200+\n // snapshots) would otherwise dominate; the collapse keeps the\n // overall shape visible. Modest: normal source dirs have <50\n // entries.\n const PER_DIR_CHILD_CAP = 50;\n const walk = async (dir: string, depth: number): Promise<void> => {\n if (truncated) return;\n if (depth > maxDepth) return;\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n entries.sort((a, b) => a.name.localeCompare(b.name));\n let emitted = 0;\n for (const e of entries) {\n if (truncated) return;\n // Dep-skip applies only to DIRECTORIES (a file named\n // \"node_modules\" is fine to list). Anything in the skip set\n // still shows up as a single node with a trailing \" (skipped)\"\n // hint so the model knows the dir exists but wasn't walked.\n const skip = e.isDirectory() && !includeDeps && SKIP_DIR_NAMES.has(e.name);\n if (emitted >= PER_DIR_CHILD_CAP) {\n const remaining = entries.length - emitted;\n let restFiles = 0;\n let restDirs = 0;\n for (const r of entries.slice(emitted)) {\n if (r.isDirectory()) restDirs++;\n else restFiles++;\n }\n const indent = \" \".repeat(depth);\n lines.push(\n `${indent}[… ${remaining} entries hidden (${restDirs} dirs, ${restFiles} files) — list_directory on this path to see all]`,\n );\n return;\n }\n const indent = \" \".repeat(depth);\n const suffix = skip ? \" (skipped — pass include_deps:true to traverse)\" : \"\";\n const line = e.isDirectory() ? `${indent}${e.name}/${suffix}` : `${indent}${e.name}`;\n totalBytes += line.length + 1;\n if (totalBytes > maxListBytes) {\n lines.push(` [… tree truncated at ${maxListBytes} bytes …]`);\n truncated = true;\n return;\n }\n lines.push(line);\n emitted++;\n if (e.isDirectory() && !skip) {\n await walk(pathMod.join(dir, e.name), depth + 1);\n }\n }\n };\n await walk(startAbs, 0);\n return lines.join(\"\\n\") || \"(empty tree)\";\n },\n });\n\n registry.register({\n name: \"search_files\",\n parallelSafe: true,\n description:\n \"Find files whose NAME matches a substring or regex. Case-insensitive. Walks the directory recursively under the sandbox root. Returns one path per line. Skips dependency / VCS / build directories (node_modules, .git, dist, build, .next, target, .venv) by default.\",\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Directory to start the search at (default: root).\" },\n pattern: {\n type: \"string\",\n description: \"Substring (or regex) to match against filenames.\",\n },\n include_deps: {\n type: \"boolean\",\n description:\n \"When true, also walk node_modules / .git / dist / build / etc. Off by default — most filename searches are about the user's own code.\",\n },\n },\n required: [\"pattern\"],\n },\n fn: async (args: { path?: string; pattern: string; include_deps?: boolean }, toolCtx) =>\n searchFiles(\n { rootDir, maxListBytes, skipDirNames: SKIP_DIR_NAMES },\n await safePath(args.path ?? \".\", \"search_files\", toolCtx),\n { ...args, signal: toolCtx?.signal },\n ),\n });\n\n registry.register({\n name: \"search_content\",\n parallelSafe: true,\n description:\n \"Recursively grep file CONTENTS for a substring or regex. This is the right tool for 'find all places that call X', 'where is Y referenced', 'what files contain Z'. Different from search_files (which matches FILE NAMES). Returns one match per line in 'path:line: text' format. Per-file hits are capped at 30 (a footer reports any extras); when the byte budget is mostly spent the remaining files switch to a 'rel: N matches' histogram so distribution stays visible instead of one popular file drowning the rest. Pass `summary_only:true` to skip line content entirely and get just the histogram. Skips dependency / VCS / build directories (node_modules, .git, dist, build, .next, target, .venv) and binary files by default.\",\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n pattern: {\n type: \"string\",\n description: \"Substring (or regex) to search file contents for.\",\n },\n path: {\n type: \"string\",\n description: \"Directory to start the search at (default: sandbox root).\",\n },\n glob: {\n type: \"string\",\n description:\n \"Optional filename filter. Real glob when the value contains `*`, `?`, `{`, or `[` — e.g. '*.ts', '**/*.tsx', 'src/**/*.{ts,tsx}'. Plain substring otherwise — e.g. '.ts' (suffix), 'test' (anywhere in the name). Patterns containing `/` match against the path relative to the search root; otherwise just the basename.\",\n },\n case_sensitive: {\n type: \"boolean\",\n description: \"When true, match case exactly. Default false (case-insensitive).\",\n },\n include_deps: {\n type: \"boolean\",\n description:\n \"When true, also search inside node_modules / .git / dist / build / etc. Off by default — most exploration questions are about the user's own code.\",\n },\n context: {\n type: \"integer\",\n description:\n \"Lines of context to show around each match (both before and after). Default 0 (just the matching line). Capped at 20. Output uses ripgrep style: `:` after the line number on the matching line, `-` on context lines, `--` separating non-adjacent windows.\",\n },\n summary_only: {\n type: \"boolean\",\n description:\n \"When true, skip line content and return one 'rel: N matches' line per matching file. Use for 'where does this exist at all' questions before drilling in with a targeted read_file.\",\n },\n },\n required: [\"pattern\"],\n },\n fn: async (\n args: {\n pattern: string;\n path?: string;\n glob?: string;\n case_sensitive?: boolean;\n include_deps?: boolean;\n context?: number;\n summary_only?: boolean;\n },\n toolCtx,\n ) =>\n searchContent(\n {\n rootDir,\n maxListBytes,\n skipDirNames: SKIP_DIR_NAMES,\n isBinaryByName: isLikelyBinaryByName,\n nameMatch: compileNameFilter(typeof args.glob === \"string\" ? args.glob : null),\n },\n await safePath(args.path ?? \".\", \"search_content\", toolCtx),\n { ...args, signal: toolCtx?.signal },\n ),\n });\n\n registry.register({\n name: \"glob\",\n parallelSafe: true,\n description:\n \"List files matching a glob pattern, sorted by mtime (most-recently-modified first) by default. Use this for 'what changed lately', 'find all *.test.ts', 'all configs under src/'. Glob syntax matches the cross-tool standard: `*` (any chars in one segment), `**` (any segments), `?` (one char), `{a,b}` (alternation). Pattern matches against the path RELATIVE to the search root (e.g. 'src/**/*.ts' from project root). Skips node_modules / .git / dist / build / etc by default. Default limit 200; raise via `limit` (max 1000). Different from `search_files` (substring on basename) and `search_content` (matches inside file contents).\",\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n pattern: {\n type: \"string\",\n description: \"Glob pattern, e.g. 'src/**/*.ts', '**/*.{md,mdx}', 'tests/*.test.ts'.\",\n },\n path: {\n type: \"string\",\n description:\n \"Base directory to walk (default: sandbox root). The pattern matches relative to this path.\",\n },\n sort_by: {\n type: \"string\",\n enum: [\"mtime\", \"name\"],\n description:\n \"Sort order. 'mtime' (default) shows most-recently-modified first — useful for 'what did I change today'. 'name' is alphabetical.\",\n },\n include_deps: {\n type: \"boolean\",\n description:\n \"When true, also walk node_modules / .git / dist / build / etc. Off by default.\",\n },\n limit: {\n type: \"integer\",\n description: \"Cap on returned matches. Default 200; clamped to [1, 1000].\",\n },\n },\n required: [\"pattern\"],\n },\n fn: async (\n args: {\n pattern: string;\n path?: string;\n sort_by?: \"mtime\" | \"name\";\n include_deps?: boolean;\n limit?: number;\n },\n toolCtx,\n ) =>\n globFiles(\n { rootDir, skipDirNames: SKIP_DIR_NAMES },\n await safePath(args.path ?? \".\", \"glob\", toolCtx),\n { ...args, signal: toolCtx?.signal },\n ),\n });\n\n registry.register({\n name: \"get_file_info\",\n parallelSafe: true,\n description:\n \"Stat a path under the sandbox root. Returns type (file|directory|symlink), size in bytes, mtime in ISO-8601.\",\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n },\n required: [\"path\"],\n },\n fn: async (args: { path: string }, ctx?: ToolCallContext) => {\n const abs = await safePath(args.path, \"get_file_info\", ctx);\n const st = await fs.lstat(abs);\n const type = st.isDirectory() ? \"directory\" : st.isSymbolicLink() ? \"symlink\" : \"file\";\n return JSON.stringify({\n type,\n size: st.size,\n mtime: st.mtime.toISOString(),\n });\n },\n });\n\n if (!allowWriting) return registry;\n\n registry.register({\n name: \"write_file\",\n description:\n \"Create or overwrite a file under the sandbox root with the given content. Parent directories are created as needed.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n content: { type: \"string\" },\n },\n required: [\"path\", \"content\"],\n },\n fn: async (args: { path: string; content: string }, ctx?: ToolCallContext) => {\n const abs = await safePath(args.path, \"write_file\", ctx, \"write\");\n await fs.mkdir(pathMod.dirname(abs), { recursive: true });\n await fs.writeFile(abs, args.content, \"utf8\");\n return `wrote ${args.content.length} chars to ${displayRel(rootDir, abs)}`;\n },\n });\n\n registry.register({\n name: \"edit_file\",\n description:\n \"Apply a SEARCH/REPLACE edit to an existing file. `search` must match exactly (whitespace sensitive) — no regex. The match must be unique in the file; otherwise the edit is refused to avoid surprise rewrites.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n search: { type: \"string\", description: \"Exact text to find (must be unique).\" },\n replace: { type: \"string\", description: \"Text to substitute in place of `search`.\" },\n },\n required: [\"path\", \"search\", \"replace\"],\n },\n fn: async (args: { path: string; search: string; replace: string }, ctx?: ToolCallContext) =>\n applyEdit(rootDir, await safePath(args.path, \"edit_file\", ctx, \"write\"), args),\n });\n\n registry.register({\n name: \"multi_edit\",\n description:\n \"Apply N SEARCH/REPLACE edits across ONE OR MORE files in a single atomic call. Edits run sequentially in array order; for edits that touch the same file, a later edit can match text inserted by an earlier one. If ANY edit fails (search not found, ambiguous match, empty search, file unreadable), NO files are written — atomic at the validation layer. Same per-edit rules as edit_file: `search` is exact text (whitespace sensitive, no regex) and must be unique in its target file at the moment that edit applies. Use this for renames spanning multiple files, cross-file refactors, or any batch where you'd otherwise loop edit_file.\",\n parameters: {\n type: \"object\",\n properties: {\n edits: {\n type: \"array\",\n description: \"Edits to apply in order. Length ≥ 1. Each edit names its own target file.\",\n items: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description: \"File the edit targets (sandbox-relative or absolute).\",\n },\n search: {\n type: \"string\",\n description: \"Exact text to find (must be unique in the file).\",\n },\n replace: { type: \"string\", description: \"Text to substitute in place of `search`.\" },\n },\n required: [\"path\", \"search\", \"replace\"],\n },\n },\n },\n required: [\"edits\"],\n },\n fn: async (\n args: { edits: Array<{ path: string; search: string; replace: string }> },\n ctx?: ToolCallContext,\n ) => {\n const resolved = await Promise.all(\n (args.edits ?? []).map(async (e) => ({\n abs: await safePath(e?.path, \"multi_edit\", ctx, \"write\"),\n search: e?.search,\n replace: e?.replace,\n })),\n );\n return applyMultiEdit(rootDir, resolved);\n },\n });\n\n registry.register({\n name: \"create_directory\",\n description: \"Create a directory (and any missing parents) under the sandbox root.\",\n parameters: {\n type: \"object\",\n properties: { path: { type: \"string\" } },\n required: [\"path\"],\n },\n fn: async (args: { path: string }, ctx?: ToolCallContext) => {\n const abs = await safePath(args.path, \"create_directory\", ctx, \"write\");\n await fs.mkdir(abs, { recursive: true });\n return `created ${displayRel(rootDir, abs)}/`;\n },\n });\n\n registry.register({\n name: \"move_file\",\n description: \"Rename/move a file or directory under the sandbox root.\",\n parameters: {\n type: \"object\",\n properties: {\n source: { type: \"string\" },\n destination: { type: \"string\" },\n },\n required: [\"source\", \"destination\"],\n },\n fn: async (args: { source: string; destination: string }, ctx?: ToolCallContext) => {\n const src = await safePath(args.source, \"move_file\", ctx, \"write\");\n const dst = await safePath(args.destination, \"move_file\", ctx, \"write\");\n await fs.mkdir(pathMod.dirname(dst), { recursive: true });\n await fs.rename(src, dst);\n return `moved ${displayRel(rootDir, src)} → ${displayRel(rootDir, dst)}`;\n },\n });\n\n registry.register({\n name: \"delete_file\",\n description:\n \"Delete one file under the sandbox root. Refuses directories — use delete_directory for those. Errors if the path doesn't exist.\",\n parameters: {\n type: \"object\",\n properties: { path: { type: \"string\" } },\n required: [\"path\"],\n },\n fn: async (args: { path: string }, ctx?: ToolCallContext) => {\n const abs = await safePath(args.path, \"delete_file\", ctx, \"write\");\n const st = await fs.lstat(abs);\n if (st.isDirectory()) {\n throw new Error(\n `delete_file: ${args.path} is a directory — use delete_directory to remove it`,\n );\n }\n await fs.unlink(abs);\n return `deleted ${displayRel(rootDir, abs)}`;\n },\n });\n\n registry.register({\n name: \"delete_directory\",\n description:\n \"Recursively delete a directory under the sandbox root. Pass `recursive:false` to refuse non-empty directories. Errors if the path doesn't exist.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n recursive: {\n type: \"boolean\",\n description:\n \"When true (default) deletes the directory and all its contents. When false, only removes empty directories — non-empty refuses with an error.\",\n },\n },\n required: [\"path\"],\n },\n fn: async (args: { path: string; recursive?: boolean }, ctx?: ToolCallContext) => {\n const abs = await safePath(args.path, \"delete_directory\", ctx, \"write\");\n const st = await fs.lstat(abs);\n if (!st.isDirectory()) {\n throw new Error(`delete_directory: ${args.path} is a file — use delete_file to remove it`);\n }\n const recursive = args.recursive !== false;\n // `fs.rm({recursive:false})` rejects every directory regardless of contents;\n // `fs.rmdir` is the empty-only variant we want when the caller said no recursion.\n if (recursive) {\n await fs.rm(abs, { recursive: true, force: false });\n } else {\n await fs.rmdir(abs);\n }\n return `deleted ${displayRel(rootDir, abs)}/${recursive ? \" (recursive)\" : \"\"}`;\n },\n });\n\n registry.register({\n name: \"copy_file\",\n description:\n \"Copy a file or directory under the sandbox root. Both source and destination resolve under the sandbox. Parent directories of the destination are created as needed. Refuses to overwrite an existing destination — delete it first if you want to replace it.\",\n parameters: {\n type: \"object\",\n properties: {\n source: { type: \"string\" },\n destination: { type: \"string\" },\n },\n required: [\"source\", \"destination\"],\n },\n fn: async (args: { source: string; destination: string }, ctx?: ToolCallContext) => {\n const src = await safePath(args.source, \"copy_file\", ctx);\n const dst = await safePath(args.destination, \"copy_file\", ctx, \"write\");\n await fs.mkdir(pathMod.dirname(dst), { recursive: true });\n await fs.cp(src, dst, { recursive: true, force: false, errorOnExist: true });\n return `copied ${displayRel(rootDir, src)} → ${displayRel(rootDir, dst)}`;\n },\n });\n\n return registry;\n}\n","import { promises as fs } from \"node:fs\";\nimport * as pathMod from \"node:path\";\n\nfunction displayRel(rootDir: string, full: string): string {\n return pathMod.relative(rootDir, full).replaceAll(\"\\\\\", \"/\");\n}\n\nexport async function applyEdit(\n rootDir: string,\n abs: string,\n args: { search: string; replace: string },\n): Promise<string> {\n if (args.search.length === 0) {\n throw new Error(\"edit_file: search cannot be empty\");\n }\n const before = await fs.readFile(abs, \"utf8\");\n const le = before.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const adaptedSearch = args.search.replace(/\\r?\\n/g, le);\n const adaptedReplace = args.replace.replace(/\\r?\\n/g, le);\n const firstIdx = before.indexOf(adaptedSearch);\n if (firstIdx < 0) {\n throw new Error(`edit_file: search text not found in ${displayRel(rootDir, abs)}`);\n }\n const nextIdx = before.indexOf(adaptedSearch, firstIdx + 1);\n if (nextIdx >= 0) {\n throw new Error(\n `edit_file: search text appears multiple times in ${displayRel(rootDir, abs)} — include more context to disambiguate`,\n );\n }\n const after =\n before.slice(0, firstIdx) + adaptedReplace + before.slice(firstIdx + adaptedSearch.length);\n await fs.writeFile(abs, after, \"utf8\");\n const rel = displayRel(rootDir, abs);\n const header = `edited ${rel} (${adaptedSearch.length}→${adaptedReplace.length} chars)`;\n const startLine = before.slice(0, firstIdx).split(/\\r?\\n/).length;\n const diff = renderEditDiff(adaptedSearch, adaptedReplace, startLine);\n return `${header}\\n${diff}`;\n}\n\nexport interface MultiEditEntry {\n abs: string;\n search: string;\n replace: string;\n}\n\nexport async function applyMultiEdit(\n rootDir: string,\n edits: ReadonlyArray<MultiEditEntry>,\n): Promise<string> {\n if (edits.length === 0) {\n throw new Error(\"multi_edit: edits must contain at least one entry\");\n }\n type FileState = {\n buf: string;\n le: string;\n hunks: string[];\n deltaChars: number;\n touched: number;\n };\n const filesByPath = new Map<string, FileState>();\n\n for (let i = 0; i < edits.length; i++) {\n const e = edits[i]!;\n if (typeof e.abs !== \"string\" || e.abs.length === 0) {\n throw new Error(`multi_edit: edit #${i + 1} requires a string \\`path\\` (no edits applied)`);\n }\n if (typeof e.search !== \"string\") {\n throw new Error(`multi_edit: edit #${i + 1} requires a string \\`search\\` (no edits applied)`);\n }\n if (typeof e.replace !== \"string\") {\n throw new Error(\n `multi_edit: edit #${i + 1} requires a string \\`replace\\` (no edits applied)`,\n );\n }\n const rel = displayRel(rootDir, e.abs);\n if (e.search.length === 0) {\n throw new Error(\n `multi_edit: edit #${i + 1} (${rel}) search cannot be empty (no edits applied)`,\n );\n }\n let state = filesByPath.get(e.abs);\n if (!state) {\n let before: string;\n try {\n before = await fs.readFile(e.abs, \"utf8\");\n } catch (err) {\n throw new Error(\n `multi_edit: edit #${i + 1} cannot read ${rel}: ${(err as Error).message} (no edits applied)`,\n );\n }\n const le = before.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n state = { buf: before, le, hunks: [], deltaChars: 0, touched: 0 };\n filesByPath.set(e.abs, state);\n }\n const adaptedSearch = e.search.replace(/\\r?\\n/g, state.le);\n const adaptedReplace = e.replace.replace(/\\r?\\n/g, state.le);\n const firstIdx = state.buf.indexOf(adaptedSearch);\n if (firstIdx < 0) {\n throw new Error(\n `multi_edit: edit #${i + 1} search text not found in ${rel} — no edits applied (multi_edit is atomic)`,\n );\n }\n const nextIdx = state.buf.indexOf(adaptedSearch, firstIdx + 1);\n if (nextIdx >= 0) {\n throw new Error(\n `multi_edit: edit #${i + 1} search text appears multiple times in ${rel} — include more context to disambiguate (no edits applied)`,\n );\n }\n const startLine = state.buf.slice(0, firstIdx).split(/\\r?\\n/).length;\n state.buf =\n state.buf.slice(0, firstIdx) +\n adaptedReplace +\n state.buf.slice(firstIdx + adaptedSearch.length);\n state.hunks.push(`# ${rel}\\n${renderEditDiff(adaptedSearch, adaptedReplace, startLine)}`);\n state.deltaChars += adaptedReplace.length - adaptedSearch.length;\n state.touched++;\n }\n\n for (const [abs, state] of filesByPath) {\n await fs.writeFile(abs, state.buf, \"utf8\");\n }\n\n const fileCount = filesByPath.size;\n const editCount = edits.length;\n let totalDelta = 0;\n const allHunks: string[] = [];\n for (const state of filesByPath.values()) {\n totalDelta += state.deltaChars;\n allHunks.push(...state.hunks);\n }\n const sign = totalDelta >= 0 ? \"+\" : \"\";\n const editNoun = editCount === 1 ? \"edit\" : \"edits\";\n const fileNoun = fileCount === 1 ? \"file\" : \"files\";\n const header = `multi_edit: applied ${editCount} ${editNoun} across ${fileCount} ${fileNoun} (${sign}${totalDelta} chars)`;\n return `${header}\\n${allHunks.join(\"\\n\")}`;\n}\n\nfunction renderEditDiff(search: string, replace: string, startLine: number): string {\n const a = search.split(/\\r?\\n/);\n const b = replace.split(/\\r?\\n/);\n const diff = lineDiff(a, b);\n const hunk = `@@ -${startLine},${a.length} +${startLine},${b.length} @@`;\n const body = diff.map((d) => `${d.op === \" \" ? \" \" : d.op} ${d.line}`).join(\"\\n\");\n return `${hunk}\\n${body}`;\n}\n\nexport function lineDiff(\n a: readonly string[],\n b: readonly string[],\n): Array<{ op: \"-\" | \"+\" | \" \"; line: string }> {\n const n = a.length;\n const m = b.length;\n // dp[i][j] = LCS length of a[0..i) and b[0..j).\n const dp: number[][] = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));\n for (let i = 1; i <= n; i++) {\n for (let j = 1; j <= m; j++) {\n if (a[i - 1] === b[j - 1]) dp[i]![j] = dp[i - 1]![j - 1]! + 1;\n else dp[i]![j] = Math.max(dp[i - 1]![j]!, dp[i]![j - 1]!);\n }\n }\n // Backtrack to recover the op sequence.\n const out: Array<{ op: \"-\" | \"+\" | \" \"; line: string }> = [];\n let i = n;\n let j = m;\n while (i > 0 && j > 0) {\n if (a[i - 1] === b[j - 1]) {\n out.unshift({ op: \" \", line: a[i - 1]! });\n i--;\n j--;\n } else if ((dp[i - 1]![j] ?? 0) > (dp[i]![j - 1] ?? 0)) {\n out.unshift({ op: \"-\", line: a[i - 1]! });\n i--;\n } else {\n // Tie-break goes here (strictly less or equal): take the\n // insertion first during backtrack so the final forward order\n // renders removals BEFORE additions for a substitution —\n // matches git-diff convention of `- old / + new`.\n out.unshift({ op: \"+\", line: b[j - 1]! });\n j--;\n }\n }\n while (i > 0) {\n out.unshift({ op: \"-\", line: a[i - 1]! });\n i--;\n }\n while (j > 0) {\n out.unshift({ op: \"+\", line: b[j - 1]! });\n j--;\n }\n return out;\n}\n","import { promises as fs } from \"node:fs\";\nimport * as pathMod from \"node:path\";\nimport picomatch from \"picomatch\";\n\nexport interface GlobContext {\n rootDir: string;\n skipDirNames: ReadonlySet<string>;\n}\n\nfunction displayRel(rootDir: string, full: string): string {\n return pathMod.relative(rootDir, full).replaceAll(\"\\\\\", \"/\");\n}\n\nexport async function globFiles(\n ctx: GlobContext,\n startAbs: string,\n args: {\n pattern: string;\n sort_by?: \"mtime\" | \"name\";\n include_deps?: boolean;\n limit?: number;\n signal?: AbortSignal;\n },\n): Promise<string> {\n if (args.signal?.aborted) {\n throw new DOMException(\"glob aborted by user\", \"AbortError\");\n }\n const includeDeps = args.include_deps === true;\n const sortBy = args.sort_by ?? \"mtime\";\n const limit = Math.max(1, Math.min(1000, Math.floor(args.limit ?? 200)));\n const isMatch = picomatch(args.pattern, { dot: true, nocase: true });\n\n const hits: { rel: string; mtimeMs: number }[] = [];\n\n const walk = async (dir: string): Promise<void> => {\n if (args.signal?.aborted) {\n throw new DOMException(\"glob aborted by user\", \"AbortError\");\n }\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n const full = pathMod.join(dir, e.name);\n if (e.isDirectory()) {\n if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;\n await walk(full);\n continue;\n }\n if (!e.isFile() && !e.isSymbolicLink()) continue;\n const rel = displayRel(ctx.rootDir, full);\n if (!isMatch(rel)) continue;\n let mtimeMs = 0;\n if (sortBy === \"mtime\") {\n try {\n const st = await fs.stat(full);\n mtimeMs = st.mtimeMs;\n } catch {\n continue;\n }\n }\n hits.push({ rel, mtimeMs });\n }\n };\n await walk(startAbs);\n\n if (hits.length === 0) return \"(no matches)\";\n if (sortBy === \"mtime\") hits.sort((a, b) => b.mtimeMs - a.mtimeMs);\n else hits.sort((a, b) => a.rel.localeCompare(b.rel));\n\n const truncated = hits.length > limit;\n const shown = hits.slice(0, limit);\n const lines = shown.map((h) => h.rel);\n if (truncated) {\n lines.push(\n `[… ${hits.length - limit} more matches — refine pattern or raise limit (max 1000) …]`,\n );\n }\n return lines.join(\"\\n\");\n}\n","/** Per-language top-level symbol outline for read_file preview. Regex-anchored at column 0 — nested decls intentionally skipped. */\n\nimport * as pathMod from \"node:path\";\n\nexport type OutlineEntry = { line: number; text: string };\n\nconst OUTLINE_MAX_ENTRIES = 30;\nconst OUTLINE_TAIL_KEEP = 5;\n\nconst TS_EXPORT_RE =\n /^export\\s+(?:default\\s+)?(?:async\\s+)?(function|class|const|let|var|interface|type|enum)\\s+\\*?\\s*(\\w+)/;\n\nconst PY_DECL_RE = /^(?:async\\s+)?(def|class)\\s+(\\w+)/;\n\nconst GO_DECL_RE = /^(func|type|var|const)\\s+(?:\\([^)]+\\)\\s+)?(\\w+)/;\n\nconst RUST_DECL_RE =\n /^(?:pub(?:\\([^)]+\\))?\\s+)?(?:async\\s+)?(?:unsafe\\s+)?(fn|struct|enum|trait|mod|type|const|static|union)\\s+(\\w+)/;\n\nconst RUST_IMPL_RE = /^(?:unsafe\\s+)?impl(?:\\s*<[^>]+>)?\\s+(?:[^{]+\\s+for\\s+)?(\\w+)/;\n\nconst MD_HEADING_RE = /^(#{1,6})\\s+(.+?)\\s*$/;\n\nconst MD_FENCE_RE = /^```/;\n\ntype Lang = \"ts\" | \"py\" | \"go\" | \"rust\" | \"md\";\n\nconst EXT_TO_LANG: Record<string, Lang> = {\n \".ts\": \"ts\",\n \".tsx\": \"ts\",\n \".mts\": \"ts\",\n \".cts\": \"ts\",\n \".js\": \"ts\",\n \".jsx\": \"ts\",\n \".mjs\": \"ts\",\n \".cjs\": \"ts\",\n \".py\": \"py\",\n \".pyi\": \"py\",\n \".go\": \"go\",\n \".rs\": \"rust\",\n \".md\": \"md\",\n \".markdown\": \"md\",\n \".mdx\": \"md\",\n};\n\nexport function extractOutline(filename: string, lines: readonly string[]): OutlineEntry[] {\n const ext = pathMod.extname(filename).toLowerCase();\n const lang = EXT_TO_LANG[ext];\n if (!lang) return [];\n switch (lang) {\n case \"ts\":\n return extractTs(lines);\n case \"py\":\n return extractPython(lines);\n case \"go\":\n return extractGo(lines);\n case \"rust\":\n return extractRust(lines);\n case \"md\":\n return extractMarkdown(lines);\n }\n}\n\nfunction extractTs(lines: readonly string[]): OutlineEntry[] {\n const out: OutlineEntry[] = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (!line.startsWith(\"export \")) continue;\n const m = TS_EXPORT_RE.exec(line);\n if (!m) continue;\n out.push({ line: i + 1, text: `export ${m[1]} ${m[2]}` });\n }\n return out;\n}\n\nfunction extractPython(lines: readonly string[]): OutlineEntry[] {\n const out: OutlineEntry[] = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (line.startsWith(\" \") || line.startsWith(\"\\t\")) continue;\n const m = PY_DECL_RE.exec(line);\n if (!m) continue;\n out.push({ line: i + 1, text: `${m[1]} ${m[2]}` });\n }\n return out;\n}\n\nfunction extractGo(lines: readonly string[]): OutlineEntry[] {\n const out: OutlineEntry[] = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (line.startsWith(\" \") || line.startsWith(\"\\t\")) continue;\n const m = GO_DECL_RE.exec(line);\n if (!m) continue;\n out.push({ line: i + 1, text: `${m[1]} ${m[2]}` });\n }\n return out;\n}\n\nfunction extractRust(lines: readonly string[]): OutlineEntry[] {\n const out: OutlineEntry[] = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (line.startsWith(\" \") || line.startsWith(\"\\t\")) continue;\n const implMatch = RUST_IMPL_RE.exec(line);\n if (implMatch) {\n out.push({ line: i + 1, text: `impl ${implMatch[1]}` });\n continue;\n }\n const m = RUST_DECL_RE.exec(line);\n if (!m) continue;\n out.push({ line: i + 1, text: `${m[1]} ${m[2]}` });\n }\n return out;\n}\n\nfunction extractMarkdown(lines: readonly string[]): OutlineEntry[] {\n const out: OutlineEntry[] = [];\n let inFence = false;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (MD_FENCE_RE.test(line)) {\n inFence = !inFence;\n continue;\n }\n if (inFence) continue;\n const m = MD_HEADING_RE.exec(line);\n if (!m) continue;\n out.push({ line: i + 1, text: `${m[1]} ${m[2]}` });\n }\n return out;\n}\n\nexport function formatOutline(entries: readonly OutlineEntry[]): string {\n const total = entries.length;\n if (total === 0) return \"\";\n const lastEntry = entries[total - 1]!;\n const width = String(lastEntry.line).length;\n const fmt = (e: OutlineEntry) => ` L${String(e.line).padStart(width, \" \")} ${e.text}`;\n const header = `[outline: ${total} symbol${total === 1 ? \"\" : \"s\"}]`;\n if (total <= OUTLINE_MAX_ENTRIES) {\n return [header, ...entries.map(fmt)].join(\"\\n\");\n }\n const headCount = OUTLINE_MAX_ENTRIES - OUTLINE_TAIL_KEEP;\n const headEntries = entries.slice(0, headCount);\n const tailEntries = entries.slice(-OUTLINE_TAIL_KEEP);\n const omitted = total - OUTLINE_MAX_ENTRIES;\n const gapStart = headEntries[headEntries.length - 1]!.line;\n const gapEnd = tailEntries[0]!.line;\n return [\n header,\n ...headEntries.map(fmt),\n ` [… ${omitted} more symbol${omitted === 1 ? \"\" : \"s\"} between L${gapStart} and L${gapEnd} …]`,\n ...tailEntries.map(fmt),\n ].join(\"\\n\");\n}\n","import { promises as fs } from \"node:fs\";\nimport * as pathMod from \"node:path\";\n\nexport interface SearchContext {\n rootDir: string;\n maxListBytes: number;\n skipDirNames: ReadonlySet<string>;\n isBinaryByName: (name: string) => boolean;\n /** Pre-baked filename→regex/substring matcher; null when no glob filter. */\n nameMatch: ((name: string, rel: string) => boolean) | null;\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (!signal?.aborted) return;\n throw new DOMException(\"search aborted by user\", \"AbortError\");\n}\n\nfunction displayRel(rootDir: string, full: string): string {\n return pathMod.relative(rootDir, full).replaceAll(\"\\\\\", \"/\");\n}\n\nexport async function searchFiles(\n ctx: Pick<SearchContext, \"rootDir\" | \"maxListBytes\" | \"skipDirNames\">,\n startAbs: string,\n args: { pattern: string; include_deps?: boolean; signal?: AbortSignal },\n): Promise<string> {\n throwIfAborted(args.signal);\n const needle = args.pattern.toLowerCase();\n const includeDeps = args.include_deps === true;\n let re: RegExp | null = null;\n try {\n re = new RegExp(args.pattern, \"i\");\n } catch {\n re = null;\n }\n const matches: string[] = [];\n let totalBytes = 0;\n const walk = async (dir: string): Promise<void> => {\n throwIfAborted(args.signal);\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n throwIfAborted(args.signal);\n const full = pathMod.join(dir, e.name);\n const lower = e.name.toLowerCase();\n const hit = re ? re.test(e.name) : lower.includes(needle);\n if (hit) {\n const rel = displayRel(ctx.rootDir, full);\n if (totalBytes + rel.length + 1 > ctx.maxListBytes) {\n matches.push(\"[… search truncated — refine pattern …]\");\n return;\n }\n matches.push(rel);\n totalBytes += rel.length + 1;\n }\n if (e.isDirectory()) {\n if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;\n await walk(full);\n }\n }\n };\n await walk(startAbs);\n return matches.length === 0 ? \"(no matches)\" : matches.join(\"\\n\");\n}\n\n/** Per-file printed-hit cap; beyond this we emit a \"N more matches in this file\" footer. */\nconst MAX_HITS_PER_FILE = 30;\n/** Once printed bytes pass this fraction of the byte budget, remaining files switch to histogram. */\nconst SUMMARY_MODE_TRIGGER_RATIO = 0.8;\n\nexport async function searchContent(\n ctx: SearchContext,\n startAbs: string,\n args: {\n pattern: string;\n case_sensitive?: boolean;\n include_deps?: boolean;\n context?: number;\n /** Skip line content; return only \"rel: N matches\" per file. */\n summary_only?: boolean;\n signal?: AbortSignal;\n },\n): Promise<string> {\n throwIfAborted(args.signal);\n const caseSensitive = args.case_sensitive === true;\n const includeDeps = args.include_deps === true;\n const ctxLines = Math.max(0, Math.min(20, Math.floor(args.context ?? 0)));\n const summaryOnly = args.summary_only === true;\n let re: RegExp | null = null;\n try {\n re = new RegExp(args.pattern, caseSensitive ? \"\" : \"i\");\n } catch {\n re = null;\n }\n const needle = caseSensitive ? args.pattern : args.pattern.toLowerCase();\n const matches: string[] = [];\n let totalBytes = 0;\n let scanned = 0;\n let truncated = false;\n let summaryMode = summaryOnly;\n let summaryNoticeEmitted = false;\n const fileHitCounts = new Map<string, number>();\n\n const pushLine = (out: string): boolean => {\n if (totalBytes + out.length + 1 > ctx.maxListBytes) {\n matches.push(`[… truncated at ${ctx.maxListBytes} bytes — refine pattern or path …]`);\n truncated = true;\n return false;\n }\n matches.push(out);\n totalBytes += out.length + 1;\n return true;\n };\n\n const maybeEnterSummaryMode = (): void => {\n if (summaryMode) return;\n if (totalBytes <= SUMMARY_MODE_TRIGGER_RATIO * ctx.maxListBytes) return;\n summaryMode = true;\n if (!summaryNoticeEmitted) {\n const pct = Math.round((totalBytes / ctx.maxListBytes) * 100);\n pushLine(\n `[switching to summary mode — byte budget at ${pct}%; remaining files will report match counts only]`,\n );\n summaryNoticeEmitted = true;\n }\n };\n\n const walk = async (dir: string): Promise<void> => {\n if (truncated) return;\n throwIfAborted(args.signal);\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (truncated) return;\n throwIfAborted(args.signal);\n if (e.isDirectory()) {\n if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;\n await walk(pathMod.join(dir, e.name));\n continue;\n }\n if (!e.isFile()) continue;\n const full = pathMod.join(dir, e.name);\n if (ctx.nameMatch && !ctx.nameMatch(e.name, displayRel(ctx.rootDir, full))) continue;\n if (ctx.isBinaryByName(e.name)) continue;\n let fh: import(\"node:fs/promises\").FileHandle;\n try {\n fh = await fs.open(full, \"r\");\n } catch {\n continue;\n }\n let raw: Buffer;\n try {\n throwIfAborted(args.signal);\n const st = await fh.stat();\n if (st.size > 2 * 1024 * 1024) {\n await fh.close();\n continue;\n }\n raw = await fh.readFile();\n } catch {\n await fh.close().catch(() => {});\n continue;\n }\n await fh.close();\n throwIfAborted(args.signal);\n const firstNul = raw.indexOf(0);\n if (firstNul !== -1 && firstNul < 8 * 1024) continue;\n const text = raw.toString(\"utf8\");\n const rel = displayRel(ctx.rootDir, full);\n const lines = text.split(/\\r?\\n/);\n const hits: number[] = [];\n for (let li = 0; li < lines.length; li++) {\n throwIfAborted(args.signal);\n const line = lines[li]!;\n const lineForCheck = caseSensitive ? line : line.toLowerCase();\n const hit = re ? re.test(line) : lineForCheck.includes(needle);\n if (hit) hits.push(li);\n }\n scanned++;\n if (hits.length === 0) continue;\n fileHitCounts.set(rel, hits.length);\n\n if (summaryMode) {\n if (!pushLine(`${rel}: ${hits.length} match${hits.length === 1 ? \"\" : \"es\"}`)) return;\n continue;\n }\n\n const printable = Math.min(hits.length, MAX_HITS_PER_FILE);\n const omittedFromFile = hits.length - printable;\n const printableHits = hits.slice(0, printable);\n\n if (ctxLines === 0) {\n for (const li of printableHits) {\n if (truncated) return;\n const line = lines[li]!;\n const display = line.length > 200 ? `${line.slice(0, 200)}…` : line;\n if (!pushLine(`${rel}:${li + 1}: ${display}`)) return;\n }\n } else {\n const hitSet = new Set(printableHits);\n let prevWindowEnd = -2;\n for (const li of printableHits) {\n if (truncated) return;\n const winStart = Math.max(0, li - ctxLines);\n const winEnd = Math.min(lines.length - 1, li + ctxLines);\n if (winStart > prevWindowEnd + 1 && prevWindowEnd >= 0) {\n if (!pushLine(\"--\")) return;\n }\n const realStart = winStart > prevWindowEnd + 1 ? winStart : prevWindowEnd + 1;\n for (let i = realStart; i <= winEnd; i++) {\n const line = lines[i]!;\n const display = line.length > 200 ? `${line.slice(0, 200)}…` : line;\n const sep = hitSet.has(i) ? \":\" : \"-\";\n if (!pushLine(`${rel}:${i + 1}${sep} ${display}`)) return;\n }\n prevWindowEnd = winEnd;\n }\n }\n\n if (omittedFromFile > 0) {\n if (\n !pushLine(\n `[${rel}: ${omittedFromFile} more match${omittedFromFile === 1 ? \"\" : \"es\"} in this file — re-grep with a tighter pattern or use read_file to see them]`,\n )\n )\n return;\n }\n\n maybeEnterSummaryMode();\n }\n };\n await walk(startAbs);\n if (matches.length === 0) {\n return scanned === 0\n ? \"(no files scanned — path empty or all files filtered out)\"\n : `(no matches across ${scanned} file${scanned === 1 ? \"\" : \"s\"})`;\n }\n return matches.join(\"\\n\");\n}\n","/** Writes are eager but the prefix is NOT re-loaded mid-session — keeps prompt-cache stable. */\n\nimport { loadMemoryTypeRegistry } from \"../config.js\";\nimport {\n type MemoryExpires,\n type MemoryPriority,\n type MemoryScope,\n MemoryStore,\n type MemoryType,\n sanitizeMemoryName,\n} from \"../memory/user.js\";\nimport type { ToolRegistry } from \"../tools.js\";\n\nexport interface MemoryToolsOptions {\n /** Sandbox root for the `project` scope. Omit for chat mode. */\n projectRoot?: string;\n /** Override `~/.luckerr` (tests). */\n homeDir?: string;\n}\n\nexport function registerMemoryTools(\n registry: ToolRegistry,\n opts: MemoryToolsOptions = {},\n): ToolRegistry {\n const store = new MemoryStore({ homeDir: opts.homeDir, projectRoot: opts.projectRoot });\n const hasProject = store.hasProjectScope();\n\n const registry_types = loadMemoryTypeRegistry();\n const customTypeNames = registry_types.filter((r) => !r.builtin).map((r) => r.name);\n const typeDescParts = [\n \"'user' = role/skills/prefs; 'feedback' = corrections or confirmed approaches; 'project' = facts/decisions about the current work; 'reference' = pointers to external systems the user uses.\",\n ];\n if (customTypeNames.length > 0) {\n typeDescParts.push(\n `Custom types declared in config: ${customTypeNames.join(\", \")}. Any string is accepted; unknown types are stored verbatim and treated as 'reference' priority.`,\n );\n }\n\n registry.register({\n name: \"remember\",\n description:\n \"Save a memory for future sessions. Use when the user states a preference, corrects your approach, shares a non-obvious fact about this project, or explicitly asks you to remember something. Don't remember transient task state — only things worth recalling next session. The memory is written now but won't re-load into the system prompt until the next `/new` or launch.\",\n parameters: {\n type: \"object\",\n properties: {\n type: {\n type: \"string\",\n description: typeDescParts.join(\" \"),\n },\n scope: {\n type: \"string\",\n enum: [\"global\", \"project\"],\n description:\n \"'global' = applies across every project (preferences, tooling); 'project' = scoped to the current sandbox (decisions, local facts). Only available in `luckerr code`.\",\n },\n name: {\n type: \"string\",\n description:\n \"filename-safe identifier, 3-40 chars, alnum + _ - . (no path separators, no leading dot).\",\n },\n description: {\n type: \"string\",\n description: \"One-line summary shown in MEMORY.md (under ~150 chars).\",\n },\n content: {\n type: \"string\",\n description:\n \"Full memory body in markdown. For feedback/project types, structure as: rule/fact, then **Why:** line, then **How to apply:** line.\",\n },\n priority: {\n type: \"string\",\n enum: [\"low\", \"medium\", \"high\"],\n description:\n \"Optional per-memory priority. `high` injects the entry into a `# HIGH PRIORITY constraints` block at the top of the system prompt — use sparingly, only for hard rules the model must never violate.\",\n },\n expires: {\n type: \"string\",\n enum: [\"project_end\"],\n description:\n \"Optional lifecycle hint. `project_end` causes `/memory clear project` to also remove this entry even when it's stored at global scope.\",\n },\n },\n required: [\"type\", \"scope\", \"name\", \"description\", \"content\"],\n },\n fn: async (args: {\n type: MemoryType;\n scope: MemoryScope;\n name: string;\n description: string;\n content: string;\n priority?: MemoryPriority;\n expires?: MemoryExpires;\n }) => {\n if (args.scope === \"project\" && !hasProject) {\n return JSON.stringify({\n error:\n \"scope='project' is unavailable in this session (no sandbox root). Retry with scope='global', or ask the user to switch to `luckerr code` for project-scoped memory.\",\n });\n }\n try {\n const path = store.write({\n name: args.name,\n type: args.type,\n scope: args.scope,\n description: args.description,\n body: args.content,\n ...(args.priority ? { priority: args.priority } : {}),\n ...(args.expires ? { expires: args.expires } : {}),\n });\n const key = sanitizeMemoryName(args.name);\n // The return text is load-bearing: it's the ONLY thing keeping\n // the fact visible within the current session, because the\n // prefix isn't re-hashed mid-session (Pillar 1). R1 reads this\n // on its next turn — the wording is deliberately imperative so\n // it doesn't get ignored in favor of explore-first behavior.\n return [\n `✓ REMEMBERED (${args.scope}/${key}): ${args.description}`,\n \"\",\n \"TREAT THIS AS ESTABLISHED FACT for the rest of this session.\",\n \"The user just told you — don't re-explore the filesystem to re-derive it.\",\n `(Saved to ${path}; pins into the system prompt on next /new or launch.)`,\n ].join(\"\\n\");\n } catch (err) {\n return JSON.stringify({ error: `remember failed: ${(err as Error).message}` });\n }\n },\n });\n\n registry.register({\n name: \"forget\",\n description:\n \"Delete a memory file and remove it from MEMORY.md. Use when the user explicitly asks to forget something, or when a previously-remembered fact has become wrong. Irreversible — no tombstone.\",\n parameters: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"Memory name (the identifier used in `remember`).\" },\n scope: { type: \"string\", enum: [\"global\", \"project\"] },\n },\n required: [\"name\", \"scope\"],\n },\n fn: async (args: { name: string; scope: MemoryScope }) => {\n if (args.scope === \"project\" && !hasProject) {\n return JSON.stringify({\n error: \"scope='project' is unavailable in this session (no sandbox root).\",\n });\n }\n try {\n const existed = store.delete(args.scope, args.name);\n return existed\n ? `forgot (${args.scope}/${sanitizeMemoryName(args.name)}). Re-load on next /new or launch.`\n : `no such memory: ${args.scope}/${args.name} (nothing to forget).`;\n } catch (err) {\n return JSON.stringify({ error: `forget failed: ${(err as Error).message}` });\n }\n },\n });\n\n registry.register({\n name: \"recall_memory\",\n description:\n \"Read the full body of a memory file when its MEMORY.md one-liner (already in the system prompt) isn't enough detail. Most of the time the index suffices — only call this when the user's question genuinely requires the full context.\",\n readOnly: true,\n parallelSafe: true,\n parameters: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n scope: { type: \"string\", enum: [\"global\", \"project\"] },\n },\n required: [\"name\", \"scope\"],\n },\n fn: async (args: { name: string; scope: MemoryScope }) => {\n if (args.scope === \"project\" && !hasProject) {\n return JSON.stringify({\n error: \"scope='project' is unavailable in this session (no sandbox root).\",\n });\n }\n try {\n const entry = store.read(args.scope, args.name);\n return [\n `# ${entry.name} (${entry.scope}/${entry.type}, created ${entry.createdAt || \"?\"})`,\n entry.description ? `> ${entry.description}` : \"\",\n \"\",\n entry.body,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n } catch (err) {\n return JSON.stringify({ error: `recall failed: ${(err as Error).message}` });\n }\n },\n });\n\n return registry;\n}\n","/** Branching primitive separate from submit_plan; throws ChoiceRequestedError so the TUI can mount a picker and the model stops. */\n\nimport { pauseGate } from \"../core/pause-gate.js\";\nimport type { ToolRegistry } from \"../tools.js\";\n\nexport interface ChoiceOption {\n id: string;\n title: string;\n summary?: string;\n}\n\nexport class ChoiceRequestedError extends Error {\n readonly question: string;\n readonly options: ChoiceOption[];\n readonly allowCustom: boolean;\n constructor(question: string, options: ChoiceOption[], allowCustom: boolean) {\n super(\n \"ChoiceRequestedError: choice submitted. STOP calling tools now — the TUI has shown the options to the user. Wait for their next message; it will either be 'user picked <id>' (carry on with that branch), 'user answered: <text>' (custom free-form reply; read and proceed), or 'user cancelled the choice' (drop the question and ask what they want instead). Don't call any tools in the meantime.\",\n );\n this.name = \"ChoiceRequestedError\";\n this.question = question;\n this.options = options;\n this.allowCustom = allowCustom;\n }\n\n toToolResult(): {\n error: string;\n question: string;\n options: ChoiceOption[];\n allowCustom: boolean;\n } {\n return {\n error: `${this.name}: ${this.message}`,\n question: this.question,\n options: this.options,\n allowCustom: this.allowCustom,\n };\n }\n}\n\nexport interface ChoiceToolOptions {\n onChoiceRequested?: (question: string, options: ChoiceOption[]) => void;\n}\n\nfunction sanitizeOptions(raw: unknown): ChoiceOption[] {\n if (!Array.isArray(raw)) return [];\n const out: ChoiceOption[] = [];\n const seen = new Set<string>();\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const e = entry as Record<string, unknown>;\n const id = typeof e.id === \"string\" ? e.id.trim() : \"\";\n const title = typeof e.title === \"string\" ? e.title.trim() : \"\";\n if (!id || !title) continue;\n if (seen.has(id)) continue;\n seen.add(id);\n const summary = typeof e.summary === \"string\" ? e.summary.trim() || undefined : undefined;\n const opt: ChoiceOption = { id, title };\n if (summary) opt.summary = summary;\n out.push(opt);\n }\n return out;\n}\n\nexport function registerChoiceTool(\n registry: ToolRegistry,\n opts: ChoiceToolOptions = {},\n): ToolRegistry {\n registry.register({\n name: \"ask_choice\",\n description:\n \"Present 2–6 alternatives to the user. The principle: if the user is supposed to pick, the tool picks — you don't enumerate the choices as prose. Prose menus have no picker in this TUI, so the user gets a wall of text to scroll through and a letter to type, strictly worse than the magenta picker this tool renders. Call it whenever (a) the user has asked for options, (b) you've analyzed multiple approaches and the final call is theirs, or (c) it's a preference fork you can't resolve without them. Skip it when one option is clearly best (just do it, or submit_plan) or a free-form text answer fits (ask in prose). Keep option ids short and stable (A/B/C). Each option: title + optional summary. allowCustom=true when their real answer might not fit. Max 6 options — narrow first if more. A one-sentence lead-in before the call is fine; don't repeat the options in it.\",\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n question: {\n type: \"string\",\n description:\n \"The question to put in front of the user. One sentence. Don't repeat the options in the question text — the picker renders them separately.\",\n },\n options: {\n type: \"array\",\n description:\n \"2–4 alternatives. Each needs a stable id and a short title; summary is optional.\",\n items: {\n type: \"object\",\n properties: {\n id: { type: \"string\", description: \"Short stable id (A, B, C, or option-1).\" },\n title: { type: \"string\", description: \"One-line title shown as the option label.\" },\n summary: {\n type: \"string\",\n description:\n \"Optional. A second dimmed line with more detail. Keep under ~80 chars.\",\n },\n },\n required: [\"id\", \"title\"],\n },\n },\n allowCustom: {\n type: \"boolean\",\n description:\n \"If true, the picker shows a 'Let me type my own answer' escape hatch. Default false. Turn on when the user's real answer might not fit any of your pre-defined options.\",\n },\n },\n required: [\"question\", \"options\"],\n },\n fn: async (args: { question: string; options: unknown; allowCustom?: boolean }, ctx) => {\n const question = (args?.question ?? \"\").trim();\n if (!question) {\n throw new Error(\n \"ask_choice: question is required — write one sentence explaining the decision.\",\n );\n }\n const options = sanitizeOptions(args?.options);\n if (options.length < 2) {\n throw new Error(\n \"ask_choice: need at least 2 well-formed options (each with a non-empty id and title). If you just need a text answer, ask the user in plain assistant text instead.\",\n );\n }\n if (options.length > 6) {\n throw new Error(\n \"ask_choice: too many options (max 6). If you really have this many branches, split into two sequential ask_choice calls or narrow down first.\",\n );\n }\n const allowCustom = args?.allowCustom === true;\n opts.onChoiceRequested?.(question, options);\n // Block until the user picks an option, types custom text, or cancels\n const verdict = await (ctx?.confirmationGate ?? pauseGate).ask({\n kind: \"choice\",\n payload: { question, options, allowCustom },\n });\n if (verdict.type === \"pick\") return `user picked: ${verdict.optionId}`;\n if (verdict.type === \"text\") return `user answered: ${verdict.text}`;\n return \"user cancelled the choice\";\n },\n });\n return registry;\n}\n","/** Plan-mode errors carry `toToolResult` so dispatch serializes structured payloads the TUI parses to mount pickers. */\n\nimport type { PlanStep } from \"./plan-types.js\";\n\nexport class PlanProposedError extends Error {\n readonly plan: string;\n readonly steps?: PlanStep[];\n readonly summary?: string;\n constructor(plan: string, steps?: PlanStep[], summary?: string) {\n super(\n \"PlanProposedError: plan submitted. STOP calling tools now — the TUI has shown the plan to the user. Wait for their next message; it will either approve (you'll then implement the plan), request a refinement (you should explore more and submit an updated plan), or cancel (drop the plan and ask what they want instead). Don't call any tools in the meantime.\",\n );\n this.name = \"PlanProposedError\";\n this.plan = plan;\n this.steps = steps;\n this.summary = summary;\n }\n\n toToolResult(): { error: string; plan: string; steps?: PlanStep[]; summary?: string } {\n const payload: { error: string; plan: string; steps?: PlanStep[]; summary?: string } = {\n error: `${this.name}: ${this.message}`,\n plan: this.plan,\n };\n if (this.steps && this.steps.length > 0) payload.steps = this.steps;\n if (this.summary) payload.summary = this.summary;\n return payload;\n }\n}\n\n/** Surgical replace of in-flight plan tail; submit_plan would reset done steps. */\nexport class PlanRevisionProposedError extends Error {\n readonly reason: string;\n readonly remainingSteps: PlanStep[];\n readonly summary?: string;\n constructor(reason: string, remainingSteps: PlanStep[], summary?: string) {\n super(\n \"PlanRevisionProposedError: revision submitted. STOP calling tools now — the TUI has paused for the user to review your proposed change. Wait for their next message; it will say 'revision accepted' (proceed with the new step list), 'revision rejected' (keep the original plan and continue), or 'revision cancelled' (drop the proposal entirely). Don't call any tools in the meantime.\",\n );\n this.name = \"PlanRevisionProposedError\";\n this.reason = reason;\n this.remainingSteps = remainingSteps;\n this.summary = summary;\n }\n\n toToolResult(): {\n error: string;\n reason: string;\n remainingSteps: PlanStep[];\n summary?: string;\n } {\n const payload: {\n error: string;\n reason: string;\n remainingSteps: PlanStep[];\n summary?: string;\n } = {\n error: `${this.name}: ${this.message}`,\n reason: this.reason,\n remainingSteps: this.remainingSteps,\n };\n if (this.summary) payload.summary = this.summary;\n return payload;\n }\n}\n","import { pauseGate } from \"../core/pause-gate.js\";\nimport type { ToolRegistry } from \"../tools.js\";\nimport { PlanProposedError, PlanRevisionProposedError } from \"./plan-errors.js\";\nimport type { PlanStep, PlanStepRisk, StepCompletion } from \"./plan-types.js\";\n\n// Tool descriptions (teaching prompts for the model). Edit here, not inline.\n\nconst SUBMIT_PLAN_DESCRIPTION =\n \"Submit ONE concrete plan you've already decided on. Use this for tasks that warrant a review gate — multi-file refactors, architecture changes, anything that would be expensive or confusing to undo. Skip it for small fixes (one-line typo, obvious bug with a clear fix) — just make the change. The user will either approve (you then implement it), ask for refinement, or cancel. If the user has already enabled /plan mode, writes are blocked at dispatch and you MUST use this. CRITICAL: do NOT use submit_plan to present alternative routes (A/B/C, option 1/2/3) for the user to pick from — the picker only exposes approve/refine/cancel, so a menu plan strands the user with no way to choose. For branching decisions, call `ask_choice` instead; only call submit_plan once the user has picked a direction and you have a single actionable plan. Write the plan as markdown with a one-line summary, a bulleted list of files to touch and what will change, and any risks or open questions. STRONGLY PREFERRED: pass `steps` — an array of {id, title, action, risk?} — so the UI renders a structured step list above the approval picker and tracks per-step progress. Use risk='high' for steps that touch prod data / break public APIs / are hard to undo; 'med' for non-trivial but reversible (multi-file edits, schema tweaks); 'low' for safe local work. After each step, call `mark_step_complete` so the user sees progress ticks.\";\n\nconst MARK_STEP_COMPLETE_DESCRIPTION =\n \"Mark one step of the approved plan as done. MANDATORY: call this exactly once after finishing each step, before starting the next one — skipping it leaves the user staring at `0/N done` on the resume banner even when the work is finished, and they have no way to know which steps actually ran. The TUI updates the plan card's progress in place; the count is persisted to disk so it survives session resume. After the FINAL step, write a brief reply summarizing what was done and end the turn. Pass the `stepId` from the plan's steps array, a short `result` (what you did), and optional `notes` for anything surprising (errors, scope changes, follow-ups). This tool doesn't change any files. Don't call it if the plan didn't include structured steps, and don't invent ids that weren't in the original plan. If you only realized at the end that you skipped marking steps, mark them then — late is still better than never.\";\n\nconst REVISE_PLAN_DESCRIPTION =\n \"Surgically replace the REMAINING steps of an in-flight plan. Call this when the user has given feedback at a checkpoint that warrants a structured plan change — skip a step, swap two steps, add a new step, change risk, etc. Pass: `reason` (one sentence why), `remainingSteps` (the new tail of the plan, replacing whatever steps haven't been done yet), and optional `summary` (updated one-line plan summary). Done steps are NEVER touched — keep them out of `remainingSteps`. The TUI shows a diff (removed in red, kept in gray, added in green) and the user accepts or rejects. Don't call this for trivial mid-step adjustments — just keep executing. Don't call submit_plan for revisions either — that resets the whole plan including completed steps. Use submit_plan only when the entire approach has changed; use revise_plan when the tail needs editing.\";\n\n// Reused by both submit_plan and revise_plan — the step list shape is\n// identical, only the outer wrapper differs. Deliberately NOT `as const`:\n// ToolRegistry's JSONSchema type expects mutable arrays.\nconst STEP_ITEM_SCHEMA = {\n type: \"object\",\n properties: {\n id: { type: \"string\", description: \"Stable id, e.g. step-1.\" },\n title: { type: \"string\", description: \"Short imperative title.\" },\n action: { type: \"string\", description: \"One-sentence description of the concrete action.\" },\n risk: {\n type: \"string\",\n enum: [\"low\", \"med\", \"high\"],\n description:\n \"Self-assessed risk. 'high' = hard-to-undo / touches prod / breaks API; 'med' = non-trivial but reversible; 'low' = safe local work. The UI shows a colored dot per step so the user knows where to focus review. Omit if you're unsure.\",\n },\n },\n required: [\"id\", \"title\", \"action\"],\n};\n\n// Registration options\n\nexport interface PlanToolOptions {\n onPlanSubmitted?: (plan: string, steps?: PlanStep[]) => void;\n onStepCompleted?: (update: StepCompletion) => void;\n onPlanRevisionProposed?: (reason: string, remainingSteps: PlanStep[], summary?: string) => void;\n}\n\n// Arg sanitizers — defensive cleanup shared between submit_plan and revise_plan\n\nfunction sanitizeRisk(raw: unknown): PlanStepRisk | undefined {\n if (raw === \"low\" || raw === \"med\" || raw === \"high\") return raw;\n return undefined;\n}\n\nfunction sanitizeSteps(raw: unknown): PlanStep[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const steps: PlanStep[] = [];\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const e = entry as Record<string, unknown>;\n const id = typeof e.id === \"string\" ? e.id.trim() : \"\";\n const title = typeof e.title === \"string\" ? e.title.trim() : \"\";\n const action = typeof e.action === \"string\" ? e.action.trim() : \"\";\n if (!id || !title || !action) continue;\n const step: PlanStep = { id, title, action };\n const risk = sanitizeRisk(e.risk);\n if (risk) step.risk = risk;\n steps.push(step);\n }\n return steps.length > 0 ? steps : undefined;\n}\n\n// Individual tool registrations — one per screen\n\nfunction registerSubmitPlan(registry: ToolRegistry, opts: PlanToolOptions): void {\n registry.register({\n name: \"submit_plan\",\n description: SUBMIT_PLAN_DESCRIPTION,\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n plan: {\n type: \"string\",\n description:\n \"Markdown-formatted plan. Lead with a one-sentence summary. Then a file-by-file breakdown of what you'll change and why. Flag any risks or open questions at the end so the user can weigh in before you start.\",\n },\n steps: {\n type: \"array\",\n description:\n \"Structured step list (strongly recommended). When provided, the UI renders a compact step list above the approval picker AND tracks per-step progress via `mark_step_complete`. Use stable ids (step-1, step-2, ...). Skip only for tiny one-step plans where the markdown body is enough.\",\n items: STEP_ITEM_SCHEMA,\n },\n summary: {\n type: \"string\",\n description:\n \"Optional. One-sentence human-friendly title for the plan, ~80 chars max. Surfaces in the PlanConfirm picker header and in /plans listings ('▸ refactor auth into signed tokens · 2/5 done'). Skip for trivial plans where the first line of the markdown body is already short and clear.\",\n },\n },\n required: [\"plan\"],\n },\n fn: async (args: { plan: string; steps?: unknown; summary?: string }, ctx) => {\n const plan = (args?.plan ?? \"\").trim();\n if (!plan) {\n throw new Error(\"submit_plan: empty plan — write a markdown plan and try again.\");\n }\n const steps = sanitizeSteps(args?.steps);\n const summary =\n typeof args?.summary === \"string\" ? args.summary.trim() || undefined : undefined;\n opts.onPlanSubmitted?.(plan, steps);\n // Block until the user approves, refines, or cancels\n const verdict = await (ctx?.confirmationGate ?? pauseGate).ask({\n kind: \"plan_proposed\",\n payload: { plan, steps, summary },\n });\n const fb = verdict.feedback?.trim();\n if (verdict.type === \"approve\") {\n return fb ? `plan approved. user's additional instructions: ${fb}` : \"plan approved\";\n }\n if (verdict.type === \"refine\") {\n throw new Error(fb ? `user requested refinement: ${fb}` : \"user requested refinement\");\n }\n throw new Error(fb ? `plan cancelled: ${fb}` : \"plan cancelled\");\n },\n });\n}\n\nfunction registerMarkStepComplete(registry: ToolRegistry, opts: PlanToolOptions): void {\n registry.register({\n name: \"mark_step_complete\",\n description: MARK_STEP_COMPLETE_DESCRIPTION,\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n stepId: {\n type: \"string\",\n description:\n \"The id of the step being marked complete. Must match one from submit_plan's steps array.\",\n },\n title: {\n type: \"string\",\n description:\n \"Optional. The step's title, echoed back for the UI. If omitted, the UI falls back to the id.\",\n },\n result: {\n type: \"string\",\n description: \"One-sentence summary of what was done for this step.\",\n },\n notes: {\n type: \"string\",\n description:\n \"Optional. Anything surprising — blockers hit, assumptions revised, follow-ups for later steps.\",\n },\n },\n required: [\"stepId\", \"result\"],\n },\n fn: async (args: { stepId: string; title?: string; result: string; notes?: string }, ctx) => {\n const stepId = (args?.stepId ?? \"\").trim();\n const result = (args?.result ?? \"\").trim();\n if (!stepId) {\n throw new Error(\"mark_step_complete: stepId is required.\");\n }\n if (!result) {\n throw new Error(\n \"mark_step_complete: result is required — say in one sentence what you did.\",\n );\n }\n const title = typeof args?.title === \"string\" ? args.title.trim() || undefined : undefined;\n const notes = typeof args?.notes === \"string\" ? args.notes.trim() || undefined : undefined;\n const update: StepCompletion = { kind: \"step_completed\", stepId, result };\n if (title) update.title = title;\n if (notes) update.notes = notes;\n opts.onStepCompleted?.(update);\n // Block until the user continues, revises, or stops\n const verdict = await (ctx?.confirmationGate ?? pauseGate).ask({\n kind: \"plan_checkpoint\",\n payload: { stepId, title, result, notes },\n });\n if (verdict.type === \"continue\") return JSON.stringify(update);\n if (verdict.type === \"revise\") {\n if (verdict.feedback) return `revision requested: ${verdict.feedback}`;\n throw new Error(\"user requested revision at checkpoint\");\n }\n throw new Error(\"user stopped at checkpoint\");\n },\n });\n}\n\nfunction registerRevisePlan(registry: ToolRegistry, opts: PlanToolOptions): void {\n registry.register({\n name: \"revise_plan\",\n description: REVISE_PLAN_DESCRIPTION,\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n reason: {\n type: \"string\",\n description:\n \"One sentence explaining why you're revising — what the user asked for, what changed your assessment.\",\n },\n remainingSteps: {\n type: \"array\",\n description:\n \"The new tail of the plan — what should run from here on. Each entry: {id, title, action, risk?}. Use stable ids; reuse old ids when a step is just being adjusted, generate new ones for genuinely new steps.\",\n items: STEP_ITEM_SCHEMA,\n },\n summary: {\n type: \"string\",\n description:\n \"Optional. Updated one-line plan summary if the overall framing has shifted.\",\n },\n },\n required: [\"reason\", \"remainingSteps\"],\n },\n fn: async (args: { reason: string; remainingSteps: unknown; summary?: string }, ctx) => {\n const reason = (args?.reason ?? \"\").trim();\n if (!reason) {\n throw new Error(\n \"revise_plan: reason is required — write one sentence explaining the change.\",\n );\n }\n const remainingSteps = sanitizeSteps(args?.remainingSteps);\n if (!remainingSteps || remainingSteps.length === 0) {\n throw new Error(\n \"revise_plan: remainingSteps must be a non-empty array of well-formed steps. If the user wants to STOP rather than continue, don't revise — the picker has its own Stop option.\",\n );\n }\n const summary =\n typeof args?.summary === \"string\" ? args.summary.trim() || undefined : undefined;\n opts.onPlanRevisionProposed?.(reason, remainingSteps, summary);\n // Block until the user accepts, rejects, or cancels the revision\n const verdict = await (ctx?.confirmationGate ?? pauseGate).ask({\n kind: \"plan_revision\",\n payload: { reason, remainingSteps, summary },\n });\n if (verdict.type === \"accepted\") return \"revision accepted\";\n if (verdict.type === \"rejected\") throw new Error(\"revision rejected\");\n throw new Error(\"revision cancelled\");\n },\n });\n}\n\n// Public entry point\n\nexport function registerPlanTool(registry: ToolRegistry, opts: PlanToolOptions = {}): ToolRegistry {\n registerSubmitPlan(registry, opts);\n registerMarkStepComplete(registry, opts);\n registerRevisePlan(registry, opts);\n return registry;\n}\n","import type { ToolRegistry } from \"../tools.js\";\n\nexport type TodoStatus = \"pending\" | \"in_progress\" | \"completed\";\n\nexport interface TodoItem {\n content: string;\n status: TodoStatus;\n activeForm: string;\n}\n\nexport interface TodoToolOptions {\n onTodosUpdated?: (todos: TodoItem[]) => void;\n}\n\nconst DESCRIPTION =\n 'In-session task tracker for multi-step work. NOT a plan — no approval gate, no checkpoint pauses, doesn\\'t touch any files. The tool replaces the entire todo list every call (set semantics, NOT append). Pass the FULL list every time.\\n\\nWhen to use:\\n• The task has 3+ distinct steps and you want to keep them straight as you work.\\n• The user gave you a multi-part request (\"do A, then B, then C\").\\n• You\\'re partway through a long task and want to record where you are so a future you doesn\\'t lose the thread.\\n\\nWhen NOT to use:\\n• One-shot edits, single-question answers, single-tool tasks.\\n• User-facing approval gates → that\\'s `submit_plan`.\\n• Branching choices → that\\'s `ask_choice`.\\n\\nRules:\\n• Exactly ONE todo may have status:\"in_progress\" at a time (or zero — between steps).\\n• Mark a todo \"completed\" the moment it\\'s actually done — don\\'t batch.\\n• Each todo: `content` (imperative, e.g. \"Add tests\"), `activeForm` (gerund shown while running, e.g. \"Adding tests\"), `status`.\\n• Empty `todos:[]` is allowed — it clears the list when work is fully done.';\n\nfunction validateTodos(raw: unknown): TodoItem[] {\n if (!Array.isArray(raw)) {\n throw new Error(\"todo_write: `todos` must be an array\");\n }\n const out: TodoItem[] = [];\n let inProgressCount = 0;\n for (let i = 0; i < raw.length; i++) {\n const entry = raw[i];\n if (!entry || typeof entry !== \"object\") {\n throw new Error(`todo_write: todo #${i + 1} must be an object`);\n }\n const e = entry as Record<string, unknown>;\n const content = typeof e.content === \"string\" ? e.content.trim() : \"\";\n const activeForm = typeof e.activeForm === \"string\" ? e.activeForm.trim() : \"\";\n const status = e.status;\n if (!content) {\n throw new Error(`todo_write: todo #${i + 1} \\`content\\` must be a non-empty string`);\n }\n if (!activeForm) {\n throw new Error(`todo_write: todo #${i + 1} \\`activeForm\\` must be a non-empty string`);\n }\n if (status !== \"pending\" && status !== \"in_progress\" && status !== \"completed\") {\n throw new Error(\n `todo_write: todo #${i + 1} \\`status\\` must be one of pending|in_progress|completed (got ${JSON.stringify(status)})`,\n );\n }\n if (status === \"in_progress\") {\n inProgressCount++;\n if (inProgressCount > 1) {\n throw new Error(\n \"todo_write: at most one todo may be in_progress at a time — mark the previous one completed first\",\n );\n }\n }\n out.push({ content, status, activeForm });\n }\n return out;\n}\n\nfunction renderTodos(todos: TodoItem[]): string {\n if (todos.length === 0) return \"todos cleared (0 items)\";\n let done = 0;\n let inProgress = 0;\n let pending = 0;\n for (const t of todos) {\n if (t.status === \"completed\") done++;\n else if (t.status === \"in_progress\") inProgress++;\n else pending++;\n }\n const header = `todos updated · ${done} done · ${inProgress} in progress · ${pending} pending`;\n const lines = todos.map((t) => {\n if (t.status === \"completed\") return `[x] ${t.content}`;\n if (t.status === \"in_progress\") return `[>] ${t.activeForm}`;\n return `[ ] ${t.content}`;\n });\n return `${header}\\n${lines.join(\"\\n\")}`;\n}\n\nexport function registerTodoTool(registry: ToolRegistry, opts: TodoToolOptions = {}): ToolRegistry {\n registry.register({\n name: \"todo_write\",\n description: DESCRIPTION,\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n todos: {\n type: \"array\",\n description:\n \"The COMPLETE new todo list. Replaces whatever was there before. Pass [] to clear.\",\n items: {\n type: \"object\",\n properties: {\n content: {\n type: \"string\",\n description: 'Imperative step description, e.g. \"Add tests for parser\".',\n },\n status: {\n type: \"string\",\n enum: [\"pending\", \"in_progress\", \"completed\"],\n description: \"Current state. Exactly one item may be in_progress.\",\n },\n activeForm: {\n type: \"string\",\n description: 'Gerund form shown while in_progress, e.g. \"Adding tests for parser\".',\n },\n },\n required: [\"content\", \"status\", \"activeForm\"],\n },\n },\n },\n required: [\"todos\"],\n },\n fn: async (args: { todos: unknown }) => {\n const todos = validateTodos(args?.todos);\n opts.onTodosUpdated?.(todos);\n return renderTodos(todos);\n },\n });\n return registry;\n}\n","/** Built-in subagent personas — system prompt + iter budget pairs picked via the `type` arg. Skills override at the run_skill level; this is the inline shortcut for parents that don't want to author one. */\n\nimport { NEGATIVE_CLAIM_RULE, TUI_FORMATTING_RULES } from \"../prompt-fragments.js\";\n\nexport type SubagentTypeName = \"explore\" | \"verify\";\n\nexport interface SubagentTypeSpec {\n system: string;\n maxToolIters: number;\n}\n\nconst EXPLORE_SYSTEM = `You are an exploration subagent. Wide-net read-only investigation; return one distilled answer.\n\nHow to operate:\n- Read-only tools only (read_file, search_files, search_content, directory_tree, list_directory, get_file_info).\n- For \"find all places that call / reference / use X\" — use search_content (content grep), NOT search_files (which only matches names).\n- Cast a wide net first to map the territory, then read the 3-10 most relevant files in full. Stop as soon as you can answer.\n- The parent does not see your tool calls — over-exploration is pure waste.\n\nFinal answer:\n- One paragraph or short bullets; lead with the conclusion.\n- Cite file:line ranges when they back the claim.\n- No follow-up offers, no \"let me know if you need more\" — the parent will ask again.\n\n${NEGATIVE_CLAIM_RULE}\n\n${TUI_FORMATTING_RULES}`;\n\nconst VERIFY_SYSTEM = `You are a verify subagent. Narrow check — return YES / NO / INCONCLUSIVE with evidence. Do not expand scope.\n\nHow to operate:\n- Read only what's needed to verify the specific claim. No exploration past the claim.\n- Use search_content / read_file to confirm the exact behavior, type, or call site in question.\n- Cap at 6-8 tool calls. If you can't verify in that, return INCONCLUSIVE plus what's missing.\n\nFinal answer:\n- Lead with VERIFIED / NOT VERIFIED / INCONCLUSIVE.\n- Cite file:line for the evidence.\n- One paragraph or a few bullets. No follow-up offers.\n\n${NEGATIVE_CLAIM_RULE}\n\n${TUI_FORMATTING_RULES}`;\n\nconst TYPES: Record<SubagentTypeName, SubagentTypeSpec> = {\n explore: { system: EXPLORE_SYSTEM, maxToolIters: 20 },\n verify: { system: VERIFY_SYSTEM, maxToolIters: 8 },\n};\n\nexport const SUBAGENT_TYPE_NAMES: readonly SubagentTypeName[] = Object.freeze(\n Object.keys(TYPES) as SubagentTypeName[],\n);\n\nexport function getSubagentType(name: unknown): SubagentTypeSpec | undefined {\n if (typeof name !== \"string\") return undefined;\n return TYPES[name as SubagentTypeName];\n}\n","/** Isolated child loop. Inherits parent registry minus spawn_subagent + submit_plan; no hooks; non-streaming. */\n\nimport { type DeepSeekClient, Usage } from \"../client.js\";\nimport { CacheFirstLoop } from \"../loop.js\";\nimport { applyProjectMemory } from \"../memory/project.js\";\nimport { ImmutablePrefix } from \"../memory/runtime.js\";\nimport {\n NEGATIVE_CLAIM_RULE,\n TUI_FORMATTING_RULES,\n escalationContract,\n} from \"../prompt-fragments.js\";\nimport { ToolRegistry } from \"../tools.js\";\nimport { SUBAGENT_TYPE_NAMES, getSubagentType } from \"./subagent-types.js\";\n\n/** Side-channel — subagents run inside a tool-dispatch frame, can't go through parent's `LoopEvent` stream. */\nexport interface SubagentEvent {\n kind: \"start\" | \"progress\" | \"end\" | \"inner\" | \"phase\";\n /** Stable per-spawn id; lets the UI key parallel runs apart instead of overwriting one shared row. */\n runId: string;\n task: string;\n skillName?: string;\n model?: string;\n iter?: number;\n elapsedMs?: number;\n summary?: string;\n error?: string;\n turns?: number;\n costUsd?: number;\n usage?: Usage;\n /** When kind === \"inner\": the raw child loop event. Parent UI translates to a child summary. */\n inner?: import(\"../loop.js\").LoopEvent;\n /** When kind === \"phase\": coarse status verb for the activity row. */\n phase?: \"exploring\" | \"summarising\";\n}\n\nlet runIdCounter = 0;\nfunction nextRunId(): string {\n runIdCounter++;\n return `sub-${runIdCounter.toString(36)}`;\n}\n\nexport interface SubagentSink {\n current: ((ev: SubagentEvent) => void) | null;\n}\n\nexport interface SpawnSubagentOptions {\n client: DeepSeekClient;\n parentRegistry: ToolRegistry;\n system: string;\n task: string;\n model?: string;\n maxToolIters?: number;\n maxResultChars?: number;\n sink?: SubagentSink;\n /** Forwarded into the child loop so parent Esc cancels nested work. */\n parentSignal?: AbortSignal;\n skillName?: string;\n /** Scopes the child registry to these literal tool names; NEVER_INHERITED still wins. Driven by skill `allowed-tools` frontmatter. */\n allowedTools?: readonly string[];\n}\n\nexport interface SubagentResult {\n success: boolean;\n output: string;\n error?: string;\n turns: number;\n toolIters: number;\n elapsedMs: number;\n costUsd: number;\n model: string;\n skillName?: string;\n /** Zero-filled when no API calls landed so consumers always see a valid shape. */\n usage: Usage;\n}\n\nexport interface SubagentToolOptions {\n client: DeepSeekClient;\n defaultSystem?: string;\n projectRoot?: string;\n defaultModel?: string;\n maxToolIters?: number;\n maxResultChars?: number;\n sink?: SubagentSink;\n}\n\n/** Memory-stable prefix — shared across spawns, cached. The model-dependent escalation contract is appended per spawn so a pro spawn doesn't get told it's running on flash (#582). */\nconst SUBAGENT_BASE_SYSTEM = `You are a Luckerr subagent. The parent agent spawned you to handle one focused subtask, then return.\n\nRules:\n- Stay on the task you were given. Do not expand scope.\n- Use tools as needed. You share the parent's sandbox + safety rules.\n- When you're done, your final assistant message is the only thing the parent will see — make it complete and self-contained. No follow-up offers, no questions, no \"let me know if you need more.\"\n- Prefer one clear, distilled answer over a long log of what you tried.\n\n${NEGATIVE_CLAIM_RULE}\n\n${TUI_FORMATTING_RULES}`;\n\nfunction defaultSubagentSystem(modelId: string): string {\n return `${SUBAGENT_BASE_SYSTEM}\\n\\n${escalationContract(modelId)}`;\n}\n\nconst DEFAULT_MAX_RESULT_CHARS = 8000;\nconst DEFAULT_MAX_ITERS = 16;\nconst MIN_MAX_ITERS = 1;\nconst MAX_MAX_ITERS = 32;\n/** Iters-from-cap at which we start appending a remaining-budget hint to tool results. */\nconst BUDGET_WARN_THRESHOLD = 3;\n\nfunction budgetParagraph(maxToolIters: number): string {\n return `Tool budget: you have ${maxToolIters} tool call${maxToolIters === 1 ? \"\" : \"s\"} for this task. The cap is enforced from outside — the call after #${maxToolIters} is refused. Pace yourself: if you can't fully resolve the task within the budget, stop early and return what you have plus what's missing, rather than burning the budget on one branch.`;\n}\n// Subagents default to flash — their work is read-and-synthesize\n// (explore, research), which doesn't need the 12× pro tier. Skill\n// frontmatter `model: deepseek-v4-pro` is the opt-in override for\n// skills that empirically benefit from the stronger model.\nconst DEFAULT_SUBAGENT_MODEL = \"deepseek-v4-flash\";\n// Subagents default to effort=high — less thinking budget than a\n// main turn (which defaults to `max` in the preset). The parent's\n// task arg is already a distilled prompt; explore/research rarely\n// need deep chains of thought, and `high` saves output tokens.\nconst DEFAULT_SUBAGENT_EFFORT: \"high\" | \"max\" = \"high\";\n\nconst SUBAGENT_TOOL_NAME = \"spawn_subagent\";\n/** spawn_subagent excluded → depth=1 hard cap; submit_plan excluded → no picker mid-parent-turn. */\nconst NEVER_INHERITED_TOOLS = new Set<string>([SUBAGENT_TOOL_NAME, \"submit_plan\"]);\n\n/** Per-session spawn count past which the soft hint fires on every subsequent return. */\nconst SOFT_HINT_AFTER_SPAWNS = 1;\n/** Per-session count past which the strong hint fires (asks the model to justify the next spawn). */\nconst STRONG_HINT_AFTER_SPAWNS = 4;\n/** Per-session cumulative subagent token total past which the strong hint also fires. */\nconst STRONG_HINT_TOKEN_THRESHOLD = 50_000;\n\n/** null → first spawn of the session, no hint. Pure for testability. */\nexport function subagentBudgetHint(spawnCount: number, totalTokens: number): string | null {\n if (spawnCount > STRONG_HINT_AFTER_SPAWNS || totalTokens >= STRONG_HINT_TOKEN_THRESHOLD) {\n return `[budget: this session has now spawned ${spawnCount} subagents totalling ${totalTokens} tokens. Each spawn pays a fresh prefix-cache miss plus a full child loop — confirm the next spawn is genuinely needed (parallel fan-out or >10-read context blow-up) before calling spawn_subagent again. If you can answer with direct tools, do that instead.]`;\n }\n if (spawnCount > SOFT_HINT_AFTER_SPAWNS) {\n return `[note: this session has spawned ${spawnCount} subagents totalling ${totalTokens} tokens; confirm this one is worth it.]`;\n }\n return null;\n}\n\n/** Errors captured in the result shape, never thrown — caller decides how to surface. */\nexport async function spawnSubagent(opts: SpawnSubagentOptions): Promise<SubagentResult> {\n const model = opts.model ?? DEFAULT_SUBAGENT_MODEL;\n const maxToolIters = opts.maxToolIters ?? DEFAULT_MAX_ITERS;\n const maxResultChars = opts.maxResultChars ?? DEFAULT_MAX_RESULT_CHARS;\n const sink = opts.sink;\n const skillName = opts.skillName;\n\n const startedAt = Date.now();\n const runId = nextRunId();\n const taskPreview = opts.task.length > 30 ? `${opts.task.slice(0, 30)}…` : opts.task;\n sink?.current?.({\n kind: \"start\",\n runId,\n task: taskPreview,\n skillName,\n model,\n iter: 0,\n elapsedMs: 0,\n });\n\n if (opts.allowedTools) {\n const missing = opts.allowedTools.filter((n) => !opts.parentRegistry.has(n));\n if (missing.length > 0) {\n const errorMessage = `subagent allow-list names tool(s) not registered in the parent: ${missing.join(\", \")}. Fix the skill's \\`allowed-tools\\` frontmatter or check spelling.`;\n sink?.current?.({\n kind: \"end\",\n runId,\n task: taskPreview,\n skillName,\n model,\n iter: 0,\n elapsedMs: Date.now() - startedAt,\n error: errorMessage,\n turns: 0,\n costUsd: 0,\n usage: new Usage(),\n });\n return {\n success: false,\n output: \"\",\n error: errorMessage,\n turns: 0,\n toolIters: 0,\n elapsedMs: Date.now() - startedAt,\n costUsd: 0,\n model,\n skillName,\n usage: new Usage(),\n };\n }\n }\n\n const childTools = opts.allowedTools\n ? forkRegistryWithAllowList(\n opts.parentRegistry,\n new Set(opts.allowedTools),\n NEVER_INHERITED_TOOLS,\n )\n : forkRegistryExcluding(opts.parentRegistry, NEVER_INHERITED_TOOLS);\n // Budget telemetry: count dispatches and append a remaining-iters hint\n // when the child is within BUDGET_WARN_THRESHOLD of the cap, so the\n // model can choose to wrap up rather than open another rabbit hole.\n let dispatchCount = 0;\n childTools.setResultAugmenter((_name, _args, result) => {\n dispatchCount++;\n const remaining = maxToolIters - dispatchCount;\n if (remaining <= 0) {\n return `${result}\\n\\n[budget: 0 of ${maxToolIters} tool calls left — finalize NOW; the next tool call will be refused]`;\n }\n if (remaining <= BUDGET_WARN_THRESHOLD) {\n return `${result}\\n\\n[budget: ${remaining} of ${maxToolIters} tool call${remaining === 1 ? \"\" : \"s\"} left — wrap up soon]`;\n }\n return result;\n });\n const childPrefix = new ImmutablePrefix({\n system: `${opts.system}\\n\\n${budgetParagraph(maxToolIters)}`,\n toolSpecs: childTools.specs(),\n });\n const childLoop = new CacheFirstLoop({\n client: opts.client,\n prefix: childPrefix,\n tools: childTools,\n model,\n // Subagents run on a constrained thinking budget by default — the\n // task is already narrow by construction, and `high` cuts output\n // tokens substantially vs `max`.\n reasoningEffort: DEFAULT_SUBAGENT_EFFORT,\n maxToolIters,\n hooks: [],\n // Streaming on so the parent UI can flip the \"summarising\" phase the\n // moment the model starts emitting the final answer (first assistant_delta\n // after the last tool result, before assistant_final lands).\n stream: true,\n });\n\n // Wire parent-abort → child-abort. Two pitfalls we have to handle:\n //\n // 1. `addEventListener(\"abort\", ...)` does NOT fire for a signal\n // that's already aborted (the abort event has already been\n // dispatched once and `once: true` is moot). If the parent\n // aborted between dispatch entry and our listener attach,\n // the listener stays silent forever and the child runs free.\n // → Check `.aborted` synchronously and forward immediately.\n //\n // 2. childLoop.step() reassigns its internal _turnAbort at the\n // top of step(). loop.ts forwards prior aborted state into\n // the fresh controller, so abort() called BEFORE step() runs\n // still kills the new step at iter 0.\n const onParentAbort = () => childLoop.abort();\n if (opts.parentSignal?.aborted) {\n childLoop.abort();\n } else {\n opts.parentSignal?.addEventListener(\"abort\", onParentAbort, { once: true });\n }\n\n let final = \"\";\n let errorMessage: string | undefined;\n let toolIter = 0;\n let summarisingEmitted = false;\n try {\n for await (const ev of childLoop.step(opts.task)) {\n sink?.current?.({ kind: \"inner\", runId, task: taskPreview, skillName, model, inner: ev });\n\n if (ev.role === \"tool\") {\n toolIter++;\n // New tool dispatched — the model went back to deciding, summarising flag resets so the next final-answer delta re-emits.\n summarisingEmitted = false;\n sink?.current?.({\n kind: \"progress\",\n runId,\n task: taskPreview,\n skillName,\n model,\n iter: toolIter,\n elapsedMs: Date.now() - startedAt,\n });\n }\n // First content delta (no concurrent tool_call_delta role) = the\n // model is now writing its final answer, not deciding the next tool.\n if (ev.role === \"assistant_delta\" && !summarisingEmitted && (ev.content ?? \"\").length > 0) {\n summarisingEmitted = true;\n sink?.current?.({\n kind: \"phase\",\n runId,\n task: taskPreview,\n skillName,\n model,\n phase: \"summarising\",\n iter: toolIter,\n elapsedMs: Date.now() - startedAt,\n });\n }\n if (ev.role === \"assistant_final\") {\n if (ev.forcedSummary) {\n errorMessage = ev.content?.trim() || \"subagent ended without producing an answer\";\n } else {\n final = ev.content ?? \"\";\n }\n }\n if (ev.role === \"error\") {\n errorMessage = ev.error ?? \"subagent error\";\n }\n }\n } catch (err) {\n errorMessage = (err as Error).message;\n } finally {\n opts.parentSignal?.removeEventListener(\"abort\", onParentAbort);\n }\n // The loop yields `done` without an `error` event when its API call\n // is aborted mid-flight (intentional UX — see the matching catch in\n // CacheFirstLoop.step). From a SUBAGENT consumer's perspective that\n // still counts as a failure: no answer came back, the parent has\n // nothing to render. Synthesize an error so `success: false` and the\n // UI surfaces the abort instead of returning empty output.\n if (!errorMessage && !final) {\n errorMessage = opts.parentSignal?.aborted\n ? \"subagent aborted before producing an answer\"\n : \"subagent ended without producing an answer\";\n }\n\n const elapsedMs = Date.now() - startedAt;\n const turns = childLoop.stats.turns.length;\n const costUsd = childLoop.stats.totalCost;\n const usage = aggregateChildUsage(childLoop);\n\n const truncated =\n final.length > maxResultChars\n ? `${final.slice(0, maxResultChars)}\\n\\n[…truncated ${final.length - maxResultChars} chars; ask the subagent for a tighter summary if you need more.]`\n : final;\n\n sink?.current?.({\n kind: \"end\",\n runId,\n task: taskPreview,\n skillName,\n model,\n iter: toolIter,\n elapsedMs,\n summary: errorMessage ? undefined : truncated.slice(0, 120),\n error: errorMessage,\n turns,\n costUsd,\n usage,\n });\n\n return {\n success: !errorMessage,\n output: errorMessage ? \"\" : truncated,\n error: errorMessage,\n turns,\n toolIters: toolIter,\n elapsedMs,\n costUsd,\n model,\n skillName,\n usage,\n };\n}\n\n/** Zero-filled when no API calls landed so downstream consumers always see a valid shape. */\nfunction aggregateChildUsage(loop: CacheFirstLoop): Usage {\n const agg = new Usage();\n for (const t of loop.stats.turns) {\n agg.promptTokens += t.usage.promptTokens;\n agg.completionTokens += t.usage.completionTokens;\n agg.totalTokens += t.usage.totalTokens;\n agg.promptCacheHitTokens += t.usage.promptCacheHitTokens;\n agg.promptCacheMissTokens += t.usage.promptCacheMissTokens;\n }\n return agg;\n}\n\nexport function formatSubagentResult(r: SubagentResult): string {\n if (!r.success) {\n return JSON.stringify({\n success: false,\n error: r.error ?? \"unknown subagent error\",\n turns: r.turns,\n tool_iters: r.toolIters,\n elapsed_ms: r.elapsedMs,\n });\n }\n return JSON.stringify({\n success: true,\n output: r.output,\n turns: r.turns,\n tool_iters: r.toolIters,\n elapsed_ms: r.elapsedMs,\n cost_usd: r.costUsd,\n });\n}\n\n/** Library surface only — `luckerr code` uses Skills `runAs: subagent` as the user-facing path. */\nexport function registerSubagentTool(\n parentRegistry: ToolRegistry,\n opts: SubagentToolOptions,\n): ToolRegistry {\n const baseSystem = opts.defaultSystem ?? SUBAGENT_BASE_SYSTEM;\n // Bake project memory into the default once — re-reading on every\n // spawn would (a) make the child prefix unstable when LUCKERR.md\n // changes mid-session, defeating cache reuse across multiple\n // subagent calls, and (b) cost a stat() per call. The parent itself\n // also reads memory once at startup; matching that semantics keeps\n // subagent and parent on the same page. The escalation contract is\n // appended per-spawn against the spawn's resolved model id (#582).\n const defaultSystemBase = opts.projectRoot\n ? applyProjectMemory(baseSystem, opts.projectRoot)\n : baseSystem;\n const defaultModel = opts.defaultModel ?? DEFAULT_SUBAGENT_MODEL;\n const maxToolIters = opts.maxToolIters ?? DEFAULT_MAX_ITERS;\n const maxResultChars = opts.maxResultChars ?? DEFAULT_MAX_RESULT_CHARS;\n const sink = opts.sink;\n // Per-session counters survive across spawn calls because registerSubagentTool\n // runs once per parent registry — closure scope is the session scope.\n let sessionSpawnCount = 0;\n let sessionSpawnTokens = 0;\n\n parentRegistry.register({\n name: SUBAGENT_TOOL_NAME,\n parallelSafe: true,\n description:\n \"Spawn an isolated subagent to handle a self-contained subtask in a fresh context, returning only its final answer. **Prefer direct tools.** Spawn primarily for parallel fan-out (2+ independent investigations issued in one tool batch) or when the work would otherwise need >10 file reads/searches whose trail you don't need to keep. Single greps, 1-3 file cross-references, and 'keep my context clean for one question' are NOT good reasons to spawn — direct tools are cheaper and let you reference the evidence later. Each spawn pays a fresh prefix-cache miss plus a full child loop. The subagent inherits your tools but runs in its own isolated message log; only the final assistant message comes back. Keep tasks focused; the subagent has a stricter iter budget than you do.\",\n parameters: {\n type: \"object\",\n properties: {\n task: {\n type: \"string\",\n description:\n \"The subtask the subagent should perform. Be specific and self-contained — the subagent has none of your conversation context, only what you write here.\",\n },\n system: {\n type: \"string\",\n description:\n \"Optional override for the subagent's system prompt. The default tells it to stay focused and return a concise answer; override only when the subtask needs a specialized persona.\",\n },\n model: {\n type: \"string\",\n enum: [\"deepseek-v4-flash\", \"deepseek-v4-pro\"],\n description:\n \"Which DeepSeek model the subagent runs on. Default is 'deepseek-v4-flash' — cheap and fast, fine for explore/research-style subtasks. Override to 'deepseek-v4-pro' (~12× more expensive) when the subtask genuinely needs the stronger model: cross-file architecture, subtle bug hunts, anything where flash has empirically underperformed.\",\n },\n max_iters: {\n type: \"integer\",\n minimum: MIN_MAX_ITERS,\n maximum: MAX_MAX_ITERS,\n description: `Cap on the subagent's tool-call iterations. Default 16 (or the type's default when 'type' is set). Hard range: ${MIN_MAX_ITERS}-${MAX_MAX_ITERS}; out-of-range values are clamped to the nearest end.`,\n },\n type: {\n type: \"string\",\n enum: [...SUBAGENT_TYPE_NAMES],\n description:\n \"Optional persona shaping the system prompt and default iter budget. 'explore' = wide-net read-only investigation (20-iter budget, returns a distilled answer). 'verify' = narrow yes/no check with evidence (8-iter budget). Omit when supplying your own 'system' prompt or when the default generic persona fits. Caller-supplied 'system' / 'max_iters' override the type's defaults.\",\n },\n },\n required: [\"task\"],\n },\n fn: async (\n args: {\n task?: unknown;\n system?: unknown;\n model?: unknown;\n max_iters?: unknown;\n type?: unknown;\n },\n ctx,\n ) => {\n const task = typeof args.task === \"string\" ? args.task.trim() : \"\";\n if (!task) {\n return JSON.stringify({\n error: \"spawn_subagent requires a non-empty 'task' argument.\",\n });\n }\n const typeSpec = getSubagentType(args.type);\n const model =\n typeof args.model === \"string\" && args.model.startsWith(\"deepseek-\")\n ? args.model\n : defaultModel;\n const system =\n typeof args.system === \"string\" && args.system.trim().length > 0\n ? args.system.trim()\n : (typeSpec?.system ?? `${defaultSystemBase}\\n\\n${escalationContract(model)}`);\n const callerIters = clampMaxIters(args.max_iters);\n const result = await spawnSubagent({\n client: opts.client,\n parentRegistry,\n system,\n task,\n model,\n maxToolIters: callerIters ?? typeSpec?.maxToolIters ?? maxToolIters,\n maxResultChars,\n sink,\n parentSignal: ctx?.signal,\n });\n sessionSpawnCount++;\n sessionSpawnTokens += result.usage.totalTokens;\n const formatted = formatSubagentResult(result);\n const hint = subagentBudgetHint(sessionSpawnCount, sessionSpawnTokens);\n return hint ? `${formatted}\\n${hint}` : formatted;\n },\n });\n\n return parentRegistry;\n}\n\n/** Floats round down; non-finite / wrong-type yields undefined so caller falls back to its default. */\nfunction clampMaxIters(raw: unknown): number | undefined {\n if (typeof raw !== \"number\" || !Number.isFinite(raw)) return undefined;\n const n = Math.floor(raw);\n if (n < MIN_MAX_ITERS) return MIN_MAX_ITERS;\n if (n > MAX_MAX_ITERS) return MAX_MAX_ITERS;\n return n;\n}\n\n/** Plan-mode state propagates — a subagent spawned under `/plan` MUST NOT escape it. */\nexport function forkRegistryExcluding(\n parent: ToolRegistry,\n exclude: ReadonlySet<string>,\n): ToolRegistry {\n const child = new ToolRegistry();\n for (const spec of parent.specs()) {\n const name = spec.function.name;\n if (exclude.has(name)) continue;\n const def = parent.get(name);\n if (!def) continue;\n // Re-register copies the public ToolDefinition fields. The child\n // re-runs auto-flatten analysis on its own, which produces an\n // identical flatSchema for the same input — no surprise.\n child.register(def);\n }\n if (parent.planMode) child.setPlanMode(true);\n return child;\n}\n\n/** alsoExclude wins over allow so NEVER_INHERITED still drops `spawn_subagent` even if a skill allow-list names it. */\nexport function forkRegistryWithAllowList(\n parent: ToolRegistry,\n allow: ReadonlySet<string>,\n alsoExclude: ReadonlySet<string>,\n): ToolRegistry {\n const child = new ToolRegistry();\n for (const spec of parent.specs()) {\n const name = spec.function.name;\n if (!allow.has(name)) continue;\n if (alsoExclude.has(name)) continue;\n const def = parent.get(name);\n if (!def) continue;\n child.register(def);\n }\n if (parent.planMode) child.setPlanMode(true);\n return child;\n}\n","/** cwd pinned to root; non-allowlisted commands throw to a UI confirm gate; spawn is `shell: false`, tokenized argv only. */\n\nimport * as pathMod from \"node:path\";\nimport { addProjectShellAllowed } from \"../config.js\";\nimport { pauseGate } from \"../core/pause-gate.js\";\nimport type { ToolRegistry } from \"../tools.js\";\nimport { JobRegistry } from \"./jobs.js\";\nimport {\n DEFAULT_MAX_OUTPUT_CHARS,\n DEFAULT_TIMEOUT_SEC,\n type RunCommandResult,\n runCommand,\n} from \"./shell/exec.js\";\nimport { isCommandAllowed } from \"./shell/parse.js\";\n\nexport {\n BUILTIN_ALLOWLIST,\n detectShellOperator,\n isAllowed,\n isCommandAllowed,\n isDqEscape,\n tokenizeCommand,\n} from \"./shell/parse.js\";\nexport type { ResolveExecutableOptions, RunCommandResult } from \"./shell/exec.js\";\nexport {\n injectPowerShellUtf8,\n killProcessTree,\n prepareSpawn,\n quoteForCmdExe,\n resolveExecutable,\n runCommand,\n smartDecodeOutput,\n withUtf8Codepage,\n} from \"./shell/exec.js\";\n\nexport interface ShellToolsOptions {\n /** Directory to run commands in. Must be an absolute path. */\n rootDir: string;\n /** Seconds before an individual command is killed. Default: 60. */\n timeoutSec?: number;\n maxOutputChars?: number;\n /** Getter form is load-bearing — newly-persisted \"always allow\" prefixes MUST take effect mid-session. */\n extraAllowed?: readonly string[] | (() => readonly string[]);\n /** Getter form lets `editMode === \"yolo\"` flip mid-session without re-registering tools. */\n allowAll?: boolean | (() => boolean);\n jobs?: JobRegistry;\n}\n\n/** Error thrown by `run_command` when the command isn't allowlisted. */\nexport class NeedsConfirmationError extends Error {\n readonly command: string;\n constructor(command: string) {\n super(\n `run_command: \"${command}\" needs the user's approval before it runs. STOP calling tools now — the TUI has already prompted the user to press y (run) or n (deny). Wait for their next message; it will either be the command's output (if they approved) or an instruction to continue without it (if they denied). Don't retry the command or call other shell commands in the meantime.`,\n );\n this.name = \"NeedsConfirmationError\";\n this.command = command;\n }\n}\n\nexport function registerShellTools(registry: ToolRegistry, opts: ShellToolsOptions): ToolRegistry {\n const rootDir = pathMod.resolve(opts.rootDir);\n const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;\n const maxOutputChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;\n const jobs = opts.jobs ?? new JobRegistry();\n // Resolved on every dispatch so newly-persisted \"always allow\"\n // prefixes take effect inside the session that added them, not just\n // on the next launch. Static arrays are wrapped into a constant\n // getter so the call site below is uniform.\n const getExtraAllowed: () => readonly string[] =\n typeof opts.extraAllowed === \"function\"\n ? opts.extraAllowed\n : (() => {\n const snapshot = opts.extraAllowed ?? [];\n return () => snapshot;\n })();\n // Resolve dynamically so the TUI can flip yolo mode mid-session and\n // have the registry pick it up on the next dispatch. Static booleans\n // are wrapped into a thunk for uniformity.\n const isAllowAll: () => boolean =\n typeof opts.allowAll === \"function\" ? opts.allowAll : () => opts.allowAll === true;\n\n registry.register({\n name: \"run_command\",\n description:\n \"Run a shell command in the project root; returns combined stdout+stderr. Allowlisted read-only / test / lint / typecheck commands run immediately; anything that could mutate state, install deps, or touch the network is gated by user confirmation. Prefer this over asking the user to run a command manually — after edits, run the project's tests to verify.\\n\\nConstraints (no real shell — argv is parsed natively for cross-platform parity):\\n• Supported: chain ops `|` / `||` / `&&` / `;` (each segment allowlist-checked individually), file redirects `>` / `>>` / `<` / `2>` / `2>>` / `2>&1` / `&>` (target paths resolve relative to project root, max one redirect per fd per segment).\\n• NOT supported: background `&`, heredoc `<<`, command substitution `$(…)`, subshells `(…)`, process substitution `<(…)`, `$VAR` env expansion, glob expansion. To pass an operator char as literal arg, quote it (`grep \\\"a|b\\\" file`).\\n• `cd` does NOT persist — between calls OR within a chain like `cd dir && cmd`. Use the binary's own cwd flag: `npm --prefix <dir>`, `git -C <dir>`, `cargo -C <dir>`, `pytest <dir>/tests`.\\n• Filter at source — unbounded output (`netstat -ano`, `find /`) wastes tokens. Use `grep -c`, `wc -l`, narrower paths, etc.\",\n // Plan-mode gate: allow allowlisted commands through (git status,\n // cargo check, ls, grep …) so the model can actually investigate\n // during planning. Anything that would otherwise trigger a\n // confirmation prompt is treated as \"not read-only\" and bounced.\n readOnlyCheck: (args: { command?: unknown }) => {\n if (isAllowAll()) return true;\n const cmd = typeof args?.command === \"string\" ? args.command.trim() : \"\";\n if (!cmd) return false;\n return isCommandAllowed(cmd, getExtraAllowed());\n },\n parameters: {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description:\n 'Full command line. POSIX-ish quoting. Chain operators `|`, `||`, `&&`, `;` and file redirects `>` / `>>` / `<` / `2>` / `2>>` / `2>&1` / `&>` work natively (no shell). Background `&`, heredoc `<<`, env-var expansion `$VAR`, and command substitution `$(…)` are rejected (or passed through as literal in the case of `$VAR`). To pass an operator character as a literal argument (e.g. a regex), wrap it in quotes: `grep \"a|b\" file.txt`.',\n },\n timeoutSec: {\n type: \"integer\",\n description: `Override the default ${timeoutSec}s timeout for a single command.`,\n },\n },\n required: [\"command\"],\n },\n fn: async (args: { command: string; timeoutSec?: number }, ctx) => {\n const cmd = args.command.trim();\n if (!cmd) throw new Error(\"run_command: empty command\");\n const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));\n if (!isAllowAll() && !isCommandAllowed(cmd, getExtraAllowed())) {\n const gate = ctx?.confirmationGate ?? pauseGate;\n const choice = await gate.ask({\n kind: \"run_command\",\n payload: { command: cmd, cwd: rootDir, timeoutSec: effectiveTimeout },\n });\n if (choice.type === \"deny\") {\n throw new Error(\n `user denied: ${cmd}${choice.denyContext ? ` — ${choice.denyContext}` : \"\"}`,\n );\n }\n if (choice.type === \"always_allow\") {\n addProjectShellAllowed(rootDir, choice.prefix);\n }\n // \"run_once\" — fall through and execute\n }\n const result = await runCommand(cmd, {\n cwd: rootDir,\n timeoutSec: effectiveTimeout,\n maxOutputChars,\n signal: ctx?.signal,\n });\n return formatCommandResult(cmd, result);\n },\n });\n\n registry.register({\n name: \"run_background\",\n description:\n \"Spawn a long-running process and detach. Waits up to `waitSec` for startup or a readiness signal ('Local:', 'listening on', 'compiled successfully'), then returns the job id + startup preview. Tail logs with `job_output`, block on completion with `wait_for_job`, kill with `stop_job`, list with `list_jobs`.\\n\\nSingle process only — chains / redirects / `cd` work as in run_command, but a typical invocation is one binary. Use the binary's own --cwd / --prefix flag for subdirectories. Vite gotcha: npm's `--prefix` only finds package.json; vite's server root still uses process cwd — pass `vite <project-dir>` instead.\\n\\nUSE THIS — not run_command — for:\\n- Dev servers / watchers: npm/yarn/pnpm dev, uvicorn / flask run, cargo watch, tsc --watch, webpack serve, anything with dev/serve/watch in the name.\\n- One-shot long jobs: curl / wget large downloads, `huggingface-cli download`, multi-GB `pip install` / `npm install`, big `cargo build` / `docker build`. Start with `run_background`, then call `wait_for_job` once (default `waitFor: 'exit'`, timeoutMs up to 300_000) — the harness blocks server-side so a 5-minute download costs ONE tool call, not 30 polls.\",\n parameters: {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description:\n \"Full command line. Same quoting rules as run_command (no pipes / redirects / chaining).\",\n },\n waitSec: {\n type: \"integer\",\n description:\n \"Max seconds to wait for startup before returning. 0..30, default 3. A ready-signal match short-circuits this.\",\n },\n },\n required: [\"command\"],\n },\n fn: async (args: { command: string; waitSec?: number }, ctx) => {\n const cmd = args.command.trim();\n if (!cmd) throw new Error(\"run_background: empty command\");\n if (!isAllowAll() && !isCommandAllowed(cmd, getExtraAllowed())) {\n const gate = ctx?.confirmationGate ?? pauseGate;\n const choice = await gate.ask({\n kind: \"run_background\",\n payload: { command: cmd, cwd: rootDir, waitSec: args.waitSec },\n });\n if (choice.type === \"deny\") {\n throw new Error(\n `user denied: ${cmd}${choice.denyContext ? ` — ${choice.denyContext}` : \"\"}`,\n );\n }\n if (choice.type === \"always_allow\") {\n addProjectShellAllowed(rootDir, choice.prefix);\n }\n // \"run_once\" — fall through and execute\n }\n const result = await jobs.start(cmd, {\n cwd: rootDir,\n waitSec: args.waitSec,\n signal: ctx?.signal,\n });\n return formatJobStart(result);\n },\n });\n\n registry.register({\n name: \"job_output\",\n description:\n \"Read the latest output of a background job started with `run_background`. By default returns the tail of the buffer (last 80 lines). Pass `since` (the `byteLength` from a previous call) to stream only new content incrementally. Tells you whether the job is still running, so you can stop polling when it's done.\",\n readOnly: true,\n parallelSafe: true,\n stormExempt: true,\n parameters: {\n type: \"object\",\n properties: {\n jobId: { type: \"integer\", description: \"Job id returned by run_background.\" },\n since: {\n type: \"integer\",\n description:\n \"Return only output written past this byte offset (for incremental polling).\",\n },\n tailLines: {\n type: \"integer\",\n description: \"Cap the returned slice to the last N lines. Default 80, 0 = unlimited.\",\n },\n },\n required: [\"jobId\"],\n },\n fn: async (args: { jobId: number; since?: number; tailLines?: number }) => {\n const out = jobs.read(args.jobId, {\n since: args.since,\n tailLines: args.tailLines ?? 80,\n });\n if (!out) return `job ${args.jobId}: not found (use list_jobs)`;\n return formatJobRead(args.jobId, out);\n },\n });\n\n registry.register({\n name: \"wait_for_job\",\n description:\n \"Block server-side until a background job finishes (or, opt-in, until it produces new output), bounded by `timeoutMs`. Costs ONE tool call regardless of how long the wait runs — use this instead of polling `job_output` in a loop. Returns JSON with `exited`, `exitCode`, and `latestOutput`.\\n\\n`waitFor` controls the wake condition:\\n- `'exit'` (default) — only wake on the job exiting (or the timeout). Right for downloads, installs, builds, anything one-shot. Chatty progress bars do NOT wake the wait.\\n- `'output-or-exit'` — also wake whenever the job writes a new line. Right for tailing a dev server / watcher and reacting to a specific log line.\\n\\nFor a download or install, set `timeoutMs` to the slowest reasonable end-to-end (e.g. 300_000 for a 5-min wheel install).\",\n readOnly: true,\n parallelSafe: true,\n stormExempt: true,\n parameters: {\n type: \"object\",\n properties: {\n jobId: { type: \"integer\", description: \"Job id returned by run_background.\" },\n timeoutMs: {\n type: \"integer\",\n description:\n \"Max time to block before returning if the wake condition hasn't fired. Clamped to 0..300000. Default 5000.\",\n },\n waitFor: {\n type: \"string\",\n enum: [\"exit\", \"output-or-exit\"],\n description:\n \"Wake condition. 'exit' = only on job exit (right for downloads / installs / builds). 'output-or-exit' = also on any new output (right for tailing a dev server). Default 'exit'.\",\n },\n },\n required: [\"jobId\"],\n },\n fn: async (args: {\n jobId: number;\n timeoutMs?: number;\n waitFor?: \"exit\" | \"output-or-exit\";\n }) => {\n const out = await jobs.waitForJob(args.jobId, {\n timeoutMs: args.timeoutMs,\n waitFor: args.waitFor,\n });\n if (!out) return `job ${args.jobId}: not found (use list_jobs)`;\n return {\n jobId: args.jobId,\n exited: out.exited,\n exitCode: out.exitCode,\n latestOutput: out.latestOutput,\n };\n },\n });\n\n registry.register({\n name: \"stop_job\",\n description:\n \"Stop a background job started with `run_background`. SIGTERM first; SIGKILL after a short grace period if it doesn't exit cleanly. Returns the final output + exit code. Safe to call on an already-exited job.\",\n parameters: {\n type: \"object\",\n properties: {\n jobId: { type: \"integer\" },\n },\n required: [\"jobId\"],\n },\n fn: async (args: { jobId: number }) => {\n const rec = await jobs.stop(args.jobId);\n if (!rec) return `job ${args.jobId}: not found`;\n return formatJobStop(rec);\n },\n });\n\n registry.register({\n name: \"list_jobs\",\n description:\n \"List every background job started this session — running and exited — with id, command, pid, status. Use when you've lost track of which job_id corresponds to which process, or to see what's still alive.\",\n readOnly: true,\n parallelSafe: true,\n stormExempt: true,\n parameters: { type: \"object\", properties: {} },\n fn: async () => {\n const all = jobs.list();\n if (all.length === 0) return \"(no background jobs started this session)\";\n return all.map(formatJobRow).join(\"\\n\");\n },\n });\n\n return registry;\n}\n\nfunction formatJobStart(r: import(\"./jobs.js\").JobStartResult): string {\n const header = r.stillRunning\n ? `[job ${r.jobId} started · pid ${r.pid ?? \"?\"} · ${r.readyMatched ? \"READY signal matched\" : \"running (no ready signal yet)\"}]`\n : r.exitCode !== null\n ? `[job ${r.jobId} exited during startup · exit ${r.exitCode}]`\n : `[job ${r.jobId} failed to start]`;\n return r.preview ? `${header}\\n${r.preview}` : header;\n}\n\nfunction formatJobRead(jobId: number, r: import(\"./jobs.js\").JobReadResult): string {\n const status = r.running\n ? `running · pid ${r.pid ?? \"?\"}`\n : r.exitCode !== null\n ? `exited ${r.exitCode}`\n : r.spawnError\n ? `failed (${r.spawnError})`\n : \"stopped\";\n const header = `[job ${jobId} · ${status} · byteLength=${r.byteLength}]\\n$ ${r.command}`;\n return r.output ? `${header}\\n${r.output}` : header;\n}\n\nfunction formatJobStop(r: import(\"./jobs.js\").JobRecord): string {\n const running = r.running\n ? \"still running (SIGKILL may be pending)\"\n : `exit ${r.exitCode ?? \"?\"}`;\n const tail = tailLines(r.output, 40);\n const header = `[job ${r.id} stopped · ${running}]\\n$ ${r.command}`;\n return tail ? `${header}\\n${tail}` : header;\n}\n\nfunction formatJobRow(r: import(\"./jobs.js\").JobRecord): string {\n const age = ((Date.now() - r.startedAt) / 1000).toFixed(1);\n const state = r.running\n ? `running · pid ${r.pid ?? \"?\"}`\n : r.exitCode !== null\n ? `exit ${r.exitCode}`\n : r.spawnError\n ? \"failed\"\n : \"stopped\";\n return ` ${String(r.id).padStart(3)} ${state.padEnd(24)} ${age}s ago $ ${r.command}`;\n}\n\nfunction tailLines(s: string, n: number): string {\n if (!s) return \"\";\n const lines = s.split(\"\\n\");\n if (lines.length <= n) return s;\n const dropped = lines.length - n;\n return [`[… ${dropped} earlier lines …]`, ...lines.slice(-n)].join(\"\\n\");\n}\n\nexport function formatCommandResult(cmd: string, r: RunCommandResult): string {\n const header = r.timedOut\n ? `$ ${cmd}\\n[killed after timeout]`\n : `$ ${cmd}\\n[exit ${r.exitCode ?? \"?\"}]`;\n return r.output ? `${header}\\n${r.output}` : header;\n}\n","/** Background process registry for never-exiting commands; ready-signal detection short-circuits the startup wait. */\n\nimport { type ChildProcess, type SpawnOptions, spawn } from \"node:child_process\";\nimport * as pathMod from \"node:path\";\nimport { detectShellOperator, prepareSpawn, tokenizeCommand } from \"./shell.js\";\n\n/** Kills the whole tree — `child.kill` only hits the direct child, leaving npm-spawned dev servers orphaned. */\nfunction killProcessTree(pid: number, signal: \"SIGTERM\" | \"SIGKILL\"): void {\n if (process.platform === \"win32\") {\n // taskkill: /T = tree, /F = force (TerminateProcess, no cleanup).\n // Graceful path still uses /F on Windows because there's no signal\n // in the POSIX sense — the closest equivalent is Ctrl+Break, which\n // is unreliable from another console. /F with /T is what most\n // process managers ship on Windows.\n const args = [\"/pid\", String(pid), \"/T\"];\n if (signal === \"SIGKILL\") args.push(\"/F\");\n try {\n const killer = spawn(\"taskkill\", args, {\n stdio: \"ignore\",\n windowsHide: true,\n });\n // Swallow ENOENT / EACCES — we did our best. Not awaiting is\n // intentional: taskkill can take a few hundred ms and the caller\n // already has its own deadline.\n killer.on(\"error\", () => {\n /* ignore */\n });\n } catch {\n /* ignore */\n }\n return;\n }\n // POSIX: negative pid signals the whole process group. Requires the\n // spawn to have been detached (which `start()` does below).\n try {\n process.kill(-pid, signal);\n return;\n } catch {\n /* group-kill failed — fall back to direct */\n }\n try {\n process.kill(pid, signal);\n } catch {\n /* ignore — already dead */\n }\n}\n\n/** Per-job output ring. Capped so a chatty dev server doesn't OOM. */\nconst DEFAULT_OUTPUT_CAP_BYTES = 64 * 1024; // 64 KB\n\n/** First match cuts startup wait short; conservative patterns — a false negative costs a real stall. */\nconst READY_SIGNALS: ReadonlyArray<RegExp> = [\n // HTTP server banners\n /\\blistening on\\b/i,\n /\\blocal:\\s+https?:\\/\\//i,\n /\\bhttps?:\\/\\/(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0)(?::\\d+)?\\b/i,\n /\\b(?:ready|server started|started server|app listening)\\b/i,\n // Bundlers / compilers\n /\\bcompiled successfully\\b/i,\n /\\bbuild complete(?:d)?\\b/i,\n /\\bwatching for (?:file )?changes\\b/i,\n /\\bready in \\d+/i,\n // Generic\n /\\bstartup (?:complete|finished)\\b/i,\n];\n\nexport interface JobStartOptions {\n /** Absolute path to cwd for the spawned child. */\n cwd: string;\n /** Capped at 30; ready-signal match short-circuits. Default 3. */\n waitSec?: number;\n /** Signal plumbed through from the calling tool's AbortSignal. */\n signal?: AbortSignal;\n /** Total per-job output buffer cap (bytes). Default 64 KB. */\n maxBufferBytes?: number;\n}\n\nexport interface JobStartResult {\n jobId: number;\n pid: number | null;\n /** True iff the child was still running at the point we returned. */\n stillRunning: boolean;\n /** True iff a READY_SIGNALS pattern matched during the wait window. */\n readyMatched: boolean;\n /** Preview of combined stdout+stderr accumulated during the wait. */\n preview: string;\n /** If the child exited during the wait, its exit code; else null. */\n exitCode: number | null;\n}\n\nexport interface JobRecord {\n id: number;\n command: string;\n pid: number | null;\n startedAt: number;\n /** Exit code once the process terminates; null while running. */\n exitCode: number | null;\n /** Combined stdout+stderr, ring-trimmed. */\n output: string;\n /** Counts all bytes the child wrote, not just what's still buffered in `output`. */\n totalBytesWritten: number;\n /** True iff the child is still alive. */\n running: boolean;\n /** Error from spawn() itself (ENOENT, etc.) once surfaced. */\n spawnError?: string;\n}\n\nexport class JobRegistry {\n private readonly jobs = new Map<number, InternalJob>();\n private nextId = 1;\n\n /** Resolves on (a) ready signal, (b) early exit, or (c) waitSec deadline — child keeps running regardless. */\n async start(command: string, opts: JobStartOptions): Promise<JobStartResult> {\n const trimmed = command.trim();\n if (!trimmed) throw new Error(\"run_background: empty command\");\n const op = detectShellOperator(trimmed);\n if (op !== null) {\n throw new Error(\n `run_background: shell operator \"${op}\" is not supported — spawn one process per background job. Compose via your orchestration, not the shell.`,\n );\n }\n const argv = tokenizeCommand(trimmed);\n if (argv.length === 0) throw new Error(\"run_background: empty command\");\n const waitMs = Math.max(0, Math.min(30, opts.waitSec ?? 3)) * 1000;\n const maxBytes = opts.maxBufferBytes ?? DEFAULT_OUTPUT_CAP_BYTES;\n\n const { bin, args, spawnOverrides } = prepareSpawn(argv);\n const spawnOpts: SpawnOptions = {\n cwd: pathMod.resolve(opts.cwd),\n shell: false,\n windowsHide: true,\n env: process.env,\n // POSIX: detach so the child becomes its own process-group leader.\n // Required for `process.kill(-pid, …)` later — without it a group\n // kill fails and we end up only signaling the wrapper, leaving\n // grandchildren (node → vite → esbuild …) orphaned.\n // Windows: detached would spawn a new console window; leave the\n // default and use taskkill /T for tree termination.\n detached: process.platform !== \"win32\",\n ...spawnOverrides,\n };\n\n let child: ChildProcess;\n try {\n child = spawn(bin, args, spawnOpts);\n } catch (err) {\n // Can't even spawn — record a dead job so the model sees the\n // failure in list_jobs, and return a synthetic result.\n const id = this.nextId++;\n const job: InternalJob = {\n id,\n command: trimmed,\n pid: null,\n startedAt: Date.now(),\n exitCode: null,\n output: `[spawn failed] ${(err as Error).message}`,\n totalBytesWritten: 0,\n running: false,\n spawnError: (err as Error).message,\n child: null,\n readyPromise: Promise.resolve(),\n signalReady: () => {},\n closedPromise: Promise.resolve(),\n signalClosed: () => {},\n outputWaiters: new Set(),\n };\n this.jobs.set(id, job);\n return {\n jobId: id,\n pid: null,\n stillRunning: false,\n readyMatched: false,\n preview: job.output,\n exitCode: null,\n };\n }\n\n const id = this.nextId++;\n let readyResolve: () => void = () => {};\n const readyPromise = new Promise<void>((res) => {\n readyResolve = res;\n });\n let closedResolve: () => void = () => {};\n const closedPromise = new Promise<void>((res) => {\n closedResolve = res;\n });\n const job: InternalJob = {\n id,\n command: trimmed,\n pid: child.pid ?? null,\n startedAt: Date.now(),\n exitCode: null,\n output: \"\",\n totalBytesWritten: 0,\n running: true,\n child,\n readyPromise,\n signalReady: readyResolve,\n closedPromise,\n signalClosed: closedResolve,\n outputWaiters: new Set(),\n };\n this.jobs.set(id, job);\n\n let readyMatched = false;\n // Sliding window for cross-chunk ready-signal matching. A banner\n // line might land split across two reads — we want the regex to\n // see it as one piece — but testing against the full `job.output`\n // (which can be tens of KB by the time the server is up) is\n // O(N²) when 9 regexes each run on a growing buffer per chunk.\n // 1KB is comfortably bigger than any banner line we look for and\n // bounds the per-chunk regex cost regardless of total output.\n let recentForReady = \"\";\n const READY_WINDOW = 1024;\n const onData = (chunk: Buffer | string) => {\n const s = chunk.toString();\n job.totalBytesWritten += s.length;\n job.output += s;\n if (job.output.length > maxBytes) {\n // Drop the oldest bytes, but keep a marker so the model can see\n // output was truncated. Trim on a rough line boundary to avoid\n // chopping a line mid-sentence.\n const overflow = job.output.length - maxBytes;\n const cut = job.output.indexOf(\"\\n\", overflow);\n const start = cut >= 0 ? cut + 1 : overflow;\n job.output = `[… older output dropped …]\\n${job.output.slice(start)}`;\n }\n if (!readyMatched) {\n recentForReady = (recentForReady + s).slice(-READY_WINDOW);\n for (const re of READY_SIGNALS) {\n if (re.test(recentForReady)) {\n readyMatched = true;\n job.signalReady();\n break;\n }\n }\n }\n if (job.outputWaiters.size > 0) {\n const waiters = [...job.outputWaiters];\n job.outputWaiters.clear();\n for (const wake of waiters) wake();\n }\n };\n child.stdout?.on(\"data\", onData);\n child.stderr?.on(\"data\", onData);\n child.on(\"error\", (err) => {\n job.running = false;\n job.spawnError = err.message;\n job.signalReady();\n job.signalClosed();\n });\n // `exit` fires when the process is dead; `close` waits for stdio drain too.\n // On Windows + Node ≥ 24, drained stdio can lag 5–10s behind taskkill /T /F,\n // so we settle `running`/`closedPromise` on the earlier event. `close` is\n // still wired for the no-exit fallback (spawn error before any process exists).\n const settleClosed = (code: number | null) => {\n if (!job.running && job.exitCode !== null) return;\n job.running = false;\n job.exitCode = code;\n job.signalReady();\n job.signalClosed();\n };\n child.on(\"exit\", settleClosed);\n child.on(\"close\", settleClosed);\n\n const onAbort = () => this.stop(id, { graceMs: 100 });\n if (opts.signal?.aborted) {\n onAbort();\n } else {\n opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n // Race: (a) ready signal, (b) child exit, (c) wait deadline.\n let timer: ReturnType<typeof setTimeout> | null = null;\n await Promise.race([\n readyPromise,\n new Promise<void>((res) => {\n timer = setTimeout(res, waitMs);\n }),\n ]);\n if (timer) clearTimeout(timer);\n\n return {\n jobId: id,\n pid: job.pid,\n stillRunning: job.running,\n readyMatched,\n preview: job.output,\n exitCode: job.exitCode,\n };\n }\n\n read(id: number, opts: { since?: number; tailLines?: number } = {}): JobReadResult | null {\n const job = this.jobs.get(id);\n if (!job) return null;\n const full = job.output;\n let slice = full;\n if (typeof opts.since === \"number\" && opts.since >= 0 && opts.since < full.length) {\n slice = full.slice(opts.since);\n }\n if (typeof opts.tailLines === \"number\" && opts.tailLines > 0) {\n const lines = slice.split(\"\\n\");\n const keep = lines.slice(Math.max(0, lines.length - opts.tailLines));\n slice = keep.join(\"\\n\");\n }\n return {\n output: slice,\n byteLength: full.length,\n running: job.running,\n exitCode: job.exitCode,\n command: job.command,\n pid: job.pid,\n spawnError: job.spawnError,\n };\n }\n\n async waitForJob(\n id: number,\n opts: { timeoutMs?: number; waitFor?: \"exit\" | \"output-or-exit\" } = {},\n ): Promise<JobWaitResult | null> {\n const job = this.jobs.get(id);\n if (!job) return null;\n if (!job.running) {\n return {\n exited: true,\n exitCode: job.exitCode,\n latestOutput: job.output,\n };\n }\n\n const timeoutMs = Math.max(0, Math.min(300_000, opts.timeoutMs ?? 5_000));\n const waitFor = opts.waitFor ?? \"exit\";\n const startOutput = job.output;\n\n const racers: Promise<void>[] = [job.closedPromise];\n let wakeOutput: (() => void) | null = null;\n if (waitFor === \"output-or-exit\") {\n racers.push(\n new Promise<void>((resolve) => {\n wakeOutput = resolve;\n job.outputWaiters.add(resolve);\n }),\n );\n }\n let timer: ReturnType<typeof setTimeout> | null = null;\n racers.push(\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n }),\n );\n await Promise.race(racers);\n if (timer) clearTimeout(timer);\n if (wakeOutput) job.outputWaiters.delete(wakeOutput);\n\n return {\n exited: !job.running,\n exitCode: job.exitCode,\n latestOutput: latestOutputSince(startOutput, job.output),\n };\n }\n\n /** SIGTERM, wait graceMs, then SIGKILL. Idempotent on already-exited jobs. */\n async stop(id: number, opts: { graceMs?: number } = {}): Promise<JobRecord | null> {\n const job = this.jobs.get(id);\n if (!job) return null;\n if (!job.running || !job.child) return snapshot(job);\n const graceMs = Math.max(0, opts.graceMs ?? 2000);\n // Tree kill — reaches grandchildren (vite, esbuild, etc.) instead\n // of just the npm/cmd.exe wrapper that our direct child represents.\n // Falls back to child.kill() only when we somehow don't have a pid.\n if (job.pid !== null) {\n killProcessTree(job.pid, \"SIGTERM\");\n } else {\n try {\n job.child.kill(\"SIGTERM\");\n } catch {\n /* already dead — fall through */\n }\n }\n // closedPromise (not readyPromise) — readyPromise can have fired at\n // startup on a ready-signal regex match, which would short-circuit\n // this race even though the process is still alive.\n await Promise.race([job.closedPromise, new Promise<void>((res) => setTimeout(res, graceMs))]);\n if (job.running) {\n if (job.pid !== null) {\n killProcessTree(job.pid, \"SIGKILL\");\n } else {\n try {\n job.child.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n // Wait for the actual close handler — a fixed timer can return\n // before Node's `close` event fires under load (Windows taskkill\n // /T /F on a three-level tree can take ~1s to propagate).\n await Promise.race([job.closedPromise, new Promise<void>((res) => setTimeout(res, 5000))]);\n // Node ≥ 24 on Windows sometimes never fires `close` after taskkill /T /F\n // (the OS handle lingers even though the process is dead). We issued the\n // kill; trust it and settle the record so callers don't see ghost-running.\n if (job.running) {\n job.running = false;\n job.signalClosed();\n }\n }\n return snapshot(job);\n }\n\n list(): JobRecord[] {\n return [...this.jobs.values()].map(snapshot);\n }\n\n async shutdown(deadlineMs = 5000): Promise<void> {\n const start = Date.now();\n const runningJobs = [...this.jobs.values()].filter((j) => j.running && j.child);\n if (runningJobs.length === 0) return;\n\n for (const job of runningJobs) {\n if (job.pid !== null) killProcessTree(job.pid, \"SIGTERM\");\n else\n try {\n job.child?.kill(\"SIGTERM\");\n } catch {\n /* ignore */\n }\n }\n const allClose = Promise.all(runningJobs.map((j) => j.readyPromise));\n const elapsed = () => Date.now() - start;\n // Grace window: give well-behaved apps time to clean up, capped at\n // half the deadline so we always leave room for a SIGKILL pass +\n // reap confirmation.\n const graceMs = Math.min(1500, Math.max(0, deadlineMs / 2));\n await Promise.race([allClose, new Promise<void>((res) => setTimeout(res, graceMs))]);\n // Force-kill everything still alive.\n for (const job of runningJobs) {\n if (!job.running) continue;\n if (job.pid !== null) killProcessTree(job.pid, \"SIGKILL\");\n else\n try {\n job.child?.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n // Wait for close events post-SIGKILL. taskkill /T on Windows is\n // async — without this final wait, shutdown() can return while\n // grandchildren are still mid-teardown, which is what \"runningCount\n // non-zero after shutdown\" looks like.\n const remaining = Math.max(800, deadlineMs - elapsed());\n await Promise.race([allClose, new Promise<void>((res) => setTimeout(res, remaining))]);\n // Same Node ≥ 24 Windows fallback as `stop()`: settle any job whose `close`\n // event never arrived after taskkill /T /F — the kill is synchronous, the\n // notification isn't.\n for (const job of runningJobs) {\n if (job.running) {\n job.running = false;\n job.signalClosed();\n }\n }\n }\n\n /** Count of still-running jobs — drives the TUI status-bar indicator. */\n runningCount(): number {\n let n = 0;\n for (const job of this.jobs.values()) if (job.running) n++;\n return n;\n }\n}\n\ninterface InternalJob extends JobRecord {\n /** Underlying Node child process. Null only on spawn failure. */\n child: ChildProcess | null;\n /** Resolved when ready-signal fires OR the child exits. */\n readyPromise: Promise<void>;\n /** Fires readyPromise — called by ready-signal OR close/error handlers. */\n signalReady: () => void;\n /** Resolves only on close/error — never on ready-signal. Used by stop() to wait for actual exit. */\n closedPromise: Promise<void>;\n signalClosed: () => void;\n /** One-shot waiters for \"some new output arrived\". Cleared after every wake. */\n outputWaiters: Set<() => void>;\n}\n\nexport interface JobReadResult {\n output: string;\n /** Total bytes ever in the buffer (pre-slice). Caller passes back as `since`. */\n byteLength: number;\n running: boolean;\n exitCode: number | null;\n command: string;\n pid: number | null;\n spawnError?: string;\n}\n\nexport interface JobWaitResult {\n exited: boolean;\n exitCode: number | null;\n latestOutput: string;\n}\n\nfunction snapshot(job: InternalJob): JobRecord {\n return {\n id: job.id,\n command: job.command,\n pid: job.pid,\n startedAt: job.startedAt,\n exitCode: job.exitCode,\n output: job.output,\n totalBytesWritten: job.totalBytesWritten,\n running: job.running,\n spawnError: job.spawnError,\n };\n}\n\nfunction latestOutputSince(before: string, after: string): string {\n if (!before) return after;\n if (after.startsWith(before)) return after.slice(before.length);\n return after;\n}\n","import { type ChildProcess, type SpawnOptions, spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport * as pathMod from \"node:path\";\nimport { parseCommandChain, runChain } from \"../shell-chain.js\";\nimport { tokenizeCommand } from \"./parse.js\";\n\nexport const DEFAULT_TIMEOUT_SEC = 60;\nexport const DEFAULT_MAX_OUTPUT_CHARS = 32_000;\n\n/** Kill child + descendants. Windows: taskkill /T /F. Unix: SIGKILL the process group when detached, else fall back to SIGKILL on the leader. */\nexport function killProcessTree(child: ChildProcess): void {\n if (!child.pid || child.killed) return;\n if (process.platform === \"win32\") {\n try {\n spawnSync(\"taskkill\", [\"/pid\", String(child.pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n windowsHide: true,\n });\n return;\n } catch {\n /* fall through to SIGKILL */\n }\n }\n try {\n process.kill(-child.pid, \"SIGKILL\");\n return;\n } catch {\n /* not a process group leader — fall through */\n }\n try {\n child.kill(\"SIGKILL\");\n } catch {\n /* already gone */\n }\n}\n\nexport interface RunCommandResult {\n exitCode: number | null;\n /** Combined stdout+stderr, truncated to `maxOutputChars` with a marker. */\n output: string;\n /** True when the process was killed for exceeding `timeoutSec`. */\n timedOut: boolean;\n}\n\nexport async function runCommand(\n cmd: string,\n opts: {\n cwd: string;\n timeoutSec?: number;\n maxOutputChars?: number;\n signal?: AbortSignal;\n },\n): Promise<RunCommandResult> {\n const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;\n const maxChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;\n const argv = tokenizeCommand(cmd);\n if (argv.length === 0) throw new Error(\"run_command: empty command\");\n const chain = parseCommandChain(cmd);\n if (chain !== null) {\n return await runChain(chain, {\n cwd: opts.cwd,\n timeoutSec,\n maxOutputChars: maxChars,\n signal: opts.signal,\n });\n }\n const timeoutMs = timeoutSec * 1000;\n const normalizedEnv = normalizeWindowsEnvVars(process.env);\n\n const spawnOpts: SpawnOptions = {\n cwd: opts.cwd,\n shell: false, // no shell-expansion — see header comment\n windowsHide: true,\n // PYTHONIOENCODING + PYTHONUTF8 force any spawned Python child\n // (run_command running `python script.py`, etc.) to emit UTF-8\n // on stdout/stderr. Without this, Chinese-Windows defaults\n // Python's stdout encoder to GBK and `print(\"…\")` raises\n // UnicodeEncodeError on emoji / non-GBK chars — the model then\n // sees a Python traceback instead of the script's real output\n // and goes around in circles trying to fix the wrong problem.\n // Harmless on non-Python processes (env vars they don't read).\n env: { ...normalizedEnv, PYTHONIOENCODING: \"utf-8\", PYTHONUTF8: \"1\" },\n };\n\n // Windows: two layered fixes on top of shell:false —\n // 1. Resolve bare command names via PATH × PATHEXT (CreateProcess\n // ignores PATHEXT, so `npm` alone misses `npm.cmd`).\n // 2. Node 21.7.3+ (CVE-2024-27980) refuses to spawn `.cmd`/`.bat`\n // directly even with shell:false and safe args — throws\n // EINVAL at invocation time. Wrap those via `cmd.exe /d /s /c`\n // with verbatim args + manual quoting, so shell metacharacters\n // in arguments stay literal.\n // Unix path is unchanged.\n const { bin, args, spawnOverrides } = prepareSpawn(argv, { env: normalizedEnv });\n const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };\n\n return await new Promise<RunCommandResult>((resolve, reject) => {\n let child: import(\"node:child_process\").ChildProcess;\n try {\n child = spawn(bin, args, effectiveSpawnOpts);\n } catch (err) {\n reject(err);\n return;\n }\n // Collect raw Buffer chunks rather than decoding incrementally —\n // a multi-byte sequence can land split across chunks, and a naïve\n // chunk.toString() corrupts it before the second half arrives.\n // We decode once at close time, where smartDecodeOutput can also\n // sniff non-UTF-8 codepages cleanly. The byte cap mirrors the\n // prior char cap (2× maxChars worth) so a chatty process can't\n // OOM us.\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n const byteCap = maxChars * 2 * 4; // worst-case 4 bytes/char for utf-8/gbk\n let timedOut = false;\n let aborted = false;\n const killChildTree = () => killProcessTree(child);\n const killTimer = setTimeout(() => {\n timedOut = true;\n killChildTree();\n }, timeoutMs);\n const onAbort = () => {\n aborted = true;\n killChildTree();\n };\n // Check synchronously first — if the signal aborted before listener attach\n // (parent loop was already cancelled), addEventListener with `once:true`\n // never fires, child runs unbounded.\n if (opts.signal?.aborted) {\n onAbort();\n } else {\n opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n const onData = (chunk: Buffer | string) => {\n const b = typeof chunk === \"string\" ? Buffer.from(chunk) : chunk;\n if (totalBytes >= byteCap) return;\n const remaining = byteCap - totalBytes;\n if (b.length > remaining) {\n chunks.push(b.subarray(0, remaining));\n totalBytes = byteCap;\n } else {\n chunks.push(b);\n totalBytes += b.length;\n }\n };\n child.stdout?.on(\"data\", onData);\n child.stderr?.on(\"data\", onData);\n child.on(\"error\", (err) => {\n clearTimeout(killTimer);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n reject(err);\n });\n child.on(\"close\", (code) => {\n clearTimeout(killTimer);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n const merged = Buffer.concat(chunks);\n const buf = smartDecodeOutput(merged);\n const output =\n buf.length > maxChars\n ? `${buf.slice(0, maxChars)}\\n\\n[… truncated ${buf.length - maxChars} chars …]`\n : buf;\n resolve({ exitCode: code, output, timedOut });\n });\n });\n}\n\n/** GBK fallback on Windows — cmd.exe's localized error DLL and native EXE stderr ignore chcp 65001. */\nexport function smartDecodeOutput(buf: Buffer): string {\n if (buf.length === 0) return \"\";\n try {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(buf);\n } catch {\n // Fall through to platform-specific fallback.\n }\n if (process.platform === \"win32\") {\n try {\n // TextDecoder supports gbk / gb18030 in Node 18+ via the WHATWG\n // Encoding spec. gb18030 is the modern superset; falling back\n // to it covers GBK byte sequences plus the rare 4-byte CJK\n // characters that appear in newer system messages.\n return new TextDecoder(\"gb18030\").decode(buf);\n } catch {\n // Decoder unavailable in this build — fall through.\n }\n }\n // Last resort: lossy UTF-8 with replacement chars. The model still\n // gets \"something happened\" with the structural exit-code marker\n // intact, which is more useful than throwing away the entire output.\n return buf.toString(\"utf8\");\n}\n\nexport interface ResolveExecutableOptions {\n platform?: NodeJS.Platform;\n env?: { PATH?: string; PATHEXT?: string };\n isFile?: (path: string) => boolean;\n pathDelimiter?: string;\n}\n\n/** CreateProcess ignores PATHEXT — bare `npm` fails ENOENT under `shell:false` without this resolver. */\nexport function resolveExecutable(cmd: string, opts: ResolveExecutableOptions = {}): string {\n const platform = opts.platform ?? process.platform;\n if (platform !== \"win32\") return cmd;\n if (!cmd) return cmd;\n // Already a path fragment — spawn handles these natively.\n if (cmd.includes(\"/\") || cmd.includes(\"\\\\\") || pathMod.isAbsolute(cmd)) return cmd;\n // If the model wrote `npm.cmd` explicitly, respect that verbatim.\n if (pathMod.extname(cmd)) return cmd;\n\n const env = opts.env ?? process.env;\n const pathExt = (getEnvCaseInsensitive(env, \"PATHEXT\") ?? \".COM;.EXE;.BAT;.CMD\")\n .split(\";\")\n .map((e) => e.trim())\n .filter(Boolean);\n const delimiter = opts.pathDelimiter ?? (platform === \"win32\" ? \";\" : pathMod.delimiter);\n const pathDirs = (getEnvCaseInsensitive(env, \"PATH\") ?? \"\").split(delimiter).filter(Boolean);\n const isFile = opts.isFile ?? defaultIsFile;\n\n for (const dir of pathDirs) {\n for (const ext of pathExt) {\n // Force win32 join so CI tests that pass `platform: \"win32\"`\n // from a Linux runner get backslash-joined paths; the real-\n // Windows runtime path lands here too and gets the correct\n // separator regardless of where pathMod defaults.\n const full = pathMod.win32.join(dir, cmd + ext);\n if (isFile(full)) return full;\n }\n }\n return cmd;\n}\n\nexport function normalizeWindowsEnvVars(\n env: NodeJS.ProcessEnv,\n opts: { platform?: NodeJS.Platform } = {},\n): NodeJS.ProcessEnv {\n const platform = opts.platform ?? process.platform;\n if (platform !== \"win32\") return { ...env };\n\n const out: NodeJS.ProcessEnv = {};\n const pathValues: string[] = [];\n const pathExtValues: string[] = [];\n\n for (const [key, value] of Object.entries(env)) {\n const lower = key.toLowerCase();\n if (lower === \"path\") {\n if (typeof value === \"string\") pathValues.push(value);\n continue;\n }\n if (lower === \"pathext\") {\n if (typeof value === \"string\") pathExtValues.push(value);\n continue;\n }\n out[key] = value;\n }\n\n if (pathValues.length > 0) out.Path = mergeWindowsPathLike(pathValues, \";\");\n if (pathExtValues.length > 0) out.PATHEXT = mergeWindowsPathLike(pathExtValues, \";\");\n\n return out;\n}\n\nfunction getEnvCaseInsensitive(\n env: Record<string, string | undefined>,\n key: string,\n): string | undefined {\n const exact = env[key];\n if (exact !== undefined) return exact;\n const target = key.toLowerCase();\n for (const [candidate, value] of Object.entries(env)) {\n if (candidate.toLowerCase() === target) return value;\n }\n return undefined;\n}\n\nfunction mergeWindowsPathLike(values: readonly string[], delimiter: string): string {\n const seen = new Set<string>();\n const merged: string[] = [];\n\n for (const value of values) {\n for (const part of value.split(delimiter)) {\n const entry = part.trim();\n if (!entry) continue;\n const normalized = entry.toLowerCase();\n if (seen.has(normalized)) continue;\n seen.add(normalized);\n merged.push(entry);\n }\n }\n\n return merged.join(delimiter);\n}\n\nfunction defaultIsFile(full: string): boolean {\n try {\n return existsSync(full) && statSync(full).isFile();\n } catch {\n return false;\n }\n}\n\n/** Windows workarounds: PATHEXT lookup + CVE-2024-27980 prohibition on direct `.cmd`/`.bat` spawn. */\nexport function prepareSpawn(\n argv: readonly string[],\n opts: ResolveExecutableOptions = {},\n): { bin: string; args: string[]; spawnOverrides: SpawnOptions } {\n const head = argv[0] ?? \"\";\n const tail = argv.slice(1);\n const platform = opts.platform ?? process.platform;\n const resolved = resolveExecutable(head, opts);\n\n if (platform !== \"win32\") {\n return { bin: resolved, args: [...tail], spawnOverrides: {} };\n }\n\n // `.cmd` / `.bat` wrappers require cmd.exe on post-CVE Node.\n if (/\\.(cmd|bat)$/i.test(resolved)) {\n const cmdline = [resolved, ...tail].map(quoteForCmdExe).join(\" \");\n return {\n bin: \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", withUtf8Codepage(cmdline)],\n // windowsVerbatimArguments prevents Node from re-quoting the /c\n // payload — we've already composed an exact cmd.exe command\n // line. Without this Node wraps our already-quoted string in\n // another round of quotes and cmd.exe can't parse it.\n spawnOverrides: { windowsVerbatimArguments: true },\n };\n }\n\n // Bare command names that PATH × PATHEXT couldn't resolve to an\n // on-disk file — these are almost always cmd.exe built-ins (`dir`,\n // `echo`, `type`, `ver`, `vol`, `where`, `help`, …) which don't\n // exist as standalone executables. Direct spawn crashes with ENOENT;\n // routing through cmd.exe lets the built-in resolve, and if it's\n // genuinely unknown the user gets the standard \"'foo' is not\n // recognized\" message instead of a raw spawn failure.\n if (isBareWindowsName(resolved) && resolved === head) {\n const cmdline = [head, ...tail].map(quoteForCmdExe).join(\" \");\n return {\n bin: \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", withUtf8Codepage(cmdline)],\n spawnOverrides: { windowsVerbatimArguments: true },\n };\n }\n\n // PowerShell variants: chcp 65001 doesn't help here because PowerShell\n // sets its own [Console]::OutputEncoding at startup — usually system\n // codepage (CP936/CP932/CP949 on CJK Windows) or UTF-16. The result\n // is mojibake when our `chunk.toString()` UTF-8-decodes its stdout.\n // Inject a UTF-8 setup prelude into the `-Command` (or `-c`) arg so\n // any output produced thereafter is UTF-8.\n if (isPowerShellExe(resolved)) {\n const patched = injectPowerShellUtf8(tail);\n if (patched) {\n return { bin: resolved, args: patched, spawnOverrides: {} };\n }\n }\n\n return { bin: resolved, args: [...tail], spawnOverrides: {} };\n}\n\n/** Resolved bin path looks like Windows PowerShell or PowerShell Core. */\nfunction isPowerShellExe(resolved: string): boolean {\n return /(?:^|[\\\\/])(?:powershell|pwsh)(?:\\.exe)?$/i.test(resolved);\n}\n\n/** Targets `-Command` only — PowerShell quoting is finicky enough that wrapping script-file mode could break it. */\nexport function injectPowerShellUtf8(args: readonly string[]): string[] | null {\n const prelude =\n \"[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;$OutputEncoding=[System.Text.Encoding]::UTF8;\";\n for (let i = 0; i < args.length; i++) {\n const a = args[i] ?? \"\";\n if (/^-(?:Command|c)$/i.test(a) && i + 1 < args.length) {\n const out = [...args];\n out[i + 1] = `${prelude}${args[i + 1] ?? \"\"}`;\n return out;\n }\n }\n return null;\n}\n\n/** Single `&` (not `&&`) so the command still runs on Win7 where chcp can return non-zero. */\nexport function withUtf8Codepage(cmdline: string): string {\n return `chcp 65001 >nul & ${cmdline}`;\n}\n\nfunction isBareWindowsName(s: string): boolean {\n if (!s) return false;\n if (s.includes(\"/\") || s.includes(\"\\\\\")) return false;\n if (pathMod.isAbsolute(s)) return false;\n if (pathMod.extname(s)) return false;\n return true;\n}\n\n/** Doubles embedded quotes per cmd.exe's `\"\"` escape rule; bare alnum passes through unquoted. */\nexport function quoteForCmdExe(arg: string): string {\n if (arg === \"\") return '\"\"';\n if (!/[\\s\"&|<>^%(),;!]/.test(arg)) return arg;\n return `\"${arg.replace(/\"/g, '\"\"')}\"`;\n}\n","/** Parse + spawn `cmd1 | cmd2 && cmd3 > out` ourselves — never invoke a shell, sidestep PS5.1's `&&` parse error and codepage drift. */\n\nimport { type ChildProcess, type SpawnOptions, spawn } from \"node:child_process\";\nimport { closeSync, openSync } from \"node:fs\";\nimport * as pathMod from \"node:path\";\nimport { isDqEscape, killProcessTree, prepareSpawn, smartDecodeOutput } from \"./shell.js\";\n\nexport type ChainOp = \"|\" | \"||\" | \"&&\" | \";\";\n\nexport type RedirectKind = \">\" | \">>\" | \"<\" | \"2>\" | \"2>>\" | \"2>&1\" | \"&>\";\n\nexport interface Redirect {\n kind: RedirectKind;\n /** File path resolved against the chain's cwd; empty for `2>&1`. */\n target: string;\n}\n\nexport interface ChainSegment {\n argv: string[];\n redirects: Redirect[];\n}\n\nexport interface CommandChain {\n segments: ChainSegment[];\n /** length === segments.length - 1 */\n ops: ChainOp[];\n}\n\nexport class UnsupportedSyntaxError extends Error {\n constructor(detail: string) {\n super(`run_command: ${detail}`);\n this.name = \"UnsupportedSyntaxError\";\n }\n}\n\n/** Whitespace-bounded splitter — chain ops only count when they begin a token, so `--flag=1&2` stays literal. */\nfunction splitOnChainOps(cmd: string): { segs: string[]; ops: ChainOp[] } {\n const segs: string[] = [];\n const ops: ChainOp[] = [];\n let segStart = 0;\n let i = 0;\n let quote: '\"' | \"'\" | null = null;\n let atTokenStart = true;\n while (i < cmd.length) {\n const ch = cmd[i]!;\n if (quote) {\n if (ch === quote) quote = null;\n else if (quote === '\"' && isDqEscape(ch, cmd[i + 1])) i++;\n i++;\n atTokenStart = false;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n i++;\n atTokenStart = false;\n continue;\n }\n if (ch === \" \" || ch === \"\\t\") {\n i++;\n atTokenStart = true;\n continue;\n }\n if (atTokenStart) {\n let op: ChainOp | null = null;\n let opLen = 0;\n const next = cmd[i + 1];\n if (ch === \"|\" && next === \"|\") {\n op = \"||\";\n opLen = 2;\n } else if (ch === \"&\" && next === \"&\") {\n op = \"&&\";\n opLen = 2;\n } else if (ch === \"|\") {\n op = \"|\";\n opLen = 1;\n } else if (ch === \";\") {\n op = \";\";\n opLen = 1;\n }\n if (op !== null) {\n segs.push(cmd.slice(segStart, i));\n ops.push(op);\n i += opLen;\n segStart = i;\n atTokenStart = true;\n continue;\n }\n }\n i++;\n atTokenStart = false;\n }\n segs.push(cmd.slice(segStart));\n return { segs, ops };\n}\n\n/** Single-pass parser: extract argv + trailing/inline redirects from one segment string. */\nfunction parseSegment(segStr: string): ChainSegment {\n const argv: string[] = [];\n const redirects: Redirect[] = [];\n let cur = \"\";\n let curHasContent = false;\n let pending: RedirectKind | null = null;\n let quote: '\"' | \"'\" | null = null;\n const flush = () => {\n if (!curHasContent && cur.length === 0) return;\n if (pending) {\n redirects.push({ kind: pending, target: cur });\n pending = null;\n } else {\n argv.push(cur);\n }\n cur = \"\";\n curHasContent = false;\n };\n let i = 0;\n while (i < segStr.length) {\n const ch = segStr[i]!;\n if (quote) {\n if (ch === quote) {\n quote = null;\n } else if (quote === '\"' && isDqEscape(ch, segStr[i + 1])) {\n cur += segStr[++i] ?? \"\";\n curHasContent = true;\n } else {\n cur += ch;\n curHasContent = true;\n }\n i++;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n curHasContent = true;\n i++;\n continue;\n }\n if (ch === \" \" || ch === \"\\t\") {\n flush();\n i++;\n continue;\n }\n if (cur.length === 0 && !curHasContent) {\n const remaining = segStr.slice(i);\n let matched: { op: RedirectKind; len: number } | null = null;\n if (remaining.startsWith(\"2>&1\")) matched = { op: \"2>&1\", len: 4 };\n else if (remaining.startsWith(\"&>\")) matched = { op: \"&>\", len: 2 };\n else if (remaining.startsWith(\"2>>\")) matched = { op: \"2>>\", len: 3 };\n else if (remaining.startsWith(\"2>\")) matched = { op: \"2>\", len: 2 };\n else if (remaining.startsWith(\">>\")) matched = { op: \">>\", len: 2 };\n else if (remaining.startsWith(\">\")) matched = { op: \">\", len: 1 };\n else if (remaining.startsWith(\"<<\")) {\n throw new UnsupportedSyntaxError(\n 'shell operator \"<<\" is not supported — heredoc / here-string is not implemented; pass input via a \"<\" file or the binary\\'s --input flag',\n );\n } else if (remaining.startsWith(\"<\")) matched = { op: \"<\", len: 1 };\n if (matched) {\n if (pending !== null) {\n throw new UnsupportedSyntaxError(\n `redirect \"${pending}\" is missing a target file before \"${matched.op}\"`,\n );\n }\n if (matched.op === \"2>&1\") {\n redirects.push({ kind: \"2>&1\", target: \"\" });\n } else {\n pending = matched.op;\n }\n i += matched.len;\n continue;\n }\n if (ch === \"&\") {\n throw new UnsupportedSyntaxError(\n 'shell operator \"&\" is not supported — background runs need run_background, not run_command. Wrap a literal `&` arg in quotes.',\n );\n }\n }\n cur += ch;\n curHasContent = true;\n i++;\n }\n if (quote) throw new Error(`unclosed ${quote} in command`);\n flush();\n if (pending) throw new UnsupportedSyntaxError(`redirect \"${pending}\" is missing a target file`);\n if (argv.length === 0 && redirects.length > 0) {\n throw new UnsupportedSyntaxError(\n \"redirect without a command — segment must have at least one program argument\",\n );\n }\n validateRedirectFds(redirects);\n return { argv, redirects };\n}\n\n/** stdin (`<`) ≤1, stdout (`>`/`>>`/`&>`) ≤1, stderr (`2>`/`2>>`/`&>`/`2>&1`) ≤1; reject conflicts. */\nfunction validateRedirectFds(redirects: readonly Redirect[]): void {\n let stdin = 0;\n let stdout = 0;\n let stderr = 0;\n for (const r of redirects) {\n if (r.kind === \"<\") stdin++;\n else if (r.kind === \">\" || r.kind === \">>\") stdout++;\n else if (r.kind === \"2>\" || r.kind === \"2>>\" || r.kind === \"2>&1\") stderr++;\n else if (r.kind === \"&>\") {\n stdout++;\n stderr++;\n }\n }\n if (stdin > 1) throw new UnsupportedSyntaxError(\"multiple `<` stdin redirects in one segment\");\n if (stdout > 1)\n throw new UnsupportedSyntaxError(\n \"multiple stdout redirects in one segment (`>` / `>>` / `&>` conflict)\",\n );\n if (stderr > 1)\n throw new UnsupportedSyntaxError(\n \"multiple stderr redirects in one segment (`2>` / `2>>` / `&>` / `2>&1` conflict)\",\n );\n}\n\n/** Returns null on plain commands without redirects (caller takes the simple path). */\nexport function parseCommandChain(cmd: string): CommandChain | null {\n const { segs, ops } = splitOnChainOps(cmd);\n const segments: ChainSegment[] = [];\n for (let i = 0; i < segs.length; i++) {\n const trimmed = segs[i]!.trim();\n if (trimmed.length === 0) {\n const op = i === 0 ? ops[0]! : ops[i - 1]!;\n throw new UnsupportedSyntaxError(\n i === 0\n ? `empty segment before \"${op}\"`\n : i === segs.length - 1\n ? `chain ends with \"${op}\"`\n : `empty segment between \"${ops[i - 1]}\" and \"${ops[i]}\"`,\n );\n }\n segments.push(parseSegment(trimmed));\n }\n // Reject `cd` inside parsed chains — the executor cannot carry cwd\n // changes between segments, and silently running the wrong directory\n // is worse than rejecting early with clear guidance.\n for (const seg of segments) {\n const cmdName = seg.argv[0] ?? \"\";\n if (cmdName.toLowerCase() === \"cd\") {\n throw new UnsupportedSyntaxError(\n \"cd in parsed command chains does not change cwd for later segments. Use a command-native cwd flag instead, such as `npm --prefix <dir> run <script>`, `git -C <dir> ...`, or `cargo -C <dir> ...`.\",\n );\n }\n }\n\n if (ops.length === 0 && segments[0]!.redirects.length === 0) return null;\n return { segments, ops };\n}\n\n/** Each segment must individually clear the allowlist for the chain to auto-run. */\nexport function chainAllowed(\n chain: CommandChain,\n isAllowed: (segmentCmd: string) => boolean,\n): boolean {\n for (const seg of chain.segments) {\n if (!isAllowed(seg.argv.join(\" \"))) return false;\n }\n return true;\n}\n\nexport interface ChainResult {\n exitCode: number | null;\n output: string;\n timedOut: boolean;\n}\n\ninterface ChainGroup {\n segments: ChainSegment[];\n /** Op connecting the PREVIOUS group to THIS one (`||`, `&&`, `;`); null on the first group. */\n opBefore: Exclude<ChainOp, \"|\"> | null;\n}\n\n/** Pipe groups are runs of segments joined by `|`; sequential ops (`||`, `&&`, `;`) split them. */\nfunction groupChain(chain: CommandChain): ChainGroup[] {\n const groups: ChainGroup[] = [{ segments: [chain.segments[0]!], opBefore: null }];\n for (let i = 0; i < chain.ops.length; i++) {\n const op = chain.ops[i]!;\n const next = chain.segments[i + 1]!;\n if (op === \"|\") {\n groups[groups.length - 1]!.segments.push(next);\n } else {\n groups.push({ segments: [next], opBefore: op });\n }\n }\n return groups;\n}\n\nexport interface RunChainOptions {\n cwd: string;\n timeoutSec: number;\n maxOutputChars: number;\n signal?: AbortSignal;\n}\n\nexport async function runChain(chain: CommandChain, opts: RunChainOptions): Promise<ChainResult> {\n const groups = groupChain(chain);\n const buf = new OutputBuffer(opts.maxOutputChars * 2 * 4);\n const deadline = Date.now() + opts.timeoutSec * 1000;\n let lastExit: number | null = 0;\n let timedOut = false;\n for (const group of groups) {\n if (group.opBefore === \"&&\" && lastExit !== 0) continue;\n if (group.opBefore === \"||\" && lastExit === 0) continue;\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n timedOut = true;\n break;\n }\n const result = await runPipeGroup(group.segments, {\n cwd: opts.cwd,\n timeoutMs: remainingMs,\n buf,\n signal: opts.signal,\n });\n lastExit = result.exitCode;\n if (result.timedOut) {\n timedOut = true;\n break;\n }\n if (opts.signal?.aborted) break;\n }\n const output = buf.toString();\n const truncated =\n output.length > opts.maxOutputChars\n ? `${output.slice(0, opts.maxOutputChars)}\\n\\n[… truncated ${output.length - opts.maxOutputChars} chars …]`\n : output;\n return { exitCode: lastExit, output: truncated, timedOut };\n}\n\ninterface PipeGroupResult {\n exitCode: number | null;\n timedOut: boolean;\n}\n\ninterface PipeGroupOptions {\n cwd: string;\n timeoutMs: number;\n buf: OutputBuffer;\n signal?: AbortSignal;\n}\n\ninterface SegmentStdio {\n /** Input fd for `<` redirect, or null when reading from prev pipe / nothing. */\n stdinFd: number | null;\n /** Output fd for `>`/`>>`/`&>` redirect, or null when writing to pipe / our buffer. */\n stdoutFd: number | null;\n /** Output fd for `2>`/`2>>`/`&>` redirect, or null when default. */\n stderrFd: number | null;\n mergeStderrToStdout: boolean;\n toClose: number[];\n}\n\nfunction openRedirects(redirects: readonly Redirect[], cwd: string): SegmentStdio {\n let stdinFd: number | null = null;\n let stdoutFd: number | null = null;\n let stderrFd: number | null = null;\n let mergeStderrToStdout = false;\n let bothFd: number | null = null;\n const toClose: number[] = [];\n const open = (target: string, flags: \"r\" | \"w\" | \"a\"): number => {\n const resolved = pathMod.resolve(cwd, target);\n const fd = openSync(resolved, flags);\n toClose.push(fd);\n return fd;\n };\n for (const r of redirects) {\n if (r.kind === \"<\") stdinFd = open(r.target, \"r\");\n else if (r.kind === \">\") stdoutFd = open(r.target, \"w\");\n else if (r.kind === \">>\") stdoutFd = open(r.target, \"a\");\n else if (r.kind === \"2>\") stderrFd = open(r.target, \"w\");\n else if (r.kind === \"2>>\") stderrFd = open(r.target, \"a\");\n else if (r.kind === \"&>\") {\n bothFd = open(r.target, \"w\");\n stdoutFd = bothFd;\n stderrFd = bothFd;\n } else if (r.kind === \"2>&1\") {\n mergeStderrToStdout = true;\n }\n }\n return { stdinFd, stdoutFd, stderrFd, mergeStderrToStdout, toClose };\n}\n\nasync function runPipeGroup(\n segments: ChainSegment[],\n opts: PipeGroupOptions,\n): Promise<PipeGroupResult> {\n const env = { ...process.env, PYTHONIOENCODING: \"utf-8\", PYTHONUTF8: \"1\" };\n const children: ChildProcess[] = [];\n const allFds: number[] = [];\n let timedOut = false;\n const killAll = () => {\n for (const c of children) killProcessTree(c);\n };\n const killTimer = setTimeout(() => {\n timedOut = true;\n killAll();\n }, opts.timeoutMs);\n const onAbort = () => killAll();\n if (opts.signal?.aborted) {\n onAbort();\n } else {\n opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n }\n try {\n for (let i = 0; i < segments.length; i++) {\n const isFirst = i === 0;\n const isLast = i === segments.length - 1;\n const seg = segments[i]!;\n const io = openRedirects(seg.redirects, opts.cwd);\n allFds.push(...io.toClose);\n const { bin, args, spawnOverrides } = prepareSpawn(seg.argv);\n const stdoutSpec = io.stdoutFd !== null ? io.stdoutFd : \"pipe\";\n const stderrSpec =\n io.stderrFd !== null ? io.stderrFd : io.mergeStderrToStdout ? stdoutSpec : \"pipe\";\n const stdinSpec = io.stdinFd !== null ? io.stdinFd : isFirst ? \"ignore\" : \"pipe\";\n const spawnOpts: SpawnOptions = {\n cwd: opts.cwd,\n shell: false,\n windowsHide: true,\n env,\n stdio: [stdinSpec, stdoutSpec, stderrSpec],\n ...spawnOverrides,\n };\n let child: ChildProcess;\n try {\n child = spawn(bin, args, spawnOpts);\n } catch (err) {\n for (const fd of allFds) tryClose(fd);\n killAll();\n clearTimeout(killTimer);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n throw err;\n }\n children.push(child);\n if (!isFirst && io.stdinFd === null) {\n const prev = children[i - 1]!;\n prev.stdout?.on(\"error\", () => {});\n child.stdin?.on(\"error\", () => {});\n const prevMergesStderr =\n segments[i - 1]!.redirects.some((r) => r.kind === \"2>&1\") && !!prev.stderr;\n if (prevMergesStderr && prev.stderr) {\n prev.stderr.on(\"error\", () => {});\n let openSources = 2;\n const closeIfDone = () => {\n if (--openSources === 0) child.stdin?.end();\n };\n prev.stdout?.pipe(child.stdin!, { end: false });\n prev.stderr.pipe(child.stdin!, { end: false });\n prev.stdout?.once(\"end\", closeIfDone);\n prev.stderr.once(\"end\", closeIfDone);\n } else {\n prev.stdout?.pipe(child.stdin!);\n }\n }\n if (child.stderr && io.stderrFd === null && !(io.mergeStderrToStdout && !isLast)) {\n child.stderr.on(\"data\", (chunk: Buffer | string) => opts.buf.push(toBuf(chunk)));\n }\n if (isLast && child.stdout && io.stdoutFd === null) {\n child.stdout.on(\"data\", (chunk: Buffer | string) => opts.buf.push(toBuf(chunk)));\n if (io.mergeStderrToStdout && child.stderr && io.stderrFd === null) {\n child.stderr.removeAllListeners(\"data\");\n child.stderr.on(\"data\", (chunk: Buffer | string) => opts.buf.push(toBuf(chunk)));\n }\n }\n }\n const exits = await Promise.all(\n children.map(\n (c) =>\n new Promise<number | null>((resolve) => {\n c.once(\"error\", () => resolve(null));\n c.once(\"close\", (code) => resolve(code));\n }),\n ),\n );\n return { exitCode: exits[exits.length - 1] ?? null, timedOut };\n } finally {\n for (const fd of allFds) tryClose(fd);\n clearTimeout(killTimer);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n }\n}\n\nfunction tryClose(fd: number): void {\n try {\n closeSync(fd);\n } catch {\n /* already closed by spawn handover or kernel */\n }\n}\n\nfunction toBuf(chunk: Buffer | string): Buffer {\n return typeof chunk === \"string\" ? Buffer.from(chunk) : chunk;\n}\n\nclass OutputBuffer {\n private chunks: Buffer[] = [];\n private bytes = 0;\n constructor(private readonly cap: number) {}\n push(b: Buffer): void {\n if (this.bytes >= this.cap) return;\n const remaining = this.cap - this.bytes;\n if (b.length > remaining) {\n this.chunks.push(b.subarray(0, remaining));\n this.bytes = this.cap;\n } else {\n this.chunks.push(b);\n this.bytes += b.length;\n }\n }\n toString(): string {\n return smartDecodeOutput(Buffer.concat(this.chunks));\n }\n}\n","import { type CommandChain, chainAllowed, parseCommandChain } from \"../shell-chain.js\";\n\n/** Read-only reports + test runners whose failure mode is \"exit 1 with output\". */\nexport const BUILTIN_ALLOWLIST: ReadonlyArray<string> = [\n // Repo inspection\n \"git status\",\n \"git diff\",\n \"git log\",\n \"git show\",\n \"git blame\",\n \"git branch\",\n \"git remote\",\n \"git rev-parse\",\n \"git config --get\",\n // Filesystem inspection\n \"ls\",\n \"pwd\",\n \"cat\",\n \"head\",\n \"tail\",\n \"wc\",\n \"file\",\n \"tree\",\n \"find\",\n \"grep\",\n \"rg\",\n // Language version probes\n \"node --version\",\n \"node -v\",\n \"npm --version\",\n \"npx --version\",\n \"python --version\",\n \"python3 --version\",\n \"cargo --version\",\n \"go version\",\n \"rustc --version\",\n \"deno --version\",\n \"bun --version\",\n // Test runners (non-destructive by convention)\n \"npm test\",\n \"npm run test\",\n \"npx vitest run\",\n \"npx vitest\",\n \"npx jest\",\n \"pytest\",\n \"python -m pytest\",\n \"cargo test\",\n \"cargo check\",\n \"cargo clippy\",\n \"go test\",\n \"go vet\",\n \"deno test\",\n \"bun test\",\n // Linters / typecheckers (read-only by convention)\n \"npm run lint\",\n \"npm run typecheck\",\n \"npx tsc --noEmit\",\n \"npx biome check\",\n \"npx eslint\",\n \"npx prettier --check\",\n \"ruff\",\n \"mypy\",\n];\n\n/** Inside `\"…\"` only `\\\"` and `\\\\` are escapes — `\\X` otherwise stays literal so Windows paths like `\"C:\\Users\\foo\\.bar\"` survive tokenization. */\nexport function isDqEscape(prev: string, next: string | undefined): boolean {\n return prev === \"\\\\\" && (next === '\"' || next === \"\\\\\");\n}\n\n/** No env / glob / backtick / `$(…)` expansion — prevents bypass of allowlist via concatenation. */\nexport function tokenizeCommand(cmd: string): string[] {\n const out: string[] = [];\n let cur = \"\";\n let quote: '\"' | \"'\" | null = null;\n for (let i = 0; i < cmd.length; i++) {\n const ch = cmd[i]!;\n if (quote) {\n if (ch === quote) {\n quote = null;\n } else if (quote === '\"' && isDqEscape(ch, cmd[i + 1])) {\n cur += cmd[++i];\n } else {\n cur += ch;\n }\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n continue;\n }\n if (ch === \" \" || ch === \"\\t\") {\n if (cur.length > 0) {\n out.push(cur);\n cur = \"\";\n }\n continue;\n }\n cur += ch;\n }\n if (quote) throw new Error(`unclosed ${quote} in command`);\n if (cur.length > 0) out.push(cur);\n return out;\n}\n\n/** Up-front detection — without it, `dir | findstr foo` quotes `|` literal and pipe silently fails. */\nexport function detectShellOperator(cmd: string): string | null {\n const opPrefix = /^(?:2>&1|&>|\\|{1,2}|&{1,2}|2>{1,2}|>{1,2}|<{1,2})/;\n let cur = \"\";\n let curQuoted = false;\n let quote: '\"' | \"'\" | null = null;\n const check = (): string | null => {\n if (cur.length === 0 && !curQuoted) return null;\n if (!curQuoted) {\n const m = opPrefix.exec(cur);\n if (m) return m[0] ?? null;\n }\n return null;\n };\n for (let i = 0; i < cmd.length; i++) {\n const ch = cmd[i]!;\n if (quote) {\n if (ch === quote) {\n quote = null;\n } else if (quote === '\"' && isDqEscape(ch, cmd[i + 1])) {\n cur += cmd[++i];\n curQuoted = true;\n } else {\n cur += ch;\n curQuoted = true;\n }\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n curQuoted = true;\n continue;\n }\n if (ch === \" \" || ch === \"\\t\") {\n const op = check();\n if (op) return op;\n cur = \"\";\n curQuoted = false;\n continue;\n }\n cur += ch;\n }\n if (quote) return null; // let tokenizeCommand throw the unclosed-quote error\n return check();\n}\n\n/** Per-prefix demotion: an otherwise-allowlisted match falls back to the confirm gate when one of these tokens appears in the tail. Issue #257: `git branch -D` skipped review. Each token also matches its `--flag=value` form. */\nconst RISKY_ARGS: Readonly<Record<string, ReadonlyArray<string>>> = {\n // Branch / remote mutation\n \"git branch\": [\"-d\", \"-D\", \"--delete\", \"-m\", \"-M\", \"--move\", \"-c\", \"-C\", \"--copy\", \"--force\"],\n \"git remote\": [\"add\", \"remove\", \"rm\", \"rename\", \"set-url\", \"set-head\", \"prune\"],\n // `--output` writes to an arbitrary path; `--ext-diff` invokes user-config'd external programs.\n \"git diff\": [\"--output\", \"--ext-diff\"],\n \"git log\": [\"--output\"],\n \"git show\": [\"--output\"],\n // `-exec*` / `-ok*` are RCE; `-delete` and `-fprint*` / `-fls` write to arbitrary paths.\n find: [\n \"-delete\",\n \"-exec\",\n \"-execdir\",\n \"-ok\",\n \"-okdir\",\n \"-fprint\",\n \"-fprint0\",\n \"-fprintf\",\n \"-fls\",\n ],\n // `-o FILE` writes the tree to an arbitrary path.\n tree: [\"-o\"],\n // Auto-fix mutates source files.\n \"npx eslint\": [\"--fix\", \"--fix-dry-run\"],\n \"npx biome check\": [\"--write\", \"--apply\", \"--apply-unsafe\"],\n ruff: [\"--fix\", \"--unsafe-fixes\", \"format\"],\n};\n\nfunction tailHasRisky(tail: readonly string[], risky: readonly string[]): boolean {\n for (const a of tail) {\n for (const r of risky) {\n if (a === r) return true;\n if (a.startsWith(`${r}=`)) return true;\n }\n }\n return false;\n}\n\n/** Allowlist match on leading argv tokens; demoted by `RISKY_ARGS` when a destructive flag appears in the tail. */\nexport function isAllowed(cmd: string, extra: readonly string[] = []): boolean {\n let argv: string[];\n try {\n argv = tokenizeCommand(cmd);\n } catch {\n return false;\n }\n if (argv.length === 0) return false;\n\n const allowlist = [...BUILTIN_ALLOWLIST, ...extra];\n for (const prefix of allowlist) {\n const prefixTokens = prefix.split(\" \");\n if (argv.length < prefixTokens.length) continue;\n let match = true;\n for (let i = 0; i < prefixTokens.length; i++) {\n if (argv[i] !== prefixTokens[i]) {\n match = false;\n break;\n }\n }\n if (!match) continue;\n\n const risky = RISKY_ARGS[prefix];\n if (risky && tailHasRisky(argv.slice(prefixTokens.length), risky)) return false;\n return true;\n }\n return false;\n}\n\n/** For chain commands, every segment must individually clear the allowlist. */\nexport function isCommandAllowed(cmd: string, extra: readonly string[] = []): boolean {\n let chain: CommandChain | null;\n try {\n chain = parseCommandChain(cmd);\n } catch {\n return false;\n }\n if (chain === null) return isAllowed(cmd, extra);\n return chainAllowed(chain, (seg) => isAllowed(seg, extra));\n}\n","/** web_search uses Mojeek (DDG returns anti-bot 202 to unauthenticated POSTs); web_fetch sniffs HTML to text. */\n\nimport { parse as parseHtml } from \"node-html-parser\";\nimport {\n webSearchEndpoint as loadWebSearchEndpoint,\n webSearchEngine as loadWebSearchEngine,\n} from \"../config.js\";\nimport { t } from \"../i18n/index.js\";\nimport type { ToolRegistry } from \"../tools.js\";\n\nexport interface SearchResult {\n title: string;\n url: string;\n snippet: string;\n}\n\nexport interface PageContent {\n url: string;\n title?: string;\n text: string;\n /** True when the extracted text was clipped to fit the cap. */\n truncated: boolean;\n}\n\nexport interface WebFetchOptions {\n /** Max bytes of extracted text. Defaults to 32_000 to match tool-result cap. */\n maxChars?: number;\n /** Timeout in ms. Defaults to 15_000. */\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nexport interface WebSearchOptions {\n topK?: number;\n signal?: AbortSignal;\n /** Backend engine: \"mojeek\" (scrapes Mojeek HTML) or \"searxng\" (self-hosted SearXNG JSON API). */\n engine?: \"mojeek\" | \"searxng\";\n /** Base URL for SearXNG. Default http://localhost:8080. */\n endpoint?: string;\n}\n\nconst DEFAULT_FETCH_MAX_CHARS = 32_000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 15_000;\nconst DEFAULT_TOPK = 5;\n/** Bytes cap applied before `resp.text()` — char cap can't fire until the body is fully buffered. */\nconst FETCH_MAX_BYTES = 10 * 1024 * 1024;\n// Real-browser UA. Servers like Mojeek are bot-friendly but still gate\n// obvious scraper UAs; a stock Chrome string avoids the fast-path block.\nconst USER_AGENT =\n \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\";\nconst MOJEEK_ENDPOINT = \"https://www.mojeek.com/search\";\n\n/** Pick a status-specific webErrors key so the model gets an actionable hint, not a bare status. */\nfunction searchStatusError(status: number): string {\n if (status === 429) return t(\"webErrors.rateLimit429\");\n if (status === 403) return t(\"webErrors.forbidden403\");\n if (status >= 500 && status <= 599) return t(\"webErrors.serverError5xx\", { status });\n return t(\"webErrors.status\", { status });\n}\n\nfunction fetchStatusError(status: number, url: string): string {\n if (status === 429) return t(\"webErrors.fetchRateLimit429\", { url });\n if (status === 403) return t(\"webErrors.fetchForbidden403\", { url });\n if (status >= 500 && status <= 599) return t(\"webErrors.fetchServerError5xx\", { status, url });\n return t(\"webErrors.fetchStatus\", { status, url });\n}\n\n/** Distinguishes \"truly 0 results\" from \"layout changed / blocked\" so callers can tell. */\nexport async function webSearch(\n query: string,\n opts: WebSearchOptions = {},\n): Promise<SearchResult[]> {\n if (opts.engine === \"searxng\") {\n return searchSearxng(query, opts);\n }\n return searchMojeek(query, opts);\n}\n\nasync function searchMojeek(query: string, opts: WebSearchOptions = {}): Promise<SearchResult[]> {\n const topK = Math.max(1, Math.min(10, opts.topK ?? DEFAULT_TOPK));\n const resp = await fetch(`${MOJEEK_ENDPOINT}?q=${encodeURIComponent(query)}`, {\n headers: {\n \"User-Agent\": USER_AGENT,\n Accept: \"text/html,application/xhtml+xml,application/xml;q=0.9\",\n \"Accept-Language\": \"en-US,en;q=0.9\",\n },\n signal: opts.signal,\n redirect: \"follow\",\n });\n if (!resp.ok) throw new Error(searchStatusError(resp.status));\n const html = await resp.text();\n const results = parseMojeekResults(html).slice(0, topK);\n if (results.length === 0) {\n if (/no results found|did not match any documents/i.test(html)) return [];\n if (/captcha|verify you are human|access denied|forbidden/i.test(html)) {\n throw new Error(t(\"webErrors.mojeekBlocked\"));\n }\n throw new Error(\n t(\"webErrors.mojeekNoResults\", {\n chars: html.length,\n preview: html.slice(0, 120).replace(/\\s+/g, \" \"),\n }),\n );\n }\n return results;\n}\n\n/** Parse + validate a SearXNG endpoint. Returns origin (protocol + host). */\nfunction normalizeSearxngEndpoint(raw: string): string {\n let url: URL;\n try {\n url = new URL(raw.includes(\"://\") ? raw : `http://${raw}`);\n } catch {\n throw new Error(t(\"webErrors.invalidEndpoint\", { endpoint: raw }));\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(t(\"webErrors.endpointMustBeHttp\", { protocol: url.protocol }));\n }\n return url.origin;\n}\n\nasync function searchSearxng(query: string, opts: WebSearchOptions = {}): Promise<SearchResult[]> {\n const topK = Math.max(1, Math.min(10, opts.topK ?? DEFAULT_TOPK));\n const baseUrl = normalizeSearxngEndpoint(opts.endpoint ?? \"http://localhost:8080\");\n\n // JSON API is often blocked by SearXNG's default limiter; HTML always works.\n const url = `${baseUrl}/search?format=html&q=${encodeURIComponent(query)}`;\n let resp: Response;\n try {\n resp = await fetch(url, {\n headers: {\n \"User-Agent\": USER_AGENT,\n Accept: \"text/html\",\n },\n signal: opts.signal,\n });\n } catch (err) {\n if (err instanceof TypeError && (err as Error).message.includes(\"fetch\")) {\n throw new Error(\n t(\"webErrors.cannotReach\", { endpoint: opts.endpoint ?? \"http://localhost:8080\" }),\n );\n }\n throw err;\n }\n if (!resp.ok) throw new Error(searchStatusError(resp.status));\n const html = await resp.text();\n const results = parseSearxngHtmlResults(html).slice(0, topK);\n if (results.length === 0) {\n if (/no results found|did not match any documents/i.test(html)) return [];\n throw new Error(t(\"webErrors.searxngNoResults\", { chars: html.length }));\n }\n return results;\n}\n\n/** Parse SearXNG HTML search results using node-html-parser. */\nexport function parseSearxngHtmlResults(html: string): SearchResult[] {\n const root = parseHtml(html);\n const results: SearchResult[] = [];\n\n // Try <article class=\"result\"> first (default SearXNG theme)\n const articles = root.querySelectorAll(\"article.result, div.result\");\n if (articles.length > 0) {\n for (const article of articles) {\n const link = article.querySelector(\"h3 a, h4 a, a[href^='http']\");\n if (!link) continue;\n const href = link.getAttribute(\"href\");\n if (!href) continue;\n const title = link.textContent.trim();\n if (!title) continue;\n let snippet = \"\";\n for (const p of article.querySelectorAll(\"p\")) {\n const text = p.textContent.trim();\n if (text.length > 10 && !text.includes(title)) {\n snippet = text;\n break;\n }\n }\n if (!snippet) {\n const cs = article.querySelector(\".content, .result-content, [class*='snippet']\");\n if (cs) snippet = cs.textContent.trim();\n }\n results.push({ title, url: href, snippet });\n }\n return results;\n }\n\n // Fallback: <h3><a href> pairs directly\n for (const a of root.querySelectorAll(\"h3 a[href]\")) {\n const href = a.getAttribute(\"href\");\n if (!href || href.startsWith(\"#\")) continue;\n const title = a.textContent.trim();\n if (!title) continue;\n let snippet = \"\";\n const p = a.parentNode?.parentNode?.querySelector(\"p\");\n if (p) snippet = p.textContent.trim();\n results.push({ title, url: href, snippet });\n }\n return results;\n}\n\n/** Title-anchor + snippet-paragraph passes paired positionally — robust to attribute reorder. */\nexport function parseMojeekResults(html: string): SearchResult[] {\n const titles: string[] = [];\n const titleAnchorRe = /<a\\b[^>]*\\bclass=\"title\"[^>]*>[\\s\\S]*?<\\/a>/g;\n let m: RegExpExecArray | null;\n while (true) {\n m = titleAnchorRe.exec(html);\n if (m === null) break;\n titles.push(m[0]);\n }\n\n const snippets: string[] = [];\n const snippetRe = /<p\\b[^>]*\\bclass=\"s\"[^>]*>([\\s\\S]*?)<\\/p>/g;\n while (true) {\n m = snippetRe.exec(html);\n if (m === null) break;\n snippets.push(m[1] ?? \"\");\n }\n\n const hrefRe = /href=\"([^\"]+)\"/;\n const innerRe = /<a\\b[^>]*>([\\s\\S]*?)<\\/a>/;\n const results: SearchResult[] = [];\n for (let i = 0; i < titles.length; i++) {\n const anchor = titles[i]!;\n const hrefMatch = anchor.match(hrefRe);\n const innerMatch = anchor.match(innerRe);\n if (!hrefMatch?.[1]) continue;\n results.push({\n title: decodeHtmlEntities(stripHtml(innerMatch?.[1] ?? \"\")).trim(),\n url: hrefMatch[1],\n snippet: decodeHtmlEntities(stripHtml(snippets[i] ?? \"\"))\n .replace(/\\s+/g, \" \")\n .trim(),\n });\n }\n return results;\n}\n\nexport async function webFetch(url: string, opts: WebFetchOptions = {}): Promise<PageContent> {\n const maxChars = opts.maxChars ?? DEFAULT_FETCH_MAX_CHARS;\n const timeoutMs = opts.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n const ctl = new AbortController();\n // Track whether the abort came from our internal timer vs the caller's\n // signal — only the timer-driven abort should produce a \"timed out\" hint.\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n ctl.abort();\n }, timeoutMs);\n // Forward the caller's abort too so an Esc during a long fetch is respected.\n const cancel = () => ctl.abort();\n opts.signal?.addEventListener(\"abort\", cancel, { once: true });\n let resp: Response;\n try {\n resp = await fetch(url, {\n headers: { \"User-Agent\": USER_AGENT, Accept: \"text/html,text/plain,*/*\" },\n signal: ctl.signal,\n redirect: \"follow\",\n });\n } catch (err) {\n if (timedOut) {\n throw new Error(t(\"webErrors.fetchTimeout\", { ms: timeoutMs, url }));\n }\n throw err;\n } finally {\n clearTimeout(timer);\n opts.signal?.removeEventListener(\"abort\", cancel);\n }\n if (!resp.ok) throw new Error(fetchStatusError(resp.status, url));\n const contentType = resp.headers.get(\"content-type\") ?? \"\";\n // Pre-check Content-Length when the server provides it. Cheaper to\n // refuse upfront than to start streaming a 1GB ISO.\n const declaredLen = Number(resp.headers.get(\"content-length\") ?? \"\");\n if (Number.isFinite(declaredLen) && declaredLen > FETCH_MAX_BYTES) {\n throw new Error(t(\"webErrors.fetchTooLarge\", { len: declaredLen, cap: FETCH_MAX_BYTES, url }));\n }\n const raw = await readBodyCapped(resp, FETCH_MAX_BYTES);\n const title = extractTitle(raw);\n const text = contentType.includes(\"text/html\") ? htmlToText(raw) : raw;\n const truncated = text.length > maxChars;\n const finalText = truncated\n ? `${text.slice(0, maxChars)}\\n\\n[… truncated ${text.length - maxChars} chars …]`\n : text;\n return { url, title, text: finalText, truncated };\n}\n\n/** Streams + caps so chunked responses (or servers lying about Content-Length) can't balloon the heap. */\nasync function readBodyCapped(resp: Response, maxBytes: number): Promise<string> {\n if (!resp.body) return await resp.text();\n const reader = resp.body.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let total = 0;\n let out = \"\";\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n try {\n await reader.cancel();\n } catch {\n /* already torn down */\n }\n throw new Error(t(\"webErrors.fetchBodyTooLarge\", { cap: maxBytes, seen: total }));\n }\n out += decoder.decode(value, { stream: true });\n }\n out += decoder.decode();\n } finally {\n try {\n reader.releaseLock();\n } catch {\n /* reader already cancelled / released */\n }\n }\n return out;\n}\n\n/** Hard cap so the per-request HTML budget stays linear-time even on adversarial pages. */\nconst MAX_HTML_INPUT = 5 * 1024 * 1024;\n\nconst STRIP_BLOCK_TAGS = \"script, style, noscript, nav, footer, aside, svg\";\n\n/** Block-level tags that should produce a paragraph break in the extracted text. */\nconst BLOCK_BREAK_TAGS = new Set([\n \"p\",\n \"div\",\n \"br\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"li\",\n \"tr\",\n \"section\",\n \"article\",\n]);\n\nexport function htmlToText(html: string): string {\n const input = html.length > MAX_HTML_INPUT ? html.slice(0, MAX_HTML_INPUT) : html;\n // Real HTML parser — sidesteps the well-known regex anti-patterns\n // (`<X[\\s\\S]*?</X>`, `<[^>]+>`) CodeQL flags as bad-tag-filter and\n // incomplete-multi-character-sanitization.\n const root = parseHtml(input);\n for (const node of root.querySelectorAll(STRIP_BLOCK_TAGS)) node.remove();\n\n const out: string[] = [];\n walkExtract(root, out);\n let s = out.join(\"\");\n s = decodeHtmlEntities(s);\n s = s.replace(/[ \\t]+/g, \" \");\n s = s.replace(/\\n[ \\t]+/g, \"\\n\");\n s = s.replace(/\\n{3,}/g, \"\\n\\n\");\n return s.trim();\n}\n\ninterface WalkableNode {\n nodeType: number;\n rawText?: string;\n text?: string;\n rawTagName?: string;\n childNodes: WalkableNode[];\n}\n\nfunction walkExtract(node: WalkableNode, out: string[]): void {\n // nodeType 3 = TEXT_NODE; 1 = ELEMENT_NODE per node-html-parser.\n if (node.nodeType === 3) {\n out.push(node.rawText ?? node.text ?? \"\");\n return;\n }\n const tag = node.rawTagName?.toLowerCase();\n const isBreak = tag !== undefined && BLOCK_BREAK_TAGS.has(tag);\n if (isBreak) out.push(\"\\n\");\n for (const child of node.childNodes) walkExtract(child, out);\n if (isBreak) out.push(\"\\n\");\n}\n\nfunction stripHtml(s: string): string {\n return parseHtml(s).text;\n}\n\nconst HTML_ENTITIES: Readonly<Record<string, string>> = {\n amp: \"&\",\n lt: \"<\",\n gt: \">\",\n quot: '\"',\n apos: \"'\",\n nbsp: \" \",\n};\n\n/** Single-pass decode — the previous chained `replace`s decoded `&amp;lt;` into `<` because `&amp;` ran before `&lt;`. */\nfunction decodeHtmlEntities(s: string): string {\n return s.replace(/&(#\\d+|#x[0-9a-fA-F]+|\\w+);/g, (raw, name: string) => {\n if (name.startsWith(\"#x\") || name.startsWith(\"#X\")) {\n const code = Number.parseInt(name.slice(2), 16);\n return Number.isFinite(code) ? String.fromCodePoint(code) : raw;\n }\n if (name.startsWith(\"#\")) {\n const code = Number.parseInt(name.slice(1), 10);\n return Number.isFinite(code) ? String.fromCodePoint(code) : raw;\n }\n return HTML_ENTITIES[name.toLowerCase()] ?? raw;\n });\n}\n\nfunction extractTitle(html: string): string | undefined {\n const m = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n if (!m?.[1]) return undefined;\n return m[1].replace(/\\s+/g, \" \").trim() || undefined;\n}\n\nexport interface WebToolsOptions {\n /** Default top-K for `web_search` when the model doesn't specify. */\n defaultTopK?: number;\n /** Byte cap for `web_fetch` extracted text. */\n maxFetchChars?: number;\n /** Backend engine: \"mojeek\" (default, scrapes Mojeek) or \"searxng\" (self-hosted SearXNG). */\n webSearchEngine?: \"mojeek\" | \"searxng\";\n /** Base URL for SearXNG (default http://localhost:8080). */\n webSearchEndpoint?: string;\n}\n\nexport function registerWebTools(registry: ToolRegistry, opts: WebToolsOptions = {}): ToolRegistry {\n const defaultTopK = opts.defaultTopK ?? DEFAULT_TOPK;\n const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;\n\n registry.register({\n name: \"web_search\",\n description:\n \"Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state — anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this.\" +\n \" To change the backend, use /web-search-engine mojeek|searxng.\",\n readOnly: true,\n parallelSafe: true,\n parameters: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Natural-language search query.\" },\n topK: {\n type: \"integer\",\n description: `Number of results to return (1..10). Default ${defaultTopK}.`,\n },\n },\n required: [\"query\"],\n },\n fn: async (args: { query: string; topK?: number }, ctx) => {\n const engine = opts.webSearchEngine ?? loadWebSearchEngine();\n const endpoint = opts.webSearchEndpoint ?? loadWebSearchEndpoint();\n const results = await webSearch(args.query, {\n topK: args.topK ?? defaultTopK,\n signal: ctx?.signal,\n engine,\n endpoint,\n });\n return formatSearchResults(args.query, results);\n },\n });\n\n registry.register({\n name: \"web_fetch\",\n description:\n \"Download a URL and return its visible text content (HTML pages get scripts/styles/nav stripped). Truncated at the tool-result cap. Use after web_search when a snippet isn't enough.\",\n readOnly: true,\n parallelSafe: true,\n parameters: {\n type: \"object\",\n properties: {\n url: { type: \"string\", description: \"Absolute http:// or https:// URL.\" },\n },\n required: [\"url\"],\n },\n fn: async (args: { url: string }, ctx) => {\n if (!/^https?:\\/\\//i.test(args.url)) {\n throw new Error(t(\"webErrors.fetchInvalidUrl\"));\n }\n const page = await webFetch(args.url, { maxChars: maxFetchChars, signal: ctx?.signal });\n const header = page.title ? `${page.title}\\n${page.url}` : page.url;\n return `${header}\\n\\n${page.text}`;\n },\n });\n\n return registry;\n}\n\nexport function formatSearchResults(query: string, results: SearchResult[]): string {\n const lines: string[] = [`query: ${query}`, `\\nresults (${results.length}):`];\n results.forEach((r, i) => {\n lines.push(`\\n${i + 1}. ${r.title}`);\n lines.push(` ${r.url}`);\n if (r.snippet) lines.push(` ${r.snippet}`);\n });\n return lines.join(\"\\n\");\n}\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport function loadDotenv(path = \".env\"): void {\n let raw: string;\n try {\n raw = readFileSync(resolve(process.cwd(), path), \"utf8\");\n } catch {\n return;\n }\n for (const line of raw.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).trim();\n let value = trimmed.slice(eq + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n if (process.env[key] === undefined) process.env[key] = value;\n }\n}\n","/** Transcripts are receipts (cost/usage/prefix); sessions are memory (ChatMessages). Don't conflate. */\n\nimport { type WriteStream, createWriteStream, readFileSync } from \"node:fs\";\nimport type { LoopEvent } from \"../loop.js\";\nimport type { RawUsage } from \"../types.js\";\n\nexport interface TranscriptRecord {\n /** ISO-8601 timestamp at emit time. */\n ts: string;\n /** 1-based turn number within the session. */\n turn: number;\n /** LoopEvent role — \"assistant_delta\" | \"assistant_final\" | \"tool\" | \"done\" | ... */\n role: string;\n /** For assistant events, the final (or delta) text; for tool events, the tool result. */\n content: string;\n /** Tool name (role === \"tool\"). */\n tool?: string;\n /** JSON-string args the model sent for a tool call (role === \"tool\"). Persisted so diff can explain *why* two runs made different calls. */\n args?: string;\n /** DeepSeek token-usage snapshot (role === \"assistant_final\"). */\n usage?: RawUsage;\n /** USD cost of this turn (role === \"assistant_final\"). */\n cost?: number;\n /** Model id that produced this turn. */\n model?: string;\n /** Lets diff attribute cache-hit delta to log stability vs prompt change. */\n prefixHash?: string;\n /** Optional error message (role === \"error\"). */\n error?: string;\n}\n\nexport interface TranscriptMeta {\n version: 1;\n source: string; // e.g. \"luckerr chat\", \"bench/baseline\", \"bench/luckerr\"\n model?: string;\n task?: string;\n mode?: string;\n repeat?: number;\n startedAt: string;\n}\n\ninterface MetaLine {\n role: \"_meta\";\n meta: TranscriptMeta;\n}\n\nexport interface ReadTranscriptResult {\n meta: TranscriptMeta | null;\n records: TranscriptRecord[];\n}\n\nexport function recordFromLoopEvent(\n ev: LoopEvent,\n extra: { model: string; prefixHash: string },\n): TranscriptRecord {\n const rec: TranscriptRecord = {\n ts: new Date().toISOString(),\n turn: ev.turn,\n role: ev.role,\n content: ev.content,\n };\n if (ev.toolName !== undefined) rec.tool = ev.toolName;\n if (ev.toolArgs !== undefined) rec.args = ev.toolArgs;\n if (ev.error !== undefined) rec.error = ev.error;\n if (ev.stats) {\n rec.usage = {\n prompt_tokens: ev.stats.usage.promptTokens,\n completion_tokens: ev.stats.usage.completionTokens,\n total_tokens: ev.stats.usage.totalTokens,\n prompt_cache_hit_tokens: ev.stats.usage.promptCacheHitTokens,\n prompt_cache_miss_tokens: ev.stats.usage.promptCacheMissTokens,\n };\n rec.cost = ev.stats.cost;\n rec.model = ev.stats.model;\n rec.prefixHash = extra.prefixHash;\n } else if (ev.role === \"assistant_final\") {\n // assistant_final without stats (shouldn't happen in the live loop but\n // might in test fixtures) — still persist model + prefix for continuity.\n rec.model = extra.model;\n rec.prefixHash = extra.prefixHash;\n }\n return rec;\n}\n\n/**\n * Append a record to an open write stream. Caller owns the stream lifecycle.\n */\nexport function writeRecord(stream: WriteStream, record: TranscriptRecord): void {\n stream.write(`${JSON.stringify(record)}\\n`);\n}\n\n/**\n * Write a _meta line to an open write stream. Call exactly once, at the top.\n */\nexport function writeMeta(stream: WriteStream, meta: TranscriptMeta): void {\n const line: MetaLine = { role: \"_meta\", meta };\n stream.write(`${JSON.stringify(line)}\\n`);\n}\n\n/**\n * Convenience: open a stream, write meta, return stream.\n */\nexport function openTranscriptFile(path: string, meta: TranscriptMeta): WriteStream {\n const stream = createWriteStream(path, { flags: \"a\" });\n writeMeta(stream, meta);\n return stream;\n}\n\n/** Tolerant: empty / malformed lines skipped, missing optionals OK — live chats may be mid-write. */\nexport function readTranscript(path: string): ReadTranscriptResult {\n const raw = readFileSync(path, \"utf8\");\n return parseTranscript(raw);\n}\n\nexport function parseTranscript(raw: string): ReadTranscriptResult {\n const out: ReadTranscriptResult = { meta: null, records: [] };\n for (const line of raw.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n let obj: unknown;\n try {\n obj = JSON.parse(trimmed);\n } catch {\n continue;\n }\n if (!obj || typeof obj !== \"object\") continue;\n const rec = obj as Record<string, unknown>;\n if (rec.role === \"_meta\" && rec.meta && typeof rec.meta === \"object\") {\n out.meta = rec.meta as TranscriptMeta;\n continue;\n }\n if (\n typeof rec.ts === \"string\" &&\n typeof rec.turn === \"number\" &&\n typeof rec.role === \"string\" &&\n typeof rec.content === \"string\"\n ) {\n out.records.push(rec as unknown as TranscriptRecord);\n }\n }\n return out;\n}\n","/** Reconstruct session economics from a transcript alone — offline audit, no API key. */\n\nimport { Usage } from \"../client.js\";\nimport {\n type SessionSummary,\n type TurnStats,\n claudeEquivalentCost,\n costUsd,\n inputCostUsd,\n outputCostUsd,\n} from \"../telemetry/stats.js\";\nimport { type ReadTranscriptResult, type TranscriptRecord, readTranscript } from \"./log.js\";\n\nexport interface TurnPage {\n turn: number;\n records: TranscriptRecord[];\n}\n\nexport function groupRecordsByTurn(records: TranscriptRecord[]): TurnPage[] {\n const byTurn = new Map<number, TranscriptRecord[]>();\n for (const rec of records) {\n const list = byTurn.get(rec.turn);\n if (list) list.push(rec);\n else byTurn.set(rec.turn, [rec]);\n }\n return [...byTurn.entries()]\n .sort(([a], [b]) => a - b)\n .map(([turn, records]) => ({ turn, records }));\n}\n\nexport function computeCumulativeStats(pages: TurnPage[], upToIdx: number): ReplayStats {\n if (upToIdx < 0) return computeReplayStats([]);\n const flat: TranscriptRecord[] = [];\n for (let i = 0; i <= upToIdx && i < pages.length; i++) {\n const records = pages[i]?.records;\n if (records) flat.push(...records);\n }\n return computeReplayStats(flat);\n}\n\nexport interface ReplayStats extends SessionSummary {\n /** Per-turn stats, in turn order. Only assistant_final records contribute. */\n perTurn: TurnStats[];\n /** Unique models that appeared in the transcript's assistant_final records. */\n models: string[];\n /** Unique prefix hashes that appeared. Length > 1 means the prefix churned (cache-hostile). */\n prefixHashes: string[];\n /** Count of user-role records (user turns issued). */\n userTurns: number;\n /** Count of tool-role records (tool calls executed). */\n toolCalls: number;\n}\n\nexport function replayFromFile(path: string): { parsed: ReadTranscriptResult; stats: ReplayStats } {\n const parsed = readTranscript(path);\n return { parsed, stats: computeReplayStats(parsed.records) };\n}\n\nexport function computeReplayStats(records: TranscriptRecord[]): ReplayStats {\n const turns: TurnStats[] = [];\n const models = new Set<string>();\n const prefixHashes = new Set<string>();\n let userTurns = 0;\n let toolCalls = 0;\n\n for (const rec of records) {\n if (rec.role === \"user\") userTurns++;\n else if (rec.role === \"tool\") toolCalls++;\n else if (rec.role === \"assistant_final\") {\n if (rec.model) models.add(rec.model);\n if (rec.prefixHash) prefixHashes.add(rec.prefixHash);\n if (rec.usage && rec.model) {\n const u = new Usage(\n rec.usage.prompt_tokens ?? 0,\n rec.usage.completion_tokens ?? 0,\n rec.usage.total_tokens ?? 0,\n rec.usage.prompt_cache_hit_tokens ?? 0,\n rec.usage.prompt_cache_miss_tokens ?? 0,\n );\n turns.push({\n turn: rec.turn,\n model: rec.model,\n usage: u,\n // `rec.cost` wins when present — honors whatever the writer computed\n // even if pricing tables have since changed. Only recompute when\n // the transcript didn't record it (old format).\n cost: rec.cost ?? costUsd(rec.model, u),\n cacheHitRatio: u.cacheHitRatio,\n });\n }\n }\n }\n\n return {\n perTurn: turns,\n models: [...models],\n prefixHashes: [...prefixHashes],\n userTurns,\n toolCalls,\n ...summarizeTurns(turns),\n };\n}\n\nfunction summarizeTurns(turns: TurnStats[]): SessionSummary {\n const totalCost = turns.reduce((s, t) => s + t.cost, 0);\n const totalInput = turns.reduce((s, t) => s + inputCostUsd(t.model, t.usage), 0);\n const totalOutput = turns.reduce((s, t) => s + outputCostUsd(t.model, t.usage), 0);\n const totalClaude = turns.reduce((s, t) => s + claudeEquivalentCost(t.usage), 0);\n let hit = 0;\n let miss = 0;\n for (const t of turns) {\n hit += t.usage.promptCacheHitTokens;\n miss += t.usage.promptCacheMissTokens;\n }\n const cacheHitRatio = hit + miss > 0 ? hit / (hit + miss) : 0;\n const savingsVsClaude = totalClaude > 0 ? 1 - totalCost / totalClaude : 0;\n const lastTurn = turns[turns.length - 1];\n return {\n turns: turns.length,\n totalCostUsd: round(totalCost, 6),\n totalInputCostUsd: round(totalInput, 6),\n totalOutputCostUsd: round(totalOutput, 6),\n claudeEquivalentUsd: round(totalClaude, 6),\n savingsVsClaudePct: round(savingsVsClaude * 100, 2),\n cacheHitRatio: round(cacheHitRatio, 4),\n lastPromptTokens: lastTurn?.usage.promptTokens ?? 0,\n lastTurnCostUsd: round(lastTurn?.cost ?? 0, 6),\n };\n}\n\nfunction round(n: number, digits: number): number {\n const f = 10 ** digits;\n return Math.round(n * f) / f;\n}\n","/** Transcript diff — pairs assistant_final by turn number; unmatched extras become only_in_a / only_in_b. */\n\nimport type { ReadTranscriptResult, TranscriptRecord } from \"./log.js\";\nimport { type ReplayStats, computeReplayStats } from \"./replay.js\";\n\nexport interface DiffSide {\n label: string;\n meta: ReadTranscriptResult[\"meta\"];\n records: TranscriptRecord[];\n stats: ReplayStats;\n}\n\nexport interface TurnPair {\n turn: number;\n aAssistant?: TranscriptRecord;\n bAssistant?: TranscriptRecord;\n aTools: TranscriptRecord[];\n bTools: TranscriptRecord[];\n kind: \"match\" | \"diverge\" | \"only_in_a\" | \"only_in_b\";\n /** When kind === \"diverge\", a short one-liner pointing at what differs. */\n divergenceNote?: string;\n}\n\nexport interface DiffReport {\n a: DiffSide;\n b: DiffSide;\n pairs: TurnPair[];\n firstDivergenceTurn: number | null;\n}\n\nexport function findNextDivergence(pairs: TurnPair[], fromIdx: number): number {\n for (let i = fromIdx + 1; i < pairs.length; i++) {\n if (pairs[i]!.kind !== \"match\") return i;\n }\n return -1;\n}\n\nexport function findPrevDivergence(pairs: TurnPair[], fromIdx: number): number {\n const start = Math.min(fromIdx - 1, pairs.length - 1);\n for (let i = start; i >= 0; i--) {\n if (pairs[i]!.kind !== \"match\") return i;\n }\n return -1;\n}\n\nexport function diffTranscripts(\n a: { label: string; parsed: ReadTranscriptResult },\n b: { label: string; parsed: ReadTranscriptResult },\n): DiffReport {\n const aSide: DiffSide = {\n label: a.label,\n meta: a.parsed.meta,\n records: a.parsed.records,\n stats: computeReplayStats(a.parsed.records),\n };\n const bSide: DiffSide = {\n label: b.label,\n meta: b.parsed.meta,\n records: b.parsed.records,\n stats: computeReplayStats(b.parsed.records),\n };\n\n const aByTurn = groupByTurn(a.parsed.records);\n const bByTurn = groupByTurn(b.parsed.records);\n const turns = [...new Set([...aByTurn.keys(), ...bByTurn.keys()])].sort((x, y) => x - y);\n\n const pairs: TurnPair[] = [];\n let firstDivergenceTurn: number | null = null;\n for (const turn of turns) {\n const aGroup = aByTurn.get(turn) ?? { assistant: undefined, tools: [] };\n const bGroup = bByTurn.get(turn) ?? { assistant: undefined, tools: [] };\n const aAssistant = aGroup.assistant;\n const bAssistant = bGroup.assistant;\n const aTools = aGroup.tools;\n const bTools = bGroup.tools;\n\n let kind: TurnPair[\"kind\"];\n let divergenceNote: string | undefined;\n if (!aAssistant && bAssistant) kind = \"only_in_b\";\n else if (aAssistant && !bAssistant) kind = \"only_in_a\";\n else if (!aAssistant && !bAssistant)\n kind = \"diverge\"; // tool-only turn (rare)\n else {\n divergenceNote = classifyDivergence(aAssistant!, bAssistant!, aTools, bTools);\n kind = divergenceNote ? \"diverge\" : \"match\";\n }\n\n if (kind !== \"match\" && firstDivergenceTurn === null) firstDivergenceTurn = turn;\n pairs.push({ turn, aAssistant, bAssistant, aTools, bTools, kind, divergenceNote });\n }\n\n return { a: aSide, b: bSide, pairs, firstDivergenceTurn };\n}\n\nfunction classifyDivergence(\n a: TranscriptRecord,\n b: TranscriptRecord,\n aTools: TranscriptRecord[],\n bTools: TranscriptRecord[],\n): string | undefined {\n const aNames = aTools.map((t) => t.tool ?? \"\").sort();\n const bNames = bTools.map((t) => t.tool ?? \"\").sort();\n if (aNames.join(\",\") !== bNames.join(\",\")) {\n return `tool calls differ: A=[${aNames.join(\",\") || \"—\"}] B=[${bNames.join(\",\") || \"—\"}]`;\n }\n // Same tool names — did they pass different args?\n for (let i = 0; i < aTools.length; i++) {\n const at = aTools[i]!;\n const bt = bTools[i]!;\n if (at.tool !== bt.tool) continue;\n if ((at.args ?? \"\") !== (bt.args ?? \"\")) {\n return `\"${at.tool}\" args differ`;\n }\n }\n const simRatio = similarity(a.content, b.content);\n if (simRatio < 0.75) return `text similarity ${(simRatio * 100).toFixed(0)}%`;\n return undefined;\n}\n\n/** Falls back to token-overlap above 2000 chars to keep diff fast on chatty transcripts. */\nexport function similarity(a: string, b: string): number {\n if (a === b) return 1;\n if (!a && !b) return 1;\n if (!a || !b) return 0;\n const maxLen = Math.max(a.length, b.length);\n if (maxLen > 2000) return tokenOverlap(a, b);\n const dist = levenshtein(a, b);\n return 1 - dist / maxLen;\n}\n\nfunction tokenOverlap(a: string, b: string): number {\n const ta = new Set(a.toLowerCase().split(/\\s+/).filter(Boolean));\n const tb = new Set(b.toLowerCase().split(/\\s+/).filter(Boolean));\n if (ta.size === 0 && tb.size === 0) return 1;\n let shared = 0;\n for (const t of ta) if (tb.has(t)) shared++;\n return (2 * shared) / (ta.size + tb.size);\n}\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = new Array(n + 1);\n let curr = new Array(n + 1);\n for (let j = 0; j <= n; j++) prev[j] = j;\n for (let i = 1; i <= m; i++) {\n curr[0] = i;\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n [prev, curr] = [curr, prev];\n }\n return prev[n];\n}\n\ninterface TurnGroup {\n assistant?: TranscriptRecord;\n tools: TranscriptRecord[];\n}\n\nfunction groupByTurn(records: TranscriptRecord[]): Map<number, TurnGroup> {\n const out = new Map<number, TurnGroup>();\n for (const rec of records) {\n if (rec.role === \"user\") continue; // user msg is input to the turn, not its output\n const g = out.get(rec.turn) ?? { tools: [] };\n if (rec.role === \"assistant_final\") g.assistant = rec;\n else if (rec.role === \"tool\") g.tools.push(rec);\n out.set(rec.turn, g);\n }\n return out;\n}\n\nexport interface RenderOptions {\n /** Monochrome output (for file redirection or piping). Defaults to true. */\n monochrome?: boolean;\n}\n\nexport function renderSummaryTable(report: DiffReport, _opts: RenderOptions = {}): string {\n const a = report.a;\n const b = report.b;\n const lines: string[] = [];\n lines.push(\"Comparing:\");\n lines.push(` A ${a.label}`);\n lines.push(` B ${b.label}`);\n lines.push(\"\");\n lines.push(row([\"\", \"A\", \"B\", \"Δ\"], [20, 14, 14, 14]));\n lines.push(\n row([\"─\".repeat(20), \"─\".repeat(14), \"─\".repeat(14), \"─\".repeat(14)], [20, 14, 14, 14]),\n );\n lines.push(statRow(\"model calls\", a.stats.turns, b.stats.turns));\n lines.push(statRow(\"user turns\", a.stats.userTurns, b.stats.userTurns));\n lines.push(statRow(\"tool calls\", a.stats.toolCalls, b.stats.toolCalls));\n lines.push(\n row(\n [\n \"cache hit\",\n `${pct(a.stats.cacheHitRatio)}`,\n `${pct(b.stats.cacheHitRatio)}`,\n signPct(b.stats.cacheHitRatio - a.stats.cacheHitRatio),\n ],\n [20, 14, 14, 14],\n ),\n );\n lines.push(\n row(\n [\n \"cost (USD)\",\n `$${a.stats.totalCostUsd.toFixed(6)}`,\n `$${b.stats.totalCostUsd.toFixed(6)}`,\n costDelta(a.stats.totalCostUsd, b.stats.totalCostUsd),\n ],\n [20, 14, 14, 14],\n ),\n );\n lines.push(statRow(\"prefix hashes\", a.stats.prefixHashes.length, b.stats.prefixHashes.length));\n lines.push(\"\");\n\n // Prefix stability story — the headline finding when comparing bench modes.\n const aPrefixStable = a.stats.prefixHashes.length <= 1;\n const bPrefixStable = b.stats.prefixHashes.length <= 1;\n if (aPrefixStable !== bPrefixStable) {\n const stable = aPrefixStable ? \"A\" : \"B\";\n const churn = aPrefixStable ? \"B\" : \"A\";\n const churnCount = aPrefixStable ? b.stats.prefixHashes.length : a.stats.prefixHashes.length;\n lines.push(\n `prefix stability: ${stable} stayed byte-stable across ${Math.max(\n a.stats.turns,\n b.stats.turns,\n )} turns; ${churn} churned ${churnCount} distinct prefixes.`,\n );\n lines.push(\"\");\n } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {\n lines.push(\n `prefix: A and B share the same prefix hash (${a.stats.prefixHashes[0].slice(0, 12)}…) — cache delta is attributable to log stability, not prompt change.`,\n );\n lines.push(\"\");\n }\n\n if (report.firstDivergenceTurn !== null) {\n const p = report.pairs.find((p) => p.turn === report.firstDivergenceTurn);\n lines.push(\n `first divergence: turn ${report.firstDivergenceTurn} — ${p?.divergenceNote ?? \"?\"}`,\n );\n if (p?.aAssistant) lines.push(` A → ${truncate(p.aAssistant.content, 100)}`);\n if (p?.bAssistant) lines.push(` B → ${truncate(p.bAssistant.content, 100)}`);\n } else {\n lines.push(\"no material divergence detected (texts within similarity threshold).\");\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function renderMarkdown(report: DiffReport): string {\n const a = report.a;\n const b = report.b;\n const out: string[] = [];\n out.push(`# Transcript diff: ${a.label} vs ${b.label}`);\n out.push(\"\");\n if (a.meta || b.meta) {\n out.push(\"## Meta\");\n out.push(\"\");\n out.push(`| | ${a.label} | ${b.label} |`);\n out.push(\"|---|---|---|\");\n out.push(`| source | ${a.meta?.source ?? \"—\"} | ${b.meta?.source ?? \"—\"} |`);\n out.push(`| model | ${a.meta?.model ?? \"—\"} | ${b.meta?.model ?? \"—\"} |`);\n out.push(`| task | ${a.meta?.task ?? \"—\"} | ${b.meta?.task ?? \"—\"} |`);\n out.push(`| startedAt | ${a.meta?.startedAt ?? \"—\"} | ${b.meta?.startedAt ?? \"—\"} |`);\n out.push(\"\");\n }\n\n out.push(\"## Summary\");\n out.push(\"\");\n out.push(`| metric | ${a.label} | ${b.label} | delta |`);\n out.push(\"|---|---:|---:|---:|\");\n out.push(\n `| model calls | ${a.stats.turns} | ${b.stats.turns} | ${signed(b.stats.turns - a.stats.turns)} |`,\n );\n out.push(\n `| user turns | ${a.stats.userTurns} | ${b.stats.userTurns} | ${signed(b.stats.userTurns - a.stats.userTurns)} |`,\n );\n out.push(\n `| tool calls | ${a.stats.toolCalls} | ${b.stats.toolCalls} | ${signed(b.stats.toolCalls - a.stats.toolCalls)} |`,\n );\n out.push(\n `| cache hit | ${pct(a.stats.cacheHitRatio)} | ${pct(b.stats.cacheHitRatio)} | **${signPct(b.stats.cacheHitRatio - a.stats.cacheHitRatio)}** |`,\n );\n out.push(\n `| cost (USD) | $${a.stats.totalCostUsd.toFixed(6)} | $${b.stats.totalCostUsd.toFixed(6)} | ${costDelta(a.stats.totalCostUsd, b.stats.totalCostUsd)} |`,\n );\n out.push(\n `| prefix hashes | ${a.stats.prefixHashes.length} | ${b.stats.prefixHashes.length} | — |`,\n );\n out.push(\"\");\n\n out.push(\"## Turn-by-turn\");\n out.push(\"\");\n out.push(`| turn | kind | ${a.label} tool calls | ${b.label} tool calls | note |`);\n out.push(\"|---:|:---:|---|---|---|\");\n for (const p of report.pairs) {\n const aTools =\n p.aTools\n .map((t) => t.tool)\n .filter(Boolean)\n .join(\", \") || \"—\";\n const bTools =\n p.bTools\n .map((t) => t.tool)\n .filter(Boolean)\n .join(\", \") || \"—\";\n out.push(`| ${p.turn} | ${p.kind} | ${aTools} | ${bTools} | ${p.divergenceNote ?? \"\"} |`);\n }\n out.push(\"\");\n\n if (report.firstDivergenceTurn !== null) {\n const p = report.pairs.find((x) => x.turn === report.firstDivergenceTurn);\n out.push(`## First divergence (turn ${report.firstDivergenceTurn})`);\n out.push(\"\");\n out.push(p?.divergenceNote ?? \"\");\n out.push(\"\");\n if (p?.aAssistant) {\n out.push(`**${a.label}:**`);\n out.push(\"\");\n out.push(\"```\");\n out.push(p.aAssistant.content);\n out.push(\"```\");\n out.push(\"\");\n }\n if (p?.bAssistant) {\n out.push(`**${b.label}:**`);\n out.push(\"\");\n out.push(\"```\");\n out.push(p.bAssistant.content);\n out.push(\"```\");\n out.push(\"\");\n }\n }\n return out.join(\"\\n\");\n}\n\nfunction row(cols: string[], widths: number[]): string {\n return cols.map((c, i) => padRight(c, widths[i] ?? c.length)).join(\" \");\n}\n\nfunction statRow(label: string, av: number, bv: number): string {\n return row([label, `${av}`, `${bv}`, signed(bv - av)], [20, 14, 14, 14]);\n}\n\nfunction padRight(s: string, w: number): string {\n return s.length >= w ? s : s + \" \".repeat(w - s.length);\n}\n\nfunction signed(n: number): string {\n if (n === 0) return \"0\";\n return `${n > 0 ? \"+\" : \"\"}${n}`;\n}\n\nfunction signPct(diff: number): string {\n if (diff === 0) return \"0pp\";\n const s = (diff * 100).toFixed(1);\n return `${diff > 0 ? \"+\" : \"\"}${s}pp`;\n}\n\nfunction pct(x: number): string {\n return `${(x * 100).toFixed(1)}%`;\n}\n\nfunction costDelta(a: number, b: number): string {\n if (a === 0 && b === 0) return \"—\";\n if (a === 0) return \"new\";\n const pctChange = ((b - a) / a) * 100;\n return `${pctChange > 0 ? \"+\" : \"\"}${pctChange.toFixed(1)}%`;\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length > n ? `${s.slice(0, n)}…` : s;\n}\n","/** VERSION sourced from package.json so it never drifts from npm; latest-check returns null on any failure. */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** npm registry endpoint for the `latest` dist-tag of this package. */\nconst REGISTRY_URL = \"https://registry.npmjs.org/luckerr/latest\";\n\n/** TTL for the on-disk cache entry. 24h keeps noise low; users who\n * want a fresh check can run `luckerr update` which passes\n * `force: true`. */\nexport const LATEST_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\n\n/** Network timeout. Short — we never block the UI waiting on this. */\nexport const LATEST_FETCH_TIMEOUT_MS = 2_000;\n\n/** `name === \"luckerr\"` guard avoids picking up an outer package.json when loaded as a dep. */\nfunction readPackageVersion(): string {\n try {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 6; i++) {\n const p = join(dir, \"package.json\");\n if (existsSync(p)) {\n const pkg = JSON.parse(readFileSync(p, \"utf8\"));\n if (pkg?.name === \"luckerr\" && typeof pkg.version === \"string\") {\n return pkg.version;\n }\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n /* fall through to fallback */\n }\n return \"0.0.0-dev\";\n}\n\nexport const VERSION: string = readPackageVersion();\n\ninterface VersionCacheEntry {\n version: string;\n /** Epoch millis the entry was written. Drives TTL comparisons. */\n checkedAt: number;\n}\n\nfunction cachePath(homeDirOverride?: string): string {\n return join(homeDirOverride ?? homedir(), \".luckerr\", \"version-cache.json\");\n}\n\nfunction readCache(homeDirOverride?: string): VersionCacheEntry | null {\n try {\n const raw = readFileSync(cachePath(homeDirOverride), \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed.version === \"string\" && typeof parsed.checkedAt === \"number\") {\n return parsed;\n }\n } catch {\n /* missing or malformed → no cached entry */\n }\n return null;\n}\n\nfunction writeCache(entry: VersionCacheEntry, homeDirOverride?: string): void {\n try {\n const p = cachePath(homeDirOverride);\n mkdirSync(dirname(p), { recursive: true });\n writeFileSync(p, JSON.stringify(entry), \"utf8\");\n } catch {\n /* cache is best-effort — a failed write just means we'll re-fetch\n * next launch. No reason to surface this to the user. */\n }\n}\n\nexport interface GetLatestVersionOptions {\n /** Ignore the cached entry and always fetch fresh. Used by `luckerr update`. */\n force?: boolean;\n /** Registry URL override (tests). */\n registryUrl?: string;\n /** Home-directory override (tests). */\n homeDir?: string;\n /** Fetch implementation override (tests). Defaults to `globalThis.fetch`. */\n fetchImpl?: typeof fetch;\n /** TTL override (tests). */\n ttlMs?: number;\n /** Network timeout override (tests). */\n timeoutMs?: number;\n}\n\n/** Returns null on failure; cache only writes on success so bad responses can't poison it. */\nexport async function getLatestVersion(opts: GetLatestVersionOptions = {}): Promise<string | null> {\n const ttl = opts.ttlMs ?? LATEST_CACHE_TTL_MS;\n if (!opts.force) {\n const cached = readCache(opts.homeDir);\n if (cached && Date.now() - cached.checkedAt < ttl) return cached.version;\n }\n\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n if (!fetchImpl) return null;\n const url = opts.registryUrl ?? REGISTRY_URL;\n const timeout = opts.timeoutMs ?? LATEST_FETCH_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const res = await fetchImpl(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n });\n if (!res.ok) return null;\n const body = (await res.json()) as { version?: unknown };\n if (typeof body.version !== \"string\") return null;\n writeCache({ version: body.version, checkedAt: Date.now() }, opts.homeDir);\n return body.version;\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Pre-release with same core sorts BELOW the bare version — matches npm `latest` dist-tag semantics. */\nexport function compareVersions(a: string, b: string): number {\n const [aCore = \"0\", aPre = \"\"] = a.split(\"-\", 2);\n const [bCore = \"0\", bPre = \"\"] = b.split(\"-\", 2);\n const aParts = aCore.split(\".\").map((p) => Number.parseInt(p, 10) || 0);\n const bParts = bCore.split(\".\").map((p) => Number.parseInt(p, 10) || 0);\n for (let i = 0; i < 3; i++) {\n const diff = (aParts[i] ?? 0) - (bParts[i] ?? 0);\n if (diff !== 0) return diff;\n }\n if (!aPre && !bPre) return 0;\n if (!aPre) return 1;\n if (!bPre) return -1;\n return aPre < bPre ? -1 : aPre > bPre ? 1 : 0;\n}\n\nexport type InstallSource = \"npm\" | \"bun\" | \"pnpm\" | \"yarn\" | \"npx\" | \"unknown\";\n\n/** Each manager owns a unique global path segment, so argv[1] tells us who installed us. */\nexport function detectInstallSource(bin?: string): InstallSource {\n const raw = bin ?? process.argv[1] ?? \"\";\n if (!raw) return \"unknown\";\n const norm = raw.replace(/\\\\/g, \"/\").toLowerCase();\n if (/\\/_npx\\//.test(norm)) return \"npx\";\n if (/\\/\\.pnpm\\//.test(norm) && /dlx/i.test(norm)) return \"npx\";\n const ua = (process.env.npm_config_user_agent ?? \"\").toLowerCase();\n if (ua.includes(\"npx/\")) return \"npx\";\n if (/\\/\\.bun\\//.test(norm) || /\\/bun\\/install\\//.test(norm)) return \"bun\";\n if (/\\/pnpm\\/global\\//.test(norm) || /\\/pnpm\\/[^/]+\\/node_modules\\//.test(norm)) return \"pnpm\";\n if (/\\/yarn\\/global\\//.test(norm) || /\\/\\.yarn\\/global\\//.test(norm)) return \"yarn\";\n if (/\\/node_modules\\/luckerr(\\b|\\/)/.test(norm)) return \"npm\";\n return \"unknown\";\n}\n\n/** Returns null when no path is given. Callers must check installSource first. */\nexport function isNpxInstall(): boolean {\n return detectInstallSource() === \"npx\";\n}\n\n/** Pin npm to the install location via --prefix so `nvm use` doesn't redirect the install elsewhere. */\nexport function detectNpmInstallPrefix(bin?: string): string | null {\n const raw = bin ?? process.argv[1] ?? \"\";\n if (!raw) return null;\n const norm = raw.replace(/\\\\/g, \"/\");\n const posix = norm.match(/^(.+?)\\/lib\\/node_modules\\/luckerr(?:\\/|$)/i);\n if (posix) return posix[1] ?? null;\n const win = norm.match(/^(.+?)\\/node_modules\\/luckerr(?:\\/|$)/i);\n if (win) return win[1] ?? null;\n return null;\n}\n","/** MCP types (spec 2024-11-05). Stdio wire format is NDJSON — one JSON-RPC message per line, no Content-Length framing. */\n\nexport type JsonRpcId = string | number;\n\nexport interface JsonRpcRequest<P = unknown> {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n method: string;\n params?: P;\n}\n\nexport interface JsonRpcNotification<P = unknown> {\n jsonrpc: \"2.0\";\n method: string;\n params?: P;\n}\n\nexport interface JsonRpcSuccess<R = unknown> {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n result: R;\n}\n\nexport interface JsonRpcError {\n jsonrpc: \"2.0\";\n id: JsonRpcId | null;\n error: {\n /** JSON-RPC standard codes: -32700 parse, -32600 invalid request, -32601 method not found, -32602 invalid params, -32603 internal. MCP also defines its own range. */\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\nexport type JsonRpcResponse<R = unknown> = JsonRpcSuccess<R> | JsonRpcError;\n\nexport type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;\n\nexport interface McpClientInfo {\n name: string;\n version: string;\n}\n\nexport interface McpClientCapabilities {\n /** Empty object advertises support without any optional sub-features. */\n tools?: Record<string, never>;\n /** Advertised when the client can consume `resources/list` + `resources/read`. */\n resources?: Record<string, never>;\n /** Advertised when the client can consume `prompts/list` + `prompts/get`. */\n prompts?: Record<string, never>;\n // sampling would go here — deferred.\n}\n\nexport interface InitializeParams {\n protocolVersion: string;\n capabilities: McpClientCapabilities;\n clientInfo: McpClientInfo;\n}\n\nexport interface InitializeResult {\n protocolVersion: string;\n serverInfo: { name: string; version: string };\n capabilities: {\n tools?: { listChanged?: boolean };\n resources?: unknown;\n prompts?: unknown;\n };\n instructions?: string;\n}\n\nexport interface McpToolSchema {\n /** JSON Schema — compatible with Luckerr's tools.ts JSONSchema shape. */\n type?: string;\n properties?: Record<string, unknown>;\n required?: string[];\n [extra: string]: unknown;\n}\n\nexport interface McpTool {\n name: string;\n description?: string;\n /** MCP calls this `inputSchema`. Luckerr's `parameters` field is the same concept. */\n inputSchema: McpToolSchema;\n}\n\nexport interface ListToolsResult {\n tools: McpTool[];\n nextCursor?: string;\n}\n\nexport interface CallToolParams {\n name: string;\n arguments?: Record<string, unknown>;\n _meta?: { progressToken?: string | number };\n}\n\nexport interface ProgressNotificationParams {\n progressToken: string | number;\n progress: number;\n total?: number;\n message?: string;\n}\n\n/** Values a `ProgressHandler` receives — `progressToken` is already matched away. */\nexport interface McpProgressInfo {\n progress: number;\n total?: number;\n message?: string;\n}\n\nexport type McpProgressHandler = (info: McpProgressInfo) => void;\n\nexport interface McpContentBlockText {\n type: \"text\";\n text: string;\n}\n\nexport interface McpContentBlockImage {\n type: \"image\";\n data: string;\n mimeType: string;\n}\n\n/** MCP result content is an array of typed blocks. Luckerr consumes only text for now — image blocks get stringified with a placeholder. */\nexport type McpContentBlock = McpContentBlockText | McpContentBlockImage;\n\nexport interface CallToolResult {\n content: McpContentBlock[];\n /** True = tool raised an error; the content describes it. */\n isError?: boolean;\n}\n\nexport interface McpResource {\n uri: string;\n name: string;\n description?: string;\n /** Hint for the content type (e.g. \"text/markdown\"). Purely informational. */\n mimeType?: string;\n}\n\nexport interface ListResourcesParams {\n /** Pagination cursor from a previous listResources response. */\n cursor?: string;\n}\n\nexport interface ListResourcesResult {\n resources: McpResource[];\n nextCursor?: string;\n}\n\nexport interface ReadResourceParams {\n uri: string;\n}\n\n/** Server populates exactly one of `text` (UTF-8) or `blob` (base64) per entry. */\nexport interface McpResourceContentsText {\n uri: string;\n mimeType?: string;\n text: string;\n}\n\nexport interface McpResourceContentsBlob {\n uri: string;\n mimeType?: string;\n blob: string;\n}\n\nexport type McpResourceContents = McpResourceContentsText | McpResourceContentsBlob;\n\nexport interface ReadResourceResult {\n contents: McpResourceContents[];\n}\n\nexport interface McpPromptArgument {\n name: string;\n description?: string;\n required?: boolean;\n}\n\nexport interface McpPrompt {\n name: string;\n description?: string;\n arguments?: McpPromptArgument[];\n}\n\nexport interface ListPromptsParams {\n cursor?: string;\n}\n\nexport interface ListPromptsResult {\n prompts: McpPrompt[];\n nextCursor?: string;\n}\n\nexport interface GetPromptParams {\n name: string;\n arguments?: Record<string, string>;\n}\n\nexport interface McpPromptMessage {\n role: \"user\" | \"assistant\";\n content: McpContentBlock | McpPromptResourceBlock;\n}\n\nexport interface McpPromptResourceBlock {\n type: \"resource\";\n resource: McpResourceContents;\n}\n\nexport interface GetPromptResult {\n description?: string;\n messages: McpPromptMessage[];\n}\n\n/** Current MCP protocol version Luckerr is coded against. */\nexport const MCP_PROTOCOL_VERSION = \"2024-11-05\";\n\n/** Type guard — success vs error response. */\nexport function isJsonRpcError(msg: JsonRpcResponse): msg is JsonRpcError {\n return \"error\" in msg;\n}\n","import { VERSION } from \"../version.js\";\nimport type { McpTransport } from \"./stdio.js\";\nimport {\n type CallToolParams,\n type CallToolResult,\n type GetPromptParams,\n type GetPromptResult,\n type InitializeParams,\n type InitializeResult,\n type JsonRpcId,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcResponse,\n type ListPromptsParams,\n type ListPromptsResult,\n type ListResourcesParams,\n type ListResourcesResult,\n type ListToolsResult,\n MCP_PROTOCOL_VERSION,\n type McpClientInfo,\n type McpProgressHandler,\n type ProgressNotificationParams,\n type ReadResourceParams,\n type ReadResourceResult,\n isJsonRpcError,\n} from \"./types.js\";\n\nexport interface McpClientOptions {\n transport: McpTransport;\n clientInfo?: McpClientInfo;\n /** Per-request timeout. Default 60s. */\n requestTimeoutMs?: number;\n}\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (err: Error) => void;\n timeout: NodeJS.Timeout;\n}\n\nexport class McpClient {\n private readonly transport: McpTransport;\n private readonly clientInfo: McpClientInfo;\n private readonly requestTimeoutMs: number;\n private readonly pending = new Map<JsonRpcId, PendingRequest>();\n private nextId = 1;\n private readerStarted = false;\n private initialized = false;\n private _serverCapabilities: InitializeResult[\"capabilities\"] = {};\n private _serverInfo: InitializeResult[\"serverInfo\"] = { name: \"\", version: \"\" };\n private _protocolVersion = \"\";\n private _instructions: string | undefined;\n // Progress-token → handler for notifications/progress routing. Tokens\n // are minted per call when the caller supplies an onProgress\n // callback; cleared when the final response lands (or the pending\n // request rejects). No leaks — the `try/finally` in callTool\n // guarantees cleanup even on timeout.\n private readonly progressHandlers = new Map<string | number, McpProgressHandler>();\n private nextProgressToken = 1;\n\n constructor(opts: McpClientOptions) {\n this.transport = opts.transport;\n this.clientInfo = opts.clientInfo ?? { name: \"luckerr\", version: VERSION };\n this.requestTimeoutMs = opts.requestTimeoutMs ?? 60_000;\n }\n\n /** Server's advertised capabilities, available after initialize(). */\n get serverCapabilities(): InitializeResult[\"capabilities\"] {\n return this._serverCapabilities;\n }\n\n /** Server's self-reported name + version, available after initialize(). */\n get serverInfo(): InitializeResult[\"serverInfo\"] {\n return this._serverInfo;\n }\n\n /** Protocol version the server agreed to during the handshake. */\n get protocolVersion(): string {\n return this._protocolVersion;\n }\n\n /** Optional free-form instructions the server provides at handshake. */\n get serverInstructions(): string | undefined {\n return this._instructions;\n }\n\n /** Compliant servers reject other methods until this completes. */\n async initialize(): Promise<InitializeResult> {\n if (this.initialized) throw new Error(\"MCP client already initialized\");\n this.startReaderIfNeeded();\n const result = await this.request<InitializeResult>(\"initialize\", {\n protocolVersion: MCP_PROTOCOL_VERSION,\n // Advertise every method the client can consume so servers know\n // they can send listChanged notifications etc. Sub-feature flags\n // (e.g. `resources.subscribe`) are omitted — we don't implement\n // those yet and the empty object means \"method-level support, no\n // sub-features.\"\n capabilities: { tools: {}, resources: {}, prompts: {} },\n clientInfo: this.clientInfo,\n } satisfies InitializeParams);\n this._serverCapabilities = result.capabilities ?? {};\n this._serverInfo = result.serverInfo ?? { name: \"\", version: \"\" };\n this._protocolVersion = result.protocolVersion ?? \"\";\n this._instructions = result.instructions;\n // Per spec: client sends notifications/initialized after receiving the\n // initialize response. Only then is the connection live for other\n // methods.\n await this.transport.send({\n jsonrpc: \"2.0\",\n method: \"notifications/initialized\",\n });\n this.initialized = true;\n return result;\n }\n\n /** List tools the server exposes. */\n async listTools(): Promise<ListToolsResult> {\n this.assertInitialized();\n return this.request<ListToolsResult>(\"tools/list\", {});\n }\n\n /** Abort sends `notifications/cancelled` and rejects immediately; late server responses are dropped. */\n async callTool(\n name: string,\n args?: Record<string, unknown>,\n opts: { onProgress?: McpProgressHandler; signal?: AbortSignal } = {},\n ): Promise<CallToolResult> {\n this.assertInitialized();\n const params: CallToolParams = { name, arguments: args ?? {} };\n let token: number | undefined;\n if (opts.onProgress) {\n token = this.nextProgressToken++;\n this.progressHandlers.set(token, opts.onProgress);\n params._meta = { progressToken: token };\n }\n try {\n return await this.request<CallToolResult>(\"tools/call\", params, opts.signal);\n } finally {\n if (token !== undefined) this.progressHandlers.delete(token);\n }\n }\n\n /** Throws on method-not-found; callers should gate on `serverCapabilities.resources` first. */\n async listResources(cursor?: string): Promise<ListResourcesResult> {\n this.assertInitialized();\n return this.request<ListResourcesResult>(\"resources/list\", {\n ...(cursor ? { cursor } : {}),\n } satisfies ListResourcesParams);\n }\n\n /** Read the contents of a resource by URI. */\n async readResource(uri: string): Promise<ReadResourceResult> {\n this.assertInitialized();\n return this.request<ReadResourceResult>(\"resources/read\", {\n uri,\n } satisfies ReadResourceParams);\n }\n\n /** List prompt templates the server exposes. */\n async listPrompts(cursor?: string): Promise<ListPromptsResult> {\n this.assertInitialized();\n return this.request<ListPromptsResult>(\"prompts/list\", {\n ...(cursor ? { cursor } : {}),\n } satisfies ListPromptsParams);\n }\n\n async getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult> {\n this.assertInitialized();\n return this.request<GetPromptResult>(\"prompts/get\", {\n name,\n ...(args ? { arguments: args } : {}),\n } satisfies GetPromptParams);\n }\n\n /** Close the transport and reject any outstanding requests. */\n async close(): Promise<void> {\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timeout);\n pending.reject(new Error(\"MCP client closed\"));\n }\n this.pending.clear();\n await this.transport.close();\n }\n\n private assertInitialized(): void {\n if (!this.initialized) throw new Error(\"MCP client not initialized — call initialize() first\");\n }\n\n private async request<R>(method: string, params: unknown, signal?: AbortSignal): Promise<R> {\n const id = this.nextId++;\n const frame: JsonRpcRequest = { jsonrpc: \"2.0\", id, method, params };\n let abortHandler: (() => void) | null = null;\n const promise = new Promise<R>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(id);\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n reject(\n new Error(`MCP request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`),\n );\n }, this.requestTimeoutMs);\n this.pending.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeout,\n });\n // Wire up cancellation: when signal fires, send an MCP cancellation\n // notification to the server (so it can stop whatever it was doing)\n // and reject the caller immediately — no need to wait for the\n // subprocess to finish its in-flight work. Late responses from the\n // server are dropped by `dispatch` because the id is gone from\n // `pending`.\n if (signal) {\n if (signal.aborted) {\n this.pending.delete(id);\n clearTimeout(timeout);\n reject(new Error(`MCP request ${method} (id=${id}) aborted before send`));\n return;\n }\n abortHandler = () => {\n this.pending.delete(id);\n clearTimeout(timeout);\n void this.transport\n .send({\n jsonrpc: \"2.0\",\n method: \"notifications/cancelled\",\n params: { requestId: id, reason: \"aborted by user\" },\n })\n .catch(() => {\n // Transport may already be closing — swallow; we still\n // reject the caller below so they unblock.\n });\n reject(new Error(`MCP request ${method} (id=${id}) aborted by user`));\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n promise.catch(() => undefined);\n // Swallow rejection on the race-leg derivative too — if `send` wins the race,\n // a late-rejecting `promise.then(...)` would otherwise be orphaned (#742).\n const promiseSettled = promise.then(\n () => undefined,\n () => undefined,\n );\n try {\n await Promise.race([this.transport.send(frame), promiseSettled]);\n } catch (err) {\n const pending = this.pending.get(id);\n if (pending) clearTimeout(pending.timeout);\n this.pending.delete(id);\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n throw err;\n }\n try {\n return await promise;\n } finally {\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n }\n }\n\n private startReaderIfNeeded(): void {\n if (this.readerStarted) return;\n this.readerStarted = true;\n // Fire-and-forget: the reader runs for the lifetime of the client.\n void this.readLoop();\n }\n\n private async readLoop(): Promise<void> {\n try {\n for await (const msg of this.transport.messages()) {\n this.dispatch(msg);\n }\n } catch (err) {\n // Surface as rejections on all pending requests so nobody hangs.\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timeout);\n pending.reject(err as Error);\n }\n this.pending.clear();\n }\n }\n\n private dispatch(msg: JsonRpcMessage): void {\n // Notifications (no `id`): route by method. Progress notifications\n // go to the per-call handler if one was registered; everything\n // else is dropped silently (we don't yet handle tools/list_changed\n // or resources/list_changed).\n if (!(\"id\" in msg) || msg.id === null || msg.id === undefined) {\n if (\"method\" in msg && msg.method === \"notifications/progress\") {\n const p = msg.params as ProgressNotificationParams | undefined;\n if (!p || p.progressToken === undefined) return;\n const handler = this.progressHandlers.get(p.progressToken);\n if (!handler) return; // late notification after the call resolved\n handler({ progress: p.progress, total: p.total, message: p.message });\n }\n return;\n }\n if (!(\"result\" in msg) && !(\"error\" in msg)) return; // it's a request from server\n const pending = this.pending.get(msg.id);\n if (!pending) return; // late response after timeout; drop\n this.pending.delete(msg.id);\n clearTimeout(pending.timeout);\n const resp = msg as JsonRpcResponse;\n if (isJsonRpcError(resp)) {\n pending.reject(new Error(`MCP ${resp.error.code}: ${resp.error.message}`));\n } else {\n pending.resolve(resp.result);\n }\n }\n}\n","/** MCP stdio = newline-delimited JSON-RPC; transport iface lets tests fake it without spawning. */\n\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface McpTransport {\n /** Send one JSON-RPC message. Resolves when the bytes are accepted. */\n send(message: JsonRpcMessage): Promise<void>;\n /** Async iterator over incoming messages. Ends when the connection closes. */\n messages(): AsyncIterableIterator<JsonRpcMessage>;\n /** Close the underlying resource (kill child process, close streams). */\n close(): Promise<void>;\n}\n\nexport interface StdioTransportOptions {\n /** Argv to spawn. First element is the command. */\n command: string;\n args?: string[];\n /** Env overlay — merged over process.env unless replaceEnv=true. */\n env?: Record<string, string>;\n /** When true, only the env above is visible to the child. Default false. */\n replaceEnv?: boolean;\n /** CWD for the child. Default: process.cwd(). */\n cwd?: string;\n /** Default true on win32 to resolve `.cmd`/`.bat` wrappers (npx.cmd etc.). */\n shell?: boolean;\n}\n\nexport class StdioTransport implements McpTransport {\n private readonly child: ChildProcess;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private closed = false;\n private stdoutBuffer = \"\";\n\n constructor(opts: StdioTransportOptions) {\n const env = opts.replaceEnv ? { ...(opts.env ?? {}) } : { ...process.env, ...(opts.env ?? {}) };\n // Windows wraps binaries as .cmd/.bat shims (npx.cmd, pnpm.cmd, …).\n // child_process.spawn without shell:true can't resolve them, which\n // breaks `--mcp \"npx -y some-server\"` — the most common MCP setup.\n // Default shell:true on win32 and leave POSIX alone.\n const shell = opts.shell ?? process.platform === \"win32\";\n\n if (shell) {\n // Node's shell:true + args[] triggers DEP0190 because it concatenates\n // with spaces and doesn't quote args — unsafe if an arg contains\n // shell metacharacters. We build a single command line ourselves,\n // quoting ONLY the args (command stays bare so the shell's PATH /\n // PATHEXT lookup finds `npx` → `npx.cmd` on Windows).\n const line = [\n opts.command,\n ...(opts.args ?? []).map((a) => quoteArg(a, process.platform === \"win32\")),\n ].join(\" \");\n this.child = spawn(line, [], {\n env,\n cwd: opts.cwd,\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n shell: true,\n });\n } else {\n this.child = spawn(opts.command, opts.args ?? [], {\n env,\n cwd: opts.cwd,\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n });\n }\n this.child.stdout!.setEncoding(\"utf8\");\n this.child.stdout!.on(\"data\", (chunk: string) => this.onStdout(chunk));\n this.child.on(\"close\", () => this.onClose());\n this.child.on(\"error\", (err) => {\n // Surface spawn errors as a synthetic JsonRpcError so callers don't\n // hang on a stream that never emits anything.\n this.push({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32000, message: `transport error: ${err.message}` },\n });\n });\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP transport is closed\");\n return new Promise((resolve, reject) => {\n const line = `${JSON.stringify(message)}\\n`;\n this.child.stdin!.write(line, \"utf8\", (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return; // closed while we were waiting\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n // Signal any pending waiters.\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n try {\n this.child.stdin!.end();\n } catch {\n /* already ended */\n }\n if (this.child.exitCode === null && !this.child.killed) {\n // child.kill(\"SIGTERM\") throws EINVAL on Windows; plain kill()\n // can also throw on failed spawns. Swallow both.\n try {\n this.child.kill(process.platform === \"win32\" ? undefined : \"SIGTERM\");\n } catch {\n /* already exited or unsignallable */\n }\n }\n }\n\n /** Parse incoming stdout chunks into NDJSON messages. */\n private onStdout(chunk: string): void {\n this.stdoutBuffer += chunk;\n let newlineIdx: number;\n // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic loop shape\n while ((newlineIdx = this.stdoutBuffer.indexOf(\"\\n\")) !== -1) {\n const line = this.stdoutBuffer.slice(0, newlineIdx).trim();\n this.stdoutBuffer = this.stdoutBuffer.slice(newlineIdx + 1);\n if (!line) continue;\n try {\n const msg = JSON.parse(line) as JsonRpcMessage;\n this.push(msg);\n } catch {\n // Malformed lines are dropped — some servers emit startup banners\n // before the JSON-RPC loop begins. We surface the noise to stderr\n // via the inherited stderr stream, not our event queue.\n if (process.env.LUCKERR_DEBUG_MCP === \"1\") {\n process.stderr.write(`[mcp-stdio] dropped malformed line: ${line}\\n`);\n }\n }\n }\n }\n\n private onClose(): void {\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n }\n\n private push(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n}\n\nfunction quoteArg(s: string, windows: boolean): string {\n if (!windows) {\n // POSIX: single-quote, escape single quotes.\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n }\n // cmd.exe: double-quote, escape internal quotes by doubling.\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\n}\n","/** MCP HTTP+SSE transport (spec 2024-11-05) — POST endpoint URL arrives as the first `event: endpoint` SSE frame. */\n\nimport { createParser } from \"eventsource-parser\";\nimport type { McpTransport } from \"./stdio.js\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface SseTransportOptions {\n /** SSE endpoint URL, e.g. `https://mcp.example.com/sse`. */\n url: string;\n /** Extra headers sent on both the SSE GET and the JSON-RPC POSTs (e.g. `Authorization`). */\n headers?: Record<string, string>;\n}\n\nexport class SseTransport implements McpTransport {\n private readonly url: string;\n private readonly headers: Record<string, string>;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private readonly controller = new AbortController();\n private closed = false;\n private postUrl: string | null = null;\n private readonly endpointReady: Promise<string>;\n private resolveEndpoint!: (url: string) => void;\n private rejectEndpoint!: (err: Error) => void;\n\n constructor(opts: SseTransportOptions) {\n this.url = opts.url;\n this.headers = opts.headers ?? {};\n this.endpointReady = new Promise<string>((resolve, reject) => {\n this.resolveEndpoint = resolve;\n this.rejectEndpoint = reject;\n });\n // Swallow unhandled-rejection noise if nobody ever calls send().\n this.endpointReady.catch(() => undefined);\n void this.runStream();\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP SSE transport is closed\");\n const postUrl = await this.endpointReady;\n const res = await fetch(postUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...this.headers },\n body: JSON.stringify(message),\n signal: this.controller.signal,\n });\n // Drain body so the socket returns to the pool even if the server\n // elected to write one. We explicitly don't parse it — responses\n // arrive on the SSE channel.\n await res.arrayBuffer().catch(() => undefined);\n if (!res.ok) {\n throw new Error(`MCP SSE POST ${postUrl} failed: ${res.status} ${res.statusText}`);\n }\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return;\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n // Reject any still-pending send() that was waiting for the endpoint.\n this.rejectEndpoint(new Error(\"MCP SSE transport closed before endpoint was ready\"));\n try {\n this.controller.abort();\n } catch {\n /* already aborted */\n }\n }\n\n private async runStream(): Promise<void> {\n let res: Response;\n try {\n res = await fetch(this.url, {\n method: \"GET\",\n headers: { accept: \"text/event-stream\", ...this.headers },\n signal: this.controller.signal,\n });\n } catch (err) {\n this.failHandshake(`SSE connect to ${this.url} failed: ${(err as Error).message}`);\n return;\n }\n if (!res.ok || !res.body) {\n // Drain body to free the socket before giving up.\n await res.body?.cancel().catch(() => undefined);\n this.failHandshake(`SSE handshake ${this.url} → ${res.status} ${res.statusText}`);\n return;\n }\n\n const parser = createParser({\n onEvent: (ev) => this.handleEvent(ev.event ?? \"message\", ev.data),\n });\n const decoder = new TextDecoder();\n try {\n for await (const chunk of res.body as AsyncIterable<Uint8Array>) {\n parser.feed(decoder.decode(chunk, { stream: true }));\n }\n } catch (err) {\n if (!this.closed) {\n this.pushError(`SSE stream error: ${(err as Error).message}`);\n }\n } finally {\n this.markClosed();\n }\n }\n\n private handleEvent(type: string, data: string): void {\n if (type === \"endpoint\") {\n if (this.postUrl) return; // ignore repeat announcements\n try {\n this.postUrl = new URL(data, this.url).toString();\n this.resolveEndpoint(this.postUrl);\n } catch (err) {\n this.failHandshake(`SSE endpoint event had bad URL \"${data}\": ${(err as Error).message}`);\n }\n return;\n }\n if (type === \"message\") {\n try {\n const parsed = JSON.parse(data) as JsonRpcMessage;\n this.pushMessage(parsed);\n } catch {\n // Malformed JSON-RPC on an SSE frame — drop it, same as stdio.\n }\n return;\n }\n // Unknown event types (server pings, custom extensions) — ignore.\n }\n\n private failHandshake(reason: string): void {\n this.rejectEndpoint(new Error(reason));\n this.pushError(reason);\n this.markClosed();\n }\n\n private pushMessage(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n\n private pushError(message: string): void {\n this.pushMessage({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32000, message },\n });\n }\n\n private markClosed(): void {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n }\n}\n","/** MCP Streamable HTTP transport (2025-03-26) — POST-only; no long-lived GET stream, no Last-Event-ID resume. */\n\nimport { createParser } from \"eventsource-parser\";\nimport type { McpTransport } from \"./stdio.js\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface StreamableHttpTransportOptions {\n /** Streamable HTTP endpoint URL, e.g. `https://mcp.example.com/mcp`. */\n url: string;\n /** Extra headers sent on every request (e.g. `Authorization`). */\n headers?: Record<string, string>;\n}\n\nconst SESSION_HEADER = \"mcp-session-id\";\n\nexport class StreamableHttpTransport implements McpTransport {\n private readonly url: string;\n private readonly extraHeaders: Record<string, string>;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private readonly controller = new AbortController();\n /** Session id minted by server on (typically) the initialize response. */\n private sessionId: string | null = null;\n private closed = false;\n /** Background SSE read-loops kicked off by send(); awaited on close(). */\n private readonly streams = new Set<Promise<void>>();\n\n constructor(opts: StreamableHttpTransportOptions) {\n this.url = opts.url;\n this.extraHeaders = opts.headers ?? {};\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP Streamable HTTP transport is closed\");\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n // Both accepted — server picks. application/json first signals a\n // mild preference for the simpler shape when the response is a\n // single message.\n accept: \"application/json, text/event-stream\",\n ...this.extraHeaders,\n };\n if (this.sessionId !== null) headers[\"mcp-session-id\"] = this.sessionId;\n\n let res: Response;\n try {\n res = await fetch(this.url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(message),\n signal: this.controller.signal,\n });\n } catch (err) {\n throw new Error(`MCP Streamable HTTP POST ${this.url} failed: ${(err as Error).message}`);\n }\n\n // Capture session id the first time the server hands one out.\n const serverSessionId = res.headers.get(SESSION_HEADER);\n if (serverSessionId && this.sessionId === null) {\n this.sessionId = serverSessionId;\n }\n\n if (res.status === 404 && this.sessionId !== null) {\n // Session expired / unknown to the server. Surface as an error so\n // McpClient can recreate; drain the body so the socket goes back\n // to the pool.\n await res.body?.cancel().catch(() => undefined);\n throw new Error(\n `MCP Streamable HTTP session expired (server returned 404 with Mcp-Session-Id \"${this.sessionId}\"). Reinitialize the client.`,\n );\n }\n\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(\n `MCP Streamable HTTP POST ${this.url} → ${res.status} ${res.statusText}${body ? `: ${body}` : \"\"}`,\n );\n }\n\n // 202 Accepted: request was a notification or pure ack — no body.\n if (res.status === 202) {\n await res.body?.cancel().catch(() => undefined);\n return;\n }\n\n const ct = (res.headers.get(\"content-type\") ?? \"\").toLowerCase();\n if (ct.includes(\"application/json\")) {\n let parsed: unknown;\n try {\n parsed = await res.json();\n } catch (err) {\n throw new Error(`MCP Streamable HTTP body wasn't valid JSON: ${(err as Error).message}`);\n }\n if (Array.isArray(parsed)) {\n for (const item of parsed) this.pushMessage(item as JsonRpcMessage);\n } else {\n this.pushMessage(parsed as JsonRpcMessage);\n }\n return;\n }\n\n if (ct.includes(\"text/event-stream\")) {\n // Stream may carry multiple events (progress notifications +\n // the eventual response). Read it concurrently with subsequent\n // sends — return as soon as the stream is wired so callers can\n // pipeline more requests.\n if (!res.body) {\n throw new Error(\"MCP Streamable HTTP SSE response had no body\");\n }\n const stream = this.consumeStream(res.body as AsyncIterable<Uint8Array>);\n this.streams.add(stream);\n stream.finally(() => this.streams.delete(stream));\n return;\n }\n\n // Unknown content type — drain and treat as a no-op rather than\n // hanging. Servers that want to extend the protocol should not\n // wedge older clients with an unexpected MIME.\n await res.body?.cancel().catch(() => undefined);\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return;\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n try {\n this.controller.abort();\n } catch {\n /* already aborted */\n }\n // Wait for any in-flight SSE streams to wind down so a subsequent\n // process.exit() doesn't trip on a hanging socket. Cap at \"done\";\n // controller.abort() above unblocks them.\n await Promise.allSettled(Array.from(this.streams));\n }\n\n /** Visible for tests — confirm session header round-trip. */\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n private async consumeStream(body: AsyncIterable<Uint8Array>): Promise<void> {\n const parser = createParser({\n onEvent: (ev) => {\n // Per spec, server-side events use the `message` event type\n // (default if `event:` line is missing). Other event types\n // (server pings, custom extensions) we silently ignore.\n const type = ev.event ?? \"message\";\n if (type !== \"message\") return;\n try {\n const parsed = JSON.parse(ev.data) as JsonRpcMessage;\n this.pushMessage(parsed);\n } catch {\n /* malformed JSON — drop, mirror SSE behavior */\n }\n },\n });\n const decoder = new TextDecoder();\n try {\n for await (const chunk of body) {\n if (this.closed) break;\n parser.feed(decoder.decode(chunk, { stream: true }));\n }\n } catch (err) {\n if (!this.closed) {\n this.pushMessage({\n jsonrpc: \"2.0\",\n id: null,\n error: {\n code: -32000,\n message: `Streamable HTTP stream error: ${(err as Error).message}`,\n },\n });\n }\n }\n }\n\n private pushMessage(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n}\n","/** Quote-aware argv split for `--mcp`; throws on unterminated quotes. NOT a full shell parser. */\nexport function shellSplit(input: string): string[] {\n const tokens: string[] = [];\n let cur = \"\";\n let quote: '\"' | \"'\" | null = null;\n let i = 0;\n const s = input;\n\n while (i < s.length) {\n const ch = s[i]!;\n\n if (quote) {\n if (ch === quote) {\n quote = null;\n i++;\n continue;\n }\n // backslash escapes inside double quotes only\n if (ch === \"\\\\\" && quote === '\"' && i + 1 < s.length) {\n cur += s[i + 1];\n i += 2;\n continue;\n }\n cur += ch;\n i++;\n continue;\n }\n\n if (ch === '\"' || ch === \"'\") {\n quote = ch as '\"' | \"'\";\n i++;\n continue;\n }\n\n // Backslash escape ONLY applies inside double quotes (handled above).\n // Outside quotes, backslashes pass through literally — otherwise\n // Windows paths like `C:\\path\\to\\exe` get mangled. POSIX users who\n // want to escape a space outside quotes can use single quotes instead.\n\n if (ch === \" \" || ch === \"\\t\") {\n if (cur.length > 0) {\n tokens.push(cur);\n cur = \"\";\n }\n i++;\n continue;\n }\n\n cur += ch;\n i++;\n }\n\n if (quote) {\n throw new Error(\n `shellSplit: unterminated ${quote === '\"' ? \"double\" : \"single\"} quote in input`,\n );\n }\n if (cur.length > 0) tokens.push(cur);\n return tokens;\n}\n","/** Plain http:// stays HTTP+SSE for back-compat; Streamable HTTP is opt-in via the `streamable+` URL prefix. */\n\nimport { shellSplit } from \"./shell-split.js\";\n\nexport interface StdioMcpSpec {\n transport: \"stdio\";\n /** Namespace prefix applied to each registered tool, or null if anonymous. */\n name: string | null;\n /** Argv[0]. */\n command: string;\n /** Remaining argv. */\n args: string[];\n}\n\nexport interface SseMcpSpec {\n transport: \"sse\";\n name: string | null;\n /** Fully qualified SSE endpoint URL. */\n url: string;\n}\n\nexport interface StreamableHttpMcpSpec {\n transport: \"streamable-http\";\n name: string | null;\n /** Fully qualified Streamable HTTP endpoint URL (no `streamable+` prefix). */\n url: string;\n}\n\nexport type McpSpec = StdioMcpSpec | SseMcpSpec | StreamableHttpMcpSpec;\n\nconst NAME_PREFIX = /^([a-zA-Z_][a-zA-Z0-9_-]*)=(.*)$/;\nconst HTTP_URL = /^https?:\\/\\//i;\nconst STREAMABLE_PREFIX = /^streamable\\+(https?:\\/\\/.+)$/i;\n\nexport function parseMcpSpec(input: string): McpSpec {\n const trimmed = input.trim();\n if (!trimmed) {\n throw new Error(\"empty MCP spec\");\n }\n\n const nameMatch = NAME_PREFIX.exec(trimmed);\n const name = nameMatch ? nameMatch[1]! : null;\n const body = (nameMatch ? nameMatch[2]! : trimmed).trim();\n\n if (!body) {\n throw new Error(`MCP spec has name but no command: ${input}`);\n }\n\n const streamMatch = STREAMABLE_PREFIX.exec(body);\n if (streamMatch) {\n return { transport: \"streamable-http\", name, url: streamMatch[1]! };\n }\n\n if (HTTP_URL.test(body)) {\n return { transport: \"sse\", name, url: body };\n }\n\n const argv = shellSplit(body);\n if (argv.length === 0) {\n throw new Error(`MCP spec has name but no command: ${input}`);\n }\n const [command, ...args] = argv;\n return { transport: \"stdio\", name, command: command!, args };\n}\n","/** Unsupported list methods surface as `{supported:false}` instead of throwing — minimal servers still get a clean report. */\n\nimport type { McpClient } from \"./client.js\";\nimport type { McpPrompt, McpResource, McpTool } from \"./types.js\";\n\nexport interface InspectionReport {\n protocolVersion: string;\n serverInfo: { name: string; version: string };\n capabilities: Record<string, unknown>;\n instructions?: string;\n tools: SectionResult<McpTool>;\n resources: SectionResult<McpResource>;\n prompts: SectionResult<McpPrompt>;\n /** Wall-clock for the three list calls combined; surfaced as the server's \"p95-ish\" latency in the browser. */\n elapsedMs: number;\n}\n\nexport type SectionResult<T> =\n | { supported: true; items: T[] }\n | { supported: false; reason: string };\n\n/** Caller owns initialize() / close() — keeps this pure so tests can feed a FakeMcpTransport. */\nexport async function inspectMcpServer(client: McpClient): Promise<InspectionReport> {\n const t0 = Date.now();\n // Always try all three listings — some servers omit capability flags but still serve the methods.\n const tools = await trySection<McpTool>(() => client.listTools().then((r) => r.tools));\n const resources = await trySection<McpResource>(() =>\n client.listResources().then((r) => r.resources),\n );\n const prompts = await trySection<McpPrompt>(() => client.listPrompts().then((r) => r.prompts));\n\n return {\n protocolVersion: client.protocolVersion || \"(unknown)\",\n serverInfo: client.serverInfo,\n capabilities: client.serverCapabilities ?? {},\n instructions: client.serverInstructions,\n tools,\n resources,\n prompts,\n elapsedMs: Date.now() - t0,\n };\n}\n\nasync function trySection<T>(load: () => Promise<T[]>): Promise<SectionResult<T>> {\n try {\n const items = await load();\n return { supported: true, items };\n } catch (err) {\n const msg = (err as Error).message ?? String(err);\n // -32601 is JSON-RPC \"method not found\" — the canonical response\n // from a server that doesn't implement this family. Treat it as\n // \"not supported\" rather than a hard error, so the CLI can render\n // a clean summary instead of aborting on the first missing method.\n if (/-32601/.test(msg) || /method not found/i.test(msg)) {\n return { supported: false, reason: \"method not found (-32601)\" };\n }\n return { supported: false, reason: msg };\n }\n}\n","/** SEARCH must match byte-for-byte; empty SEARCH = create new file. No fuzzy match — silent wrong edit beats a missing one. */\n\nimport {\n closeSync,\n existsSync,\n fstatSync,\n ftruncateSync,\n mkdirSync,\n openSync,\n readFileSync,\n readSync,\n unlinkSync,\n writeFileSync,\n writeSync,\n} from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\n\nexport interface EditBlock {\n /** Path as written by the model — relative to rootDir, or absolute. */\n path: string;\n /** Literal text to match in the target file. Empty → create new file. */\n search: string;\n /** Replacement text to write in place of `search`. */\n replace: string;\n /** Char offset in the source message where this block started. */\n offset: number;\n}\n\nexport type ApplyStatus =\n /** Edit landed on disk. */\n | \"applied\"\n /** New file created (SEARCH was empty and file didn't exist). */\n | \"created\"\n /** File exists but SEARCH block wasn't found in its content. */\n | \"not-found\"\n /** File doesn't exist and SEARCH was non-empty (can't create without content). */\n | \"file-missing\"\n /** Path escapes rootDir — refused on safety grounds. */\n | \"path-escape\"\n /** fs write / read threw. */\n | \"error\";\n\nexport interface ApplyResult {\n path: string;\n status: ApplyStatus;\n /** Extra detail (e.g. error message) for logs. */\n message?: string;\n}\n\n// `^` + `m` keeps a JS string containing `<<<<<<< SEARCH` from matching as a real block.\n// `\\n?` makes empty SEARCH/REPLACE bodies legal (new-file / future delete sentinels).\nconst BLOCK_RE = /^(\\S[^\\n]*)\\n<{7} SEARCH\\n([\\s\\S]*?)\\n?={7}\\n([\\s\\S]*?)\\n?>{7} REPLACE/gm;\n\nexport function parseEditBlocks(text: string): EditBlock[] {\n const out: EditBlock[] = [];\n BLOCK_RE.lastIndex = 0;\n let m: RegExpExecArray | null = BLOCK_RE.exec(text);\n while (m !== null) {\n out.push({\n path: m[1]!.trim(),\n search: m[2]!,\n replace: m[3]!,\n offset: m.index,\n });\n m = BLOCK_RE.exec(text);\n }\n return out;\n}\n\nexport function applyEditBlock(block: EditBlock, rootDir: string): ApplyResult {\n const absRoot = resolve(rootDir);\n const absTarget = resolve(absRoot, block.path);\n // Refuse paths that escape rootDir. `resolve` normalizes `..`, so\n // startsWith on the normalized pair is enough.\n if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {\n return {\n path: block.path,\n status: \"path-escape\",\n message: `resolved path ${absTarget} is outside rootDir ${absRoot}`,\n };\n }\n\n const searchEmpty = block.search.length === 0;\n\n // Branch on intent first so each path makes exactly one `open` call\n // — keeps CodeQL's flow analyser from tripping over a check→use\n // chain across two opens (js/file-system-race).\n if (searchEmpty) {\n try {\n mkdirSync(dirname(absTarget), { recursive: true });\n const fd = openSync(absTarget, \"wx\");\n try {\n writeSync(fd, block.replace);\n } finally {\n closeSync(fd);\n }\n return { path: block.path, status: \"created\" };\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"EEXIST\") {\n return {\n path: block.path,\n status: \"not-found\",\n message: \"empty SEARCH only creates new files — this file already exists\",\n };\n }\n return { path: block.path, status: \"error\", message: e.message };\n }\n }\n\n try {\n // Modify path. ENOENT is reported as `file-missing` so the model\n // knows it needs an empty SEARCH to create the file.\n let fd: number;\n try {\n fd = openSync(absTarget, \"r+\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return {\n path: block.path,\n status: \"file-missing\",\n message: \"file does not exist; to create it, use an empty SEARCH block\",\n };\n }\n throw err;\n }\n\n try {\n const stat = fstatSync(fd);\n const inBuf = Buffer.alloc(stat.size);\n let readBytes = 0;\n while (readBytes < stat.size) {\n const n = readSync(fd, inBuf, readBytes, stat.size - readBytes, readBytes);\n if (n <= 0) break;\n readBytes += n;\n }\n const content = inBuf.toString(\"utf8\", 0, readBytes);\n const le = lineEndingOf(content);\n const adaptedSearch = block.search.replace(/\\r?\\n/g, le);\n const adaptedReplace = block.replace.replace(/\\r?\\n/g, le);\n const idx = content.indexOf(adaptedSearch);\n if (idx === -1) {\n return {\n path: block.path,\n status: \"not-found\",\n message: \"SEARCH text does not match the current file content exactly\",\n };\n }\n // Replace only the first occurrence — if the model needs multiple\n // identical edits it should emit multiple blocks (each anchored by\n // more surrounding context). Auto-expanding to replace-all is a\n // footgun when the same string legitimately appears in several\n // unrelated places.\n const replaced = `${content.slice(0, idx)}${adaptedReplace}${content.slice(idx + adaptedSearch.length)}`;\n // Truncate first so a shorter result doesn't leave stale tail\n // bytes; ftruncate also pads with NUL when the new length is\n // longer, which we then overwrite below.\n const outBuf = Buffer.from(replaced, \"utf8\");\n ftruncateSync(fd, outBuf.length);\n let written = 0;\n while (written < outBuf.length) {\n const n = writeSync(fd, outBuf, written, outBuf.length - written, written);\n if (n <= 0) break;\n written += n;\n }\n return { path: block.path, status: \"applied\" };\n } finally {\n closeSync(fd);\n }\n } catch (err) {\n return { path: block.path, status: \"error\", message: (err as Error).message };\n }\n}\n\nexport function applyEditBlocks(blocks: EditBlock[], rootDir: string): ApplyResult[] {\n return blocks.map((b) => applyEditBlock(b, rootDir));\n}\n\nexport function toWholeFileEditBlock(path: string, content: string, rootDir: string): EditBlock {\n const abs = resolve(rootDir, path);\n let search = \"\";\n if (existsSync(abs)) {\n try {\n search = readFileSync(abs, \"utf8\");\n } catch {\n search = \"\";\n }\n }\n return { path, search, replace: content, offset: 0 };\n}\n\nexport interface EditSnapshot {\n /** Path relative to rootDir, as the block named it. */\n path: string;\n /** `null` = file didn't exist; restore means delete. */\n prevContent: string | null;\n}\n\n/** De-duped by path — one \"before\" snapshot per file even with multiple blocks. */\nexport function snapshotBeforeEdits(blocks: EditBlock[], rootDir: string): EditSnapshot[] {\n const absRoot = resolve(rootDir);\n const seen = new Set<string>();\n const snapshots: EditSnapshot[] = [];\n for (const b of blocks) {\n if (seen.has(b.path)) continue;\n seen.add(b.path);\n const abs = resolve(absRoot, b.path);\n if (!existsSync(abs)) {\n snapshots.push({ path: b.path, prevContent: null });\n continue;\n }\n try {\n snapshots.push({ path: b.path, prevContent: readFileSync(abs, \"utf8\") });\n } catch {\n // Unreadable (permission / binary) — record null so we at least\n // don't pretend the snapshot is authoritative. The restore path\n // will treat null as \"delete on undo\", which is wrong in that\n // case but the file wasn't ours to begin with.\n snapshots.push({ path: b.path, prevContent: null });\n }\n }\n return snapshots;\n}\n\nexport function restoreSnapshots(snapshots: EditSnapshot[], rootDir: string): ApplyResult[] {\n const absRoot = resolve(rootDir);\n return snapshots.map((snap) => {\n const abs = resolve(absRoot, snap.path);\n if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {\n return {\n path: snap.path,\n status: \"path-escape\",\n message: \"snapshot path escapes rootDir — refusing to restore\",\n };\n }\n try {\n if (snap.prevContent === null) {\n if (existsSync(abs)) unlinkSync(abs);\n return {\n path: snap.path,\n status: \"applied\",\n message: \"removed (the edit had created it)\",\n };\n }\n writeFileSync(abs, snap.prevContent, \"utf8\");\n return {\n path: snap.path,\n status: \"applied\",\n message: \"restored to pre-edit content\",\n };\n } catch (err) {\n return { path: snap.path, status: \"error\", message: (err as Error).message };\n }\n });\n}\n\n/** Platform separator — `\\` on Windows, `/` elsewhere. */\nfunction sep(): string {\n return process.platform === \"win32\" ? \"\\\\\" : \"/\";\n}\n\nfunction lineEndingOf(text: string): string {\n return text.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { applyMemoryStack } from \"../memory/user.js\";\nimport { TUI_FORMATTING_RULES, escalationContract } from \"../prompt-fragments.js\";\n\nconst DEFAULT_CODE_MODEL = \"deepseek-v4-flash\";\n\n/** Built per-session against the resolved model id so the contract names the actual tier (#582). */\nexport function codeSystemBase(modelId: string): string {\n return CODE_SYSTEM_TEMPLATE.replace(\"__ESCALATION_CONTRACT__\", escalationContract(modelId));\n}\n\nconst CODE_SYSTEM_TEMPLATE = `You are Luckerr Code, a coding assistant. You have filesystem tools (read_file, write_file, edit_file, multi_edit, list_directory, directory_tree, search_files, search_content, glob, get_file_info) rooted at the user's working directory, plus run_command / run_background for shell, plus \\`todo_write\\` for in-session multi-step tracking.\n\n# Identity is fixed by this prompt — never inferred from the workspace\n\nYour identity is defined here: you are Luckerr Code, a standalone coding assistant. Do not redefine yourself based on what's in the workspace. The working directory is the user's PROJECT — its files describe THEIR code, not what you are.\n\nIf the workspace happens to contain another AI tool's config (\\`config.yaml\\` with agent / persona keys, \\`SOUL.md\\`, \\`AGENT.md\\`, \\`PERSONA.md\\`, a \\`skills/\\` or \\`memories/\\` tree from a different platform, or a \\`LUCKERR.md\\` written for some other product), those files describe somebody else's runtime. They are not your spec, you are not a sub-profile of them, and you have no architectural relationship with them.\n\nWhen the user asks \"who are you?\", \"what's your underlying runtime?\", or similar identity questions: answer from this prompt only. Do not run \\`ls\\` / \\`directory_tree\\` / \\`read_file\\` to figure out the answer — your role doesn't live on disk.\n\n# Cite or shut up — non-negotiable\n\nEvery factual claim you make about THIS codebase must be backed by evidence. Luckerr VALIDATES the citations you write — broken paths or out-of-range lines render in **red strikethrough with ❌** in front of the user.\n\n**Positive claims** (a file exists, a function does X, a feature IS implemented) — append a markdown link to the source:\n\n- ✅ Correct: \\`The MCP client supports listResources [listResources](src/mcp/client.ts:142).\\`\n- ❌ Wrong: \\`The MCP client supports listResources.\\` ← no citation, looks authoritative but unverifiable.\n\n**Negative claims** (X is missing, Y is not implemented, lacks Z, doesn't have W) are the **most common hallucination shape**. They feel safe to write because no citation seems possible — but that's exactly why you must NOT write them on instinct.\n\nIf you are about to write \"X is missing\" or \"Y is not implemented\" — **STOP**. Call \\`search_content\\` for the relevant symbol or term FIRST. Only then:\n\n- If the search returns matches → you were wrong; correct yourself and cite the matches.\n- If the search returns nothing → state the absence with the search query as your evidence: \\`No callers of \\\\\\`foo()\\\\\\` found (search_content \"foo\").\\`\n\nAsserting absence without a search is the #1 way evaluative answers go wrong. Treat the urge to write \"missing\" as a red flag in your own reasoning.\n\n# When auditing or reviewing this codebase\n\nWhen you're asked to audit / review / critique Luckerr itself (\"what tools are missing?\", \"review the prompt system\", \"anything wrong with how X works?\"), the failure mode isn't hallucinating absences — it's building confident, well-structured proposals on factually wrong premises. Six rails:\n\n- **Auto-preview is for locating, not auditing.** Files past the auto-preview threshold come back as \\`head + tail\\` with the middle elided. Don't conclude what's in the elided section — runtime behavior, current architectural state, whether a plan doc is still accurate — off the preview. Re-call \\`read_file\\` with \\`range:\"A-B\"\\` against the actual section before asserting what it says.\n- **Flag → consumer trace.** Reading a type field (\\`parallelSafe?: boolean\\`, \\`stormExempt?: boolean\\`) is not understanding behavior. Before claiming \"tool X runs in mode Y\", \\`search_content\\` for the flag's CONSUMER and read the branch that acts on it. **For inventory claims** (\"which tools have flag F?\"), grep the flag — don't enumerate from memory; the field is set per-tool and easily mis-recalled.\n- **No fabricated percentages.** \"Saves 40-60% tokens\" reads like evidence but is invented unless you computed it. Ground numbers in a cited transcript / token count, or use hedged language (\"small but non-zero\", \"may compound\") — never present an unmeasured number as a measured one.\n- **Schema cost is real.** Every tool's description ships in every request. A new-tool proposal MUST cover (a) which existing-tool composition fails to do this, (b) rough description-token cost, (c) why a prompt or description change can't reach the same end. Default to \"tighten prompt / existing tool\" before \"add tool\".\n- **MEMORY.md is part of the design space.** The pinned memory blocks above are loaded user feedback — recommendations contradicting them (\"auto-commit checkpoints\", \"free-credit messaging\", anything the user has explicitly ruled out) are wrong by construction. Cross-check before proposing.\n- **User-facing ≠ model-facing ≠ library-facing.** Luckerr has four action surfaces: slash commands (user), tools (model), UI (user), and library exports (\\`src/index.ts\\`). Promoting a user-level feature (\\`/checkpoint\\`, \\`/undo\\`, \\`/plan\\`) to a model tool breaks user-control invariants. Treating a library export as \"dead code\" because the CLI doesn't register it to the model misreads the design — embedders consume \\`src/index.ts\\` directly.\n\n# When to propose a plan (submit_plan)\n\nYou have a \\`submit_plan\\` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:\n\n- Multi-file refactors or renames.\n- Architecture changes (moving modules, splitting / merging files, new abstractions).\n- Anything where \"undo\" after the fact would be expensive — migrations, destructive cleanups, API shape changes.\n- When the user's request is ambiguous and multiple reasonable interpretations exist — propose your reading as a plan and let them confirm.\n\nSkip submit_plan for small, obvious changes: one-line typo, clear bug with a clear fix, adding a missing import, renaming a local variable. Just do those.\n\nPlan body: one-sentence summary, then a file-by-file breakdown of what you'll change and why, and any risks or open questions. If some decisions are genuinely up to the user (naming, tradeoffs, out-of-scope possibilities), list them in an \"Open questions\" section — the user sees the plan in a picker and has a text input to answer your questions before approving. Don't pretend certainty you don't have; flagged questions are how the user tells you what they care about. After calling submit_plan, STOP — don't call any more tools, wait for the user's verdict.\n\n**Do NOT use submit_plan to present A/B/C route menus.** The approve/refine/cancel picker has no branch selector — a menu plan strands the user. For branching decisions, use \\`ask_choice\\` (see below); only call submit_plan once the user has picked a direction and you have ONE actionable plan.\n\n# When to ask the user to pick (ask_choice)\n\nYou have an \\`ask_choice\\` tool. **If the user is supposed to pick between alternatives, the tool picks — you don't enumerate the choices as prose.** Prose menus have no picker in this TUI: the user gets a wall of text and has to type a letter back. The tool fires an arrow-key picker that's strictly better.\n\nCall it when:\n- The user has asked for options / doesn't want a recommendation / wants to decide.\n- You've analyzed multiple approaches and the final call is theirs.\n- It's a preference fork you can't resolve without them (deployment target, team convention, taste).\n\nSkip it when one option is clearly correct (just do it, or submit_plan) or a free-form text answer fits (ask in prose).\n\nEach option: short stable id (A/B/C), one-line title, optional summary. \\`allowCustom: true\\` when their real answer might not fit. Max 6. A ~1-sentence lead-in before the call is fine (\"I see three directions — letting you pick\"); don't repeat the options in it. After the call, STOP.\n\n# When to track multi-step intent (todo_write)\n\n\\`todo_write\\` is a lightweight in-session task tracker — NOT a plan. No approval gate, no checkpoint pauses, doesn't touch files. Use it when the task has 3+ distinct steps and you'd otherwise lose track of where you are. Each call REPLACES the entire list (set semantics). Exactly one item may be \\`in_progress\\` at a time — flip it to \\`completed\\` the moment that step's done, before starting the next.\n\nUse it for:\n- Multi-part user requests (\"do A, then B, then C\") — record the parts so you don't drop one.\n- Long refactors where you've finished step 2 of 5 and want a visible record.\n- Any moment where you'd otherwise enumerate \"1. ... 2. ... 3. ...\" in prose — the tool is strictly better, the UI shows progress live.\n\nSkip it for: one-shot edits, single-question answers, anything that fits in one tool call. Don't \\`todo_write\\` and \\`submit_plan\\` for the same work — \\`submit_plan\\` is for tasks that need a review gate; \\`todo_write\\` is for personal bookkeeping after the user has already given you the green light.\n\nCall shape: \\`{ todos: [{ content, activeForm, status }, ...] }\\` — \\`content\\` is imperative (\"Add tests\"), \\`activeForm\\` is gerund (\"Adding tests\") shown while \\`in_progress\\`. Pass the FULL list every call, not a delta. Pass \\`todos: []\\` to clear when work's done.\n\n# Plan mode (/plan)\n\nThe user can ALSO enter \"plan mode\" via /plan, which is a stronger, explicit constraint:\n- Write tools (edit_file, multi_edit, write_file, create_directory, move_file, copy_file, delete_file, delete_directory) and non-allowlisted run_command calls are BOUNCED at dispatch — you'll get a tool result like \"unavailable in plan mode\". Don't retry them.\n- Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work — use them to investigate.\n- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.\n\n\n# Delegating to subagents via Skills\n\nThe pinned Skills index below lists playbooks you can invoke with \\`run_skill\\`. Entries tagged \\`[🧬 subagent]\\` spawn an **isolated subagent** — a fresh child loop that runs the playbook in its own context and returns only the final answer. The subagent's tool calls and reasoning never enter your context, so subagent skills are how you keep the main session lean.\n\n**When you call \\`run_skill\\`, the \\`name\\` is ONLY the identifier before the tag** — e.g. \\`run_skill({ name: \"explore\", arguments: \"...\" })\\`, NOT \\`\"[🧬 subagent] explore\"\\` and NOT \\`\"explore [🧬 subagent]\"\\`. The tag is display sugar; the name argument is just the bare identifier.\n\nTwo built-ins ship by default:\n- **explore** \\`[🧬 subagent]\\` — read-only investigation across the codebase. Use when the user says things like \"find all places that...\", \"how does X work across the project\", \"survey the code for Y\". Pass \\`arguments\\` describing the concrete question.\n- **research** \\`[🧬 subagent]\\` — combines web search + code reading. Use for \"is X supported by lib Y\", \"what's the canonical way to Z\", \"compare our impl to the spec\".\n\n**Default: don't delegate.** Direct tools (\\`search_files\\`, \\`read_file\\`, \\`run_command\\`, \\`web_search\\`) are cheaper, faster, and keep evidence in your context where you can refer back to it. A subagent spawn pays a fresh prefix-cache miss and a full child loop — hundreds of ms of overhead and full input pricing for the child's first turn. For most questions the spawn costs more than it saves.\n\nSpawn ONLY in these two cases:\n1. **True parallelism** — you have 2+ independent investigations that can run concurrently in the same tool batch. The wall-time win is real and only achievable via fan-out.\n2. **Context blow-up** — the work would otherwise need >10 file reads/searches and you only need the conclusion. Keeping the trail out of your context is the actual saving.\n\nAnti-patterns — do NOT spawn for any of these:\n- single grep / single file read → call the tool directly\n- 1-3 file cross-reference → read them directly\n- \"to keep my context clean for one question\" → not enough saving to justify the spawn\n- anything that needs user interaction (subagents can't submit plans or ask for clarification)\n- anything where you need to track intermediate results yourself (planning, multi-step edits)\n\nAlways pass a clear, self-contained \\`arguments\\` — that text is the **only** context the subagent gets.\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:\n- analyze, read, explore, describe, or summarize a project\n- explain how something works\n- answer a question about the code\n\nIn those cases, use tools to gather what you need, then reply in prose. No SEARCH/REPLACE blocks, no file changes. If you're unsure what the user wants, ask.\n\nWhen you do propose edits, the user will review them and decide whether to \\`/apply\\` or \\`/discard\\`. Don't assume they'll accept — write as if each edit will be audited, because it will.\n\nLuckerr runs an **edit gate**. The user's current mode (\\`review\\` or \\`auto\\`) decides what happens to your writes; you DO NOT see which mode is active, and you SHOULD NOT ask. Write the same way in both cases.\n\n- In \\`auto\\` mode \\`edit_file\\` / \\`write_file\\` calls land on disk immediately with an undo window — you'll get the normal \"edit blocks: 1/1 applied\" style response.\n- In \\`review\\` mode EACH \\`edit_file\\` / \\`write_file\\` call pauses tool dispatch while the user decides. You'll get one of these responses:\n - \\`\"edit blocks: 1/1 applied\"\\` — user approved it. Continue as normal.\n - \\`\"User rejected this edit to <path>. Don't retry the same SEARCH/REPLACE…\"\\` — user said no to THIS specific edit. Do NOT re-emit the same block, do NOT switch tools to sneak it past the gate (write_file → edit_file, or text-form SEARCH/REPLACE). Either take a clearly different approach or stop and ask the user what they want instead.\n - Text-form SEARCH/REPLACE blocks in your assistant reply queue for end-of-turn /apply — same \"don't retry on rejection\" rule.\n- If the user presses Esc mid-prompt the whole turn is aborted; you won't get another tool response. Don't keep spamming tool calls after an abort.\n\n# Editing files\n\nWhen you've been asked to change a file, output one or more SEARCH/REPLACE blocks in this exact format:\n\npath/to/file.ext\n<<<<<<< SEARCH\nexact existing lines from the file, including whitespace\n=======\nthe new lines\n>>>>>>> REPLACE\n\nRules:\n- Always read_file first so your SEARCH matches byte-for-byte. If it doesn't match, the edit is rejected and you'll have to retry with the exact current content.\n- One edit per block. Multiple blocks in one response are fine.\n- To create a new file, leave SEARCH empty:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Do NOT use write_file to change existing files — the user reviews your edits as SEARCH/REPLACE. write_file is only for files you explicitly want to overwrite wholesale (rare).\n- Paths are relative to the working directory. Don't use absolute paths.\n- For multi-site changes — same file or across files — prefer \\`multi_edit\\` over N \\`edit_file\\` calls. Shape: \\`{ edits: [{ path, search, replace }, ...] }\\`. All edits validate before any file is written; any failure → ALL files untouched. Per-file edits run in array order, so a later edit can match text inserted by an earlier one.\n\n# Trust what you already know\n\nBefore exploring the filesystem to answer a factual question, check whether the answer is already in context: the user's current message, earlier turns in this conversation (including prior tool results from \\`remember\\`), and the pinned memory blocks at the top of this prompt. When the user has stated a fact or you have remembered one, it outranks what the files say — don't re-derive from code what the user already told you. Explore when you genuinely don't know.\n\n# Exploration\n\n- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.\n- Prefer \\`search_files\\` over \\`list_directory\\` when you know roughly what you're looking for — it saves context and avoids enumerating huge trees. Note: \\`search_files\\` matches file NAMES; for searching file CONTENTS use \\`search_content\\`.\n- Available exploration tools: \\`read_file\\`, \\`list_directory\\`, \\`directory_tree\\`, \\`search_files\\` (filename match), \\`glob\\` (mtime-sorted glob — use for \"what changed lately\", \"all *.ts under src/\"), \\`search_content\\` (content grep — use for \"where is X called\", \"find all references to Y\"; pass \\`context:N\\` for grep -C N around hits), \\`get_file_info\\`. Don't call \\`grep\\` or other tools that aren't in this list — they don't exist as functions.\n\n# Path conventions\n\nTwo different rules depending on which tool:\n\n- **Filesystem tools** (\\`read_file\\`, \\`list_directory\\`, \\`search_files\\`, \\`edit_file\\`, etc.): paths resolve against the sandbox root. Relative (\\`src/foo.ts\\`), POSIX-absolute (\\`/src/foo.ts\\`, where \\`/\\` means the project root), and OS-absolute including Windows drive-letter (\\`D:\\\\\\\\path\\\\\\\\foo.cpp\\`) all work — anything that resolves INSIDE the sandbox is readable, regardless of the path shape. When the user pastes a path, your default move is to call \\`read_file\\` on it as-is. The tool returns a clear \"path escapes sandbox\" error (with a relaunch hint) if it's actually out of scope; refusing on path shape alone, claiming \"I can't access the filesystem\", or falling back to \\`web_search\\` for a local file are all wrong — you have filesystem tools, use them.\n- **\\`run_command\\`**: the command runs in a real OS shell with cwd pinned to the project root. Paths inside the shell command are interpreted by THAT shell, not by us. **Never use leading \\`/\\` in run_command arguments** — Windows treats \\`/tests\\` as drive-root \\`F:\\\\tests\\` (non-existent), POSIX shells treat it as filesystem root. Use plain relative paths (\\`tests\\`, \\`./tests\\`, \\`src/loop.ts\\`) instead.\n\n# When the user wants to switch project / working directory\n\nYou can't. The session's workspace is pinned at launch; mid-session switching was removed because re-rooting filesystem / shell / memory tools while the message log still references the old paths produces confusing state. Tell the user to quit and relaunch with the new directory (e.g. \\`cd ../other-project && luckerr code\\`).\n\nDo NOT try to switch via \\`run_command\\` (\\`cd\\`, \\`pushd\\`, etc.) — your tool sandbox is pinned and \\`cd\\` inside one shell call doesn't carry to the next.\n\n# Foreground vs. background commands\n\nYou have TWO tools for running shell commands, and picking the right one is non-negotiable:\n\n- \\`run_command\\` — blocks until the process exits. Use for: **tests, builds, lints, typechecks, git operations, one-shot scripts**. Anything that naturally returns in under a minute.\n- \\`run_background\\` — spawns and detaches after a brief startup window. Use for:\n - **Dev servers / watchers / anything with \"dev\" / \"serve\" / \"watch\" / \"start\" in the name.** Examples: \\`npm run dev\\`, \\`pnpm dev\\`, \\`yarn start\\`, \\`vite\\`, \\`next dev\\`, \\`uvicorn app:app --reload\\`, \\`flask run\\`, \\`python -m http.server\\`, \\`cargo watch\\`, \\`tsc --watch\\`, \\`webpack serve\\`.\n - **One-shot long jobs that would blow run_command's 60s ceiling.** Examples: \\`curl -L -O <big-url>\\`, \\`wget\\`, \\`huggingface-cli download\\`, multi-GB \\`pip install\\` / \\`npm install\\`, big \\`cargo build\\` / \\`docker build\\`. Start with \\`run_background\\`, then call \\`wait_for_job\\` ONCE with a long \\`timeoutMs\\` — that costs one tool call total, not one per poll.\n\n**Never use run_command for a dev server or a download likely to exceed a minute.** It will block, time out, and the user will see a frozen tool call while the work was actually running fine. Always \\`run_background\\` + \\`wait_for_job\\` / \\`job_output\\`.\n\nAfter \\`run_background\\`, tools available to you:\n- \\`job_output(jobId, tailLines?)\\` — read recent logs to verify startup / debug errors.\n- \\`wait_for_job(jobId, timeoutMs?, waitFor?)\\` — block server-side until the job finishes (or, with \\`waitFor: 'output-or-exit'\\`, until it writes a new line). ONE tool call per wait regardless of duration. \\`timeoutMs\\` clamps at 300_000. For downloads / installs / builds: leave \\`waitFor\\` at the default \\`'exit'\\` and set \\`timeoutMs\\` to the slowest reasonable end-to-end. For tailing a dev server and reacting to a specific log line: pass \\`waitFor: 'output-or-exit'\\` with a short \\`timeoutMs\\`.\n- \\`list_jobs\\` — see every job this session (running + exited).\n- \\`stop_job(jobId)\\` — SIGTERM → SIGKILL after grace. Stop before switching port / config.\n\nDon't re-start an already-running dev server — call \\`list_jobs\\` first when in doubt.\n\n# Scope discipline on \"run it\" / \"start it\" requests\n\nWhen the user's request is to **run / start / launch / serve / boot up** something, your job is ONLY:\n\n1. Start it (\\`run_background\\` for dev servers, \\`run_command\\` for one-shots).\n2. Verify it came up (read a ready signal via \\`job_output\\`, or fetch the URL with \\`web_fetch\\` if they want you to confirm).\n3. Report what's running, where (URL / port / pid), and STOP.\n\nDo NOT, in the same turn:\n- Run \\`tsc\\` / type-checkers / linters unless the user asked for it.\n- Scan for bugs to \"proactively\" fix. The page rendering is success.\n- Clean up unused imports, dead code, or refactor \"while you're here.\"\n- Edit files to improve anything the user didn't mention.\n\nIf you notice an obvious issue, MENTION it in one sentence and wait for the user to say \"fix it.\" The cost of over-eagerness is real: you burn tokens, make surprise edits the user didn't want, and chain into cascading \"fix the new error I just introduced\" loops. The storm-breaker will cut you off, but the user still sees the mess.\n\n\"It works\" is the end state. Resist the urge to polish.\n\n# Style\n\n- Show edits; don't narrate them in prose. \"Here's the fix:\" is enough.\n- One short paragraph explaining *why*, then the blocks.\n- If you need to explore first (list / read / search), do it with tool calls before writing any prose — silence while exploring is fine.\n\n__ESCALATION_CONTRACT__\n\n${TUI_FORMATTING_RULES}\n`;\n\n/** Backward-compat — public-API const, frozen at the historical flash phrasing. Internal callers use codeSystemPrompt(rootDir, { modelId }) so the contract names the real tier (#582). */\nexport const CODE_SYSTEM_PROMPT = codeSystemBase(DEFAULT_CODE_MODEL);\n\n/** Stack order (stable for cache prefix): base → LUCKERR.md → global → project → .gitignore. */\nconst SEMANTIC_SEARCH_ROUTING = `\n\n# Search routing\n\nYou have BOTH \\`semantic_search\\` (vector index) and \\`search_content\\` (literal grep).\n\n- **Descriptive queries** (\"where do we handle X\", \"which file owns Y\", \"how does Z work\", \"find the logic that does …\", \"the code responsible for …\") → call \\`semantic_search\\` FIRST. It indexes the project by meaning, so it finds the right file even when your phrasing shares no tokens with the code.\n- **Exact-token queries** (a specific identifier, regex, or \"find every call to foo\") → call \\`search_content\\`.\n\nIf \\`semantic_search\\` returns nothing useful (low scores, off-topic), THEN fall back to \\`search_content\\`. Don't go the other way — grepping a paraphrased question wastes turns.`;\n\nexport interface CodeSystemPromptOptions {\n /** True when semantic_search is registered for this run. Adds an\n * explicit routing fragment so the model picks it for intent-style\n * queries instead of defaulting to grep. */\n hasSemanticSearch?: boolean;\n /** Inline string appended after the generated code system prompt.\n * Preserves the default prompt — this is append-only, not a replacement. */\n systemAppend?: string;\n /** UTF-8 file contents appended after the generated code system prompt.\n * Preserves the default prompt — this is append-only, not a replacement. */\n systemAppendFile?: string;\n /** Model the loop will run on — interpolated into the escalation contract so the model can name itself correctly when asked (#582). */\n modelId?: string;\n}\n\nexport function codeSystemPrompt(rootDir: string, opts: CodeSystemPromptOptions = {}): string {\n const codeBase = codeSystemBase(opts.modelId ?? DEFAULT_CODE_MODEL);\n const base = opts.hasSemanticSearch ? `${codeBase}${SEMANTIC_SEARCH_ROUTING}` : codeBase;\n const withMemory = applyMemoryStack(base, rootDir);\n const gitignorePath = join(rootDir, \".gitignore\");\n let result = withMemory;\n if (existsSync(gitignorePath)) {\n let content: string | undefined;\n try {\n content = readFileSync(gitignorePath, \"utf8\");\n } catch {}\n if (content !== undefined) {\n const MAX = 2000;\n const truncated =\n content.length > MAX\n ? `${content.slice(0, MAX)}\\n… (truncated ${content.length - MAX} chars)`\n : content;\n result = `${result}\\n\\n# Project .gitignore\\n\\nThe user's repo ships this .gitignore — treat every pattern as \"don't traverse or edit inside these paths unless explicitly asked\":\\n\\n\\`\\`\\`\\n${truncated}\\n\\`\\`\\`\\n`;\n }\n }\n const appendParts = [opts.systemAppend, opts.systemAppendFile].filter(Boolean);\n if (appendParts.length > 0) {\n result = `${result}\\n\\n# User System Append\\n\\n${appendParts.join(\"\\n\\n\")}`;\n }\n return result;\n}\n","/** Append-only JSONL of per-turn tokens + cost; best-effort writes, never blocks the turn. No prompts/completions logged. */\n\nimport {\n appendFileSync,\n closeSync,\n existsSync,\n fstatSync,\n mkdirSync,\n openSync,\n readFileSync,\n readSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { Usage } from \"../client.js\";\nimport {\n CLAUDE_SONNET_PRICING,\n DEEPSEEK_PRICING,\n cacheSavingsUsd,\n claudeEquivalentCost,\n costUsd,\n} from \"./stats.js\";\n\n/** One turn's snapshot — serialized verbatim as a JSONL line. */\nexport interface UsageRecord {\n /** Epoch millis when the record was written. */\n ts: number;\n /** Session name if the turn ran inside a persisted session, `null` for ephemeral. */\n session: string | null;\n /** Model id the turn ran against (drives the pricing lookup). */\n model: string;\n promptTokens: number;\n completionTokens: number;\n cacheHitTokens: number;\n cacheMissTokens: number;\n /** Total cost of the turn in USD. */\n costUsd: number;\n /** What the same turn would have cost at Claude Sonnet 4.6 rates. */\n claudeEquivUsd: number;\n /** Absent on legacy records — treat as \"turn\" when missing. */\n kind?: \"turn\" | \"subagent\";\n /** Present when `kind === \"subagent\"`. Attribution metadata for the /stats roll-up. */\n subagent?: {\n /** Skill that spawned it, when the spawn came from a `runAs: subagent` skill. */\n skillName?: string;\n /** First ~60 chars of the task prompt — enough context to recognize a run, never the full text. */\n taskPreview: string;\n /** Tool calls the child loop dispatched before returning. */\n toolIters: number;\n /** Wall-clock ms. */\n durationMs: number;\n };\n}\n\n/** Where the log lives. Tests override via `opts.path`. */\nexport function defaultUsageLogPath(homeDirOverride?: string): string {\n return join(homeDirOverride ?? homedir(), \".luckerr\", \"usage.jsonl\");\n}\n\nexport interface AppendUsageInput {\n session: string | null;\n model: string;\n usage: Usage;\n /** Override the timestamp (tests). */\n now?: number;\n /** Override the log path (tests). */\n path?: string;\n /** When appending a subagent summary row, set `kind: \"subagent\"` and populate `subagent`. */\n kind?: \"turn\" | \"subagent\";\n subagent?: UsageRecord[\"subagent\"];\n}\n\nconst USAGE_COMPACTION_THRESHOLD_BYTES = 5 * 1024 * 1024;\nconst USAGE_RETENTION_DAYS = 365;\n\nfunction compactUsageLogIfLarge(path: string, now: number): void {\n // Open once for the size check + read so they bind to the same fd\n // (CodeQL js/file-system-race). Concurrent appenders that grow the\n // log between check and read can no longer cause us to act on a\n // stale size and rewrite based on partial content.\n let raw: string;\n try {\n const fd = openSync(path, \"r\");\n try {\n const stat = fstatSync(fd);\n if (stat.size < USAGE_COMPACTION_THRESHOLD_BYTES) return;\n const buf = Buffer.alloc(stat.size);\n let read = 0;\n while (read < stat.size) {\n const n = readSync(fd, buf, read, stat.size - read, read);\n if (n <= 0) break;\n read += n;\n }\n raw = buf.toString(\"utf8\", 0, read);\n } finally {\n closeSync(fd);\n }\n } catch {\n return;\n }\n const cutoff = now - USAGE_RETENTION_DAYS * 24 * 60 * 60 * 1000;\n const lines = raw.split(/\\r?\\n/);\n const kept: string[] = [];\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const rec = JSON.parse(line);\n if (isValidRecord(rec) && rec.ts >= cutoff) kept.push(line);\n } catch {\n /* skip malformed */\n }\n }\n // No-op when nothing aged out — avoids rewrite storms on fresh logs.\n if (kept.length === lines.filter((l) => l.trim()).length) return;\n // Write to a sibling tmp path then rename — atomic from a reader's\n // POV and severs CodeQL's stat→write taint chain. Concurrent\n // appenders during the compaction window lose their entries; we\n // accept that for a best-effort usage log.\n const tmp = `${path}.compacting`;\n try {\n writeFileSync(tmp, kept.length > 0 ? `${kept.join(\"\\n\")}\\n` : \"\", \"utf8\");\n renameSync(tmp, path);\n } catch {\n try {\n unlinkSync(tmp);\n } catch {\n /* tmp may not exist — ignore */\n }\n }\n}\n\n/** Returns the record so tests can assert cost fields without re-reading the log. */\nexport function appendUsage(input: AppendUsageInput): UsageRecord {\n const record: UsageRecord = {\n ts: input.now ?? Date.now(),\n session: input.session,\n model: input.model,\n promptTokens: input.usage.promptTokens,\n completionTokens: input.usage.completionTokens,\n cacheHitTokens: input.usage.promptCacheHitTokens,\n cacheMissTokens: input.usage.promptCacheMissTokens,\n costUsd: costUsd(input.model, input.usage),\n claudeEquivUsd: claudeEquivalentCost(input.usage),\n };\n if (input.kind === \"subagent\") record.kind = \"subagent\";\n if (input.subagent) record.subagent = input.subagent;\n\n const path = input.path ?? defaultUsageLogPath();\n try {\n mkdirSync(dirname(path), { recursive: true });\n appendFileSync(path, `${JSON.stringify(record)}\\n`, \"utf8\");\n compactUsageLogIfLarge(path, record.ts);\n } catch {\n /* best-effort — disk failure shouldn't break the chat */\n }\n return record;\n}\n\nexport function readUsageLog(path: string = defaultUsageLogPath()): UsageRecord[] {\n if (!existsSync(path)) return [];\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return [];\n }\n const out: UsageRecord[] = [];\n for (const line of raw.split(/\\r?\\n/)) {\n if (!line.trim()) continue;\n try {\n const rec = JSON.parse(line);\n if (isValidRecord(rec)) out.push(rec);\n } catch {\n /* skip malformed */\n }\n }\n return out;\n}\n\nfunction isValidRecord(rec: unknown): rec is UsageRecord {\n if (!rec || typeof rec !== \"object\") return false;\n const r = rec as Partial<UsageRecord>;\n return (\n typeof r.ts === \"number\" &&\n typeof r.model === \"string\" &&\n typeof r.promptTokens === \"number\" &&\n typeof r.completionTokens === \"number\" &&\n typeof r.cacheHitTokens === \"number\" &&\n typeof r.cacheMissTokens === \"number\" &&\n typeof r.costUsd === \"number\" &&\n typeof r.claudeEquivUsd === \"number\"\n );\n}\n\n/** One row of the `luckerr stats` dashboard — a rolled-up window. */\nexport interface UsageBucket {\n label: string;\n /** Start of the window as epoch millis. `0` = unbounded (all-time). */\n since: number;\n turns: number;\n promptTokens: number;\n completionTokens: number;\n cacheHitTokens: number;\n cacheMissTokens: number;\n costUsd: number;\n claudeEquivUsd: number;\n /** Recomputed from current pricing each aggregate — intentionally NOT frozen with `costUsd`. */\n cacheSavingsUsd: number;\n}\n\n/** Cache hit ratio for a bucket — zero denominator returns 0. */\nexport function bucketCacheHitRatio(b: UsageBucket): number {\n const denom = b.cacheHitTokens + b.cacheMissTokens;\n return denom > 0 ? b.cacheHitTokens / denom : 0;\n}\n\n/** Savings vs Claude as a fraction (0.94 = 94% savings). 0 if Claude cost is 0. */\nexport function bucketSavingsFraction(b: UsageBucket): number {\n return b.claudeEquivUsd > 0 ? 1 - b.costUsd / b.claudeEquivUsd : 0;\n}\n\nfunction emptyBucket(label: string, since: number): UsageBucket {\n return {\n label,\n since,\n turns: 0,\n promptTokens: 0,\n completionTokens: 0,\n cacheHitTokens: 0,\n cacheMissTokens: 0,\n costUsd: 0,\n claudeEquivUsd: 0,\n cacheSavingsUsd: 0,\n };\n}\n\nfunction addToBucket(b: UsageBucket, r: UsageRecord): void {\n b.turns += 1;\n b.promptTokens += r.promptTokens;\n b.completionTokens += r.completionTokens;\n b.cacheHitTokens += r.cacheHitTokens;\n b.cacheMissTokens += r.cacheMissTokens;\n b.costUsd += r.costUsd;\n b.claudeEquivUsd += r.claudeEquivUsd;\n b.cacheSavingsUsd += cacheSavingsUsd(r.model, r.cacheHitTokens);\n}\n\nexport interface AggregateOptions {\n /** Override `Date.now()` for deterministic tests. */\n now?: number;\n}\n\nexport interface UsageAggregate {\n /** Fixed-order rolling windows: today, week, month, all-time. */\n buckets: UsageBucket[];\n /** Model id → turn count. Sorted descending; top entry is the \"most used.\" */\n byModel: Array<{ model: string; turns: number }>;\n /** Session name → turn count. Sorted descending. Null sessions are grouped under `\"(ephemeral)\"`. */\n bySession: Array<{ session: string; turns: number }>;\n /** Earliest record's ts, or `null` when the log is empty. Drives \"saved $X since <date>\". */\n firstSeen: number | null;\n /** Latest record's ts, or `null` when the log is empty. */\n lastSeen: number | null;\n /** Undefined when no subagent records exist; counts spawns, not internal child-loop turns. */\n subagents?: SubagentAggregate;\n}\n\n/** Rolled-up view of all `kind: \"subagent\"` records. */\nexport interface SubagentAggregate {\n total: number;\n costUsd: number;\n totalDurationMs: number;\n /** Per-skill breakdown. Records without `skillName` (raw spawn_subagent calls) group under `\"(adhoc)\"`. */\n bySkill: Array<{ skillName: string; count: number; costUsd: number; durationMs: number }>;\n}\n\n/** Rolling 24h/7d/30d windows — avoids \"it's 00:03, 'today' is empty\" surprises. */\nexport function aggregateUsage(\n records: UsageRecord[],\n opts: AggregateOptions = {},\n): UsageAggregate {\n const now = opts.now ?? Date.now();\n const day = 24 * 60 * 60 * 1000;\n const today = emptyBucket(\"today\", now - day);\n const week = emptyBucket(\"week\", now - 7 * day);\n const month = emptyBucket(\"month\", now - 30 * day);\n const all = emptyBucket(\"all-time\", 0);\n\n const modelCounts = new Map<string, number>();\n const sessionCounts = new Map<string, number>();\n let firstSeen: number | null = null;\n let lastSeen: number | null = null;\n const skillCounts = new Map<string, { count: number; costUsd: number; durationMs: number }>();\n let subagentTotal = 0;\n let subagentCost = 0;\n let subagentDuration = 0;\n\n for (const r of records) {\n addToBucket(all, r);\n if (r.ts >= today.since) addToBucket(today, r);\n if (r.ts >= week.since) addToBucket(week, r);\n if (r.ts >= month.since) addToBucket(month, r);\n\n modelCounts.set(r.model, (modelCounts.get(r.model) ?? 0) + 1);\n const sessKey = r.session ?? \"(ephemeral)\";\n sessionCounts.set(sessKey, (sessionCounts.get(sessKey) ?? 0) + 1);\n\n if (firstSeen === null || r.ts < firstSeen) firstSeen = r.ts;\n if (lastSeen === null || r.ts > lastSeen) lastSeen = r.ts;\n\n if (r.kind === \"subagent\") {\n subagentTotal += 1;\n subagentCost += r.costUsd;\n const dur = r.subagent?.durationMs ?? 0;\n subagentDuration += dur;\n const key = r.subagent?.skillName?.trim() || \"(adhoc)\";\n const prev = skillCounts.get(key) ?? { count: 0, costUsd: 0, durationMs: 0 };\n prev.count += 1;\n prev.costUsd += r.costUsd;\n prev.durationMs += dur;\n skillCounts.set(key, prev);\n }\n }\n\n const byModel = Array.from(modelCounts.entries())\n .map(([model, turns]) => ({ model, turns }))\n .sort((a, b) => b.turns - a.turns);\n const bySession = Array.from(sessionCounts.entries())\n .map(([session, turns]) => ({ session, turns }))\n .sort((a, b) => b.turns - a.turns);\n\n const subagents: SubagentAggregate | undefined =\n subagentTotal > 0\n ? {\n total: subagentTotal,\n costUsd: subagentCost,\n totalDurationMs: subagentDuration,\n bySkill: Array.from(skillCounts.entries())\n .map(([skillName, v]) => ({ skillName, ...v }))\n .sort((a, b) => b.count - a.count),\n }\n : undefined;\n\n return {\n buckets: [today, week, month, all],\n byModel,\n bySession,\n firstSeen,\n lastSeen,\n subagents,\n };\n}\n\n/** File-size helper for the stats header — \"1.2 MB\" etc. Returns \"\" if missing. */\nexport function formatLogSize(path: string = defaultUsageLogPath()): string {\n if (!existsSync(path)) return \"\";\n try {\n const s = statSync(path);\n const bytes = s.size;\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n } catch {\n return \"\";\n }\n}\n\n/** Re-exports for downstream consumers that also want the pricing constants. */\nexport { CLAUDE_SONNET_PRICING, DEEPSEEK_PRICING };\n"],"mappings":";AAAA,SAAkC,oBAAoB;;;ACuBtD,IAAM,6BAA6B,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AAEhE,eAAsB,eACpB,SACA,KACA,MACA,OAAqB,CAAC,GACH;AACnB,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,UAAU,KAAK,oBAAoB;AACzC,QAAM,MAAM,KAAK,gBAAgB;AACjC,QAAM,YAAY,IAAI,IAAI,KAAK,qBAAqB,0BAA0B;AAE9E,MAAI;AAEJ,WAAS,UAAU,GAAG,UAAU,aAAa,WAAW;AACtD,QAAI,KAAK,QAAQ,QAAS,OAAM,IAAI,MAAM,SAAS;AAEnD,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,KAAK,IAAI;AAGpC,UAAI,KAAK,MAAM,CAAC,UAAU,IAAI,KAAK,MAAM,EAAG,QAAO;AAInD,UAAI,YAAY,cAAc,EAAG,QAAO;AAGxC,YAAM,KAAK,KAAK,EAAE,MAAM,MAAM,MAAS;AAEvC,YAAM,SAAS,YAAY,SAAS,SAAS,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC;AACjF,WAAK,UAAU,EAAE,SAAS,UAAU,GAAG,QAAQ,QAAQ,KAAK,MAAM,IAAI,OAAO,CAAC;AAC9E,YAAM,MAAM,QAAQ,KAAK,MAAM;AAAA,IACjC,SAAS,KAAK;AACZ,kBAAY;AAEZ,UAAI,aAAa,GAAG,KAAK,KAAK,QAAQ,QAAS,OAAM;AACrD,UAAI,YAAY,cAAc,EAAG,OAAM;AAEvC,YAAM,SAAS,YAAY,SAAS,SAAS,KAAK,IAAI;AACtD,WAAK,UAAU;AAAA,QACb,SAAS,UAAU;AAAA,QACnB,QAAQ,YAAY,UAAU,GAAG,CAAC;AAAA,QAClC;AAAA,MACF,CAAC;AACD,YAAM,MAAM,QAAQ,KAAK,MAAM;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,aAAa,IAAI,MAAM,0CAA0C;AACzE;AAEA,SAAS,YACP,SACA,SACA,KACA,YACQ;AACR,MAAI,YAAY;AACd,UAAM,UAAU,OAAO,WAAW,UAAU;AAC5C,QAAI,OAAO,SAAS,OAAO,KAAK,UAAU,GAAG;AAC3C,aAAO,KAAK,IAAI,UAAU,KAAM,GAAG;AAAA,IACrC;AAAA,EACF;AACA,QAAM,MAAM,UAAU,KAAK;AAE3B,QAAM,SAAS,OAAO,OAAO,KAAK,OAAO,IAAI;AAC7C,SAAO,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC,GAAG,GAAG;AAC1C;AAEA,SAAS,MAAM,IAAY,QAAqC;AAC9D,MAAI,MAAM,EAAG,QAAO,QAAQ,QAAQ;AACpC,SAAO,IAAI,QAAQ,CAACA,WAAS,WAAW;AACtC,UAAM,QAAQ,WAAWA,WAAS,EAAE;AACpC,QAAI,QAAQ;AACV,YAAM,UAAU,MAAM;AACpB,qBAAa,KAAK;AAClB,eAAO,IAAI,MAAM,SAAS,CAAC;AAAA,MAC7B;AACA,UAAI,OAAO,QAAS,SAAQ;AAAA,UACvB,QAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,IAC/D;AAAA,EACF,CAAC;AACH;AAEA,SAAS,aAAa,KAAuB;AAC3C,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,OAAQ,IAA2B;AACzC,SAAO,SAAS;AAClB;AAEA,SAAS,UAAU,KAAsB;AACvC,MAAI,eAAe,MAAO,QAAO,IAAI;AACrC,MAAI;AACF,WAAO,OAAO,GAAG;AAAA,EACnB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACvHO,IAAM,WAA4B;AAAA,EACvC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,qBAAqB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAClF,mBAAqB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAClF,iBAAqB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAClF,qBAAqB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,IACpF;AAAA,IACA,UAAU,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,EACzE;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB,CAAC,qBAAqB,mBAAmB,mBAAmB;AAAA,IAC5E,gBAAgB;AAAA,EAClB;AAAA,EACA,eAAe;AAAA,IACb,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,EACvB;AAAA,EACA,sBAAsB;AACxB;;;AC1CO,IAAM,cAA+B;AAAA,EAC1C,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,IACP,QAAQ;AAAA;AAAA,MAEN,2BAA2B,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MACxF,2BAA2B,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA;AAAA,MAExF,6BAA6B,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC1F,6BAA6B,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC1F,6BAA6B,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC1F,4BAA6B,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA;AAAA,MAE1F,qCAAqC,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAClG,oCAAqC,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAClG,8BAA8B,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA;AAAA,MAE3F,uBAAuB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,IACtF;AAAA,IACA,UAAU,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,EACzE;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AAAA,EACA,sBAAsB;AACxB;;;AClDO,IAAM,aAA8B;AAAA,EACzC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,IACP,UAAU,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,EAAE;AAAA,EAC7D;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AAAA,EACA,sBAAsB;AACxB;;;AC/BO,IAAM,QAAyB;AAAA,EACpC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,cAAc,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC3E,eAAe,EAAE,eAAe,GAAO,gBAAgB,GAAO,QAAQ,EAAM;AAAA,MAC5E,aAAe,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC5E,cAAe,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC5E,SAAe,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC5E,eAAe,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC5E,gBAAe,EAAE,eAAe,GAAO,gBAAgB,GAAO,QAAQ,EAAM;AAAA,MAC5E,UAAe,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,IAC9E;AAAA,IACA,UAAU,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,EACzE;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AAAA,EACA,eAAe;AAAA,IACb,cAAc;AAAA,IACd,eAAe;AAAA,IACf,aAAa;AAAA,IACb,cAAc;AAAA,IACd,SAAS;AAAA,IACT,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,UAAU;AAAA,EACZ;AAAA,EACA,sBAAsB;AACxB;;;ACpDO,IAAM,WAA4B;AAAA,EACvC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,kBAAoB,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,EAAE;AAAA,MACrE,mBAAoB,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,EAAE;AAAA,MACrE,oBAAoB,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,EAAE;AAAA,MACrE,eAAoB,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,EAAE;AAAA,IACvE;AAAA,IACA,UAAU,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,EAAE;AAAA,EAC7D;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AAAA,EACA,eAAe;AAAA,IACb,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,eAAe;AAAA,EACjB;AAAA,EACA,sBAAsB;AACxB;;;ACxCO,IAAM,OAAwB;AAAA,EACnC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,YAAc,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC3E,aAAc,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC3E,cAAc,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC3E,aAAc,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MAC3E,wBAAwB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MACrF,wBAAwB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MACrF,wBAAwB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MACrF,uBAAwB,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,MACrF,YAAc,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,IAC7E;AAAA,IACA,UAAU,EAAE,eAAe,OAAO,gBAAgB,OAAO,QAAQ,MAAM;AAAA,EACzE;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AAAA,EACA,eAAe;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,aAAa;AAAA,IACb,YAAY;AAAA,EACd;AAAA,EACA,sBAAsB;AACxB;;;ACnDO,IAAM,SAA0B;AAAA,EACrC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,SAAe,EAAE,eAAe,OAAO,gBAAgB,KAAM,QAAQ,EAAK;AAAA,MAC1E,cAAe,EAAE,eAAe,OAAO,gBAAgB,KAAM,QAAQ,IAAK;AAAA,MAC1E,cAAe,EAAE,eAAe,OAAO,gBAAgB,KAAM,QAAQ,IAAK;AAAA,MAC1E,WAAe,EAAE,eAAe,KAAO,gBAAgB,GAAM,QAAQ,EAAK;AAAA,MAC1E,gBAAe,EAAE,eAAe,KAAO,gBAAgB,KAAM,QAAQ,IAAK;AAAA,MAC1E,gBAAe,EAAE,eAAe,OAAO,gBAAgB,KAAM,QAAQ,IAAK;AAAA,MAC1E,WAAe,EAAE,eAAe,OAAO,gBAAgB,KAAM,QAAQ,IAAK;AAAA,MAC1E,MAAe,EAAE,eAAe,MAAO,gBAAgB,GAAM,QAAQ,GAAM;AAAA,MAC3E,WAAe,EAAE,eAAe,OAAO,gBAAgB,KAAM,QAAQ,IAAK;AAAA,IAC5E;AAAA,IACA,UAAU,EAAE,eAAe,KAAM,gBAAgB,KAAM,QAAQ,IAAK;AAAA,EACtE;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB,CAAC,WAAW,MAAM,WAAW,OAAO;AAAA,IACpD,gBAAgB;AAAA,EAClB;AAAA,EACA,eAAe;AAAA,IACb,SAAS;AAAA,IACT,cAAc;AAAA,IACd,cAAc;AAAA,IACd,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA,EACA,sBAAsB;AACxB;;;ACxDO,IAAM,YAA6B;AAAA,EACxC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,4BAA4B,EAAE,eAAe,KAAM,gBAAgB,GAAM,QAAQ,GAAM;AAAA,MACvF,0BAA4B,EAAE,eAAe,KAAM,gBAAgB,IAAO,QAAQ,GAAM;AAAA,MACxF,6BAA4B,EAAE,eAAe,MAAM,gBAAgB,KAAM,QAAQ,EAAK;AAAA,IACxF;AAAA,IACA,UAAU,EAAE,eAAe,KAAM,gBAAgB,GAAM,QAAQ,GAAM;AAAA,EACvE;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,MACd;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,EAClB;AAAA,EACA,eAAe;AAAA,IACb,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,6BAA6B;AAAA,EAC/B;AAAA,EACA,sBAAsB;AAAA,EACtB,cAAc;AAAA,IACZ,qBAAqB;AAAA,EACvB;AACF;;;AC5CO,IAAM,OAAwB;AAAA,EACnC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,2BAA2B,EAAE,eAAe,GAAG,gBAAgB,MAAM,QAAQ,KAAK;AAAA,MAClF,wBAA2B,EAAE,eAAe,GAAG,gBAAgB,MAAM,QAAQ,KAAK;AAAA,MAClF,sBAA2B,EAAE,eAAe,GAAG,gBAAgB,MAAM,QAAQ,KAAK;AAAA,MAClF,gBAA2B,EAAE,eAAe,GAAG,gBAAgB,MAAM,QAAQ,KAAK;AAAA,IACpF;AAAA,IACA,UAAU,EAAE,eAAe,GAAG,gBAAgB,MAAM,QAAQ,KAAK;AAAA,EACnE;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AAAA,EACA,sBAAsB;AACxB;;;AClCO,IAAM,aAA8B;AAAA,EACzC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA;AAAA;AAAA,IAGP,UAAU,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,EAAE;AAAA,EAC7D;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AAAA,EACA,sBAAsB;AAAA,EACtB,cAAc;AAAA,IACZ,gBAAgB;AAAA,IAChB,WAAW;AAAA,EACb;AACF;;;AC1BA,IAAM,YAAY,oBAAI,IAA6B;AAGnD,IAAM,kBAAkB,oBAAI,IAAoB;AAEhD,SAAS,SAAS,GAA0B;AAC1C,MAAI,UAAU,IAAI,EAAE,EAAE,GAAG;AACvB,UAAM,IAAI,MAAM,0BAA0B,EAAE,EAAE,EAAE;AAAA,EAClD;AACA,YAAU,IAAI,EAAE,IAAI,CAAC;AACrB,aAAW,OAAO,EAAE,QAAQ;AAC1B,QAAI,gBAAgB,IAAI,GAAG,EAAG;AAC9B,oBAAgB,IAAI,KAAK,EAAE,EAAE;AAAA,EAC/B;AACF;AAGA,SAAS,QAAQ;AACjB,SAAS,WAAW;AACpB,SAAS,UAAU;AACnB,SAAS,KAAK;AACd,SAAS,QAAQ;AACjB,SAAS,IAAI;AACb,SAAS,MAAM;AACf,SAAS,SAAS;AAClB,SAAS,IAAI;AACb,SAAS,UAAU;AAGZ,SAAS,iBAAiB,SAAgC;AAC/D,WAAS,OAAO;AAClB;AAGO,SAAS,mBAAmB,IAAqB;AACtD,QAAM,IAAI,UAAU,IAAI,EAAE;AAC1B,MAAI,CAAC,EAAG,QAAO;AACf,aAAW,OAAO,EAAE,QAAQ;AAC1B,QAAI,gBAAgB,IAAI,GAAG,MAAM,GAAI,iBAAgB,OAAO,GAAG;AAAA,EACjE;AACA,SAAO,UAAU,OAAO,EAAE;AAC5B;AAGO,SAAS,YAAY,IAAyC;AACnE,SAAO,UAAU,IAAI,EAAE;AACzB;AAGO,SAAS,gBAAmC;AACjD,SAAO,CAAC,GAAG,UAAU,OAAO,CAAC;AAC/B;AAOO,SAAS,gBAAgB,SAA8C;AAC5E,QAAM,MAAM,gBAAgB,IAAI,OAAO;AACvC,MAAI,IAAK,QAAO,UAAU,IAAI,GAAG;AAGjC,aAAW,CAAC,QAAQ,UAAU,KAAK,YAAY;AAC7C,QAAI,QAAQ,WAAW,MAAM,EAAG,QAAO,UAAU,IAAI,UAAU;AAAA,EACjE;AAEA,SAAO;AACT;AAEA,IAAM,aAAiC;AAAA,EACrC,CAAC,WAAW,QAAQ;AAAA,EACpB,CAAC,cAAc,WAAW;AAAA,EAC1B,CAAC,aAAa,UAAU;AAAA,EACxB,CAAC,gBAAgB,aAAa;AAAA,EAC9B,CAAC,SAAS,aAAa;AAAA,EACvB,CAAC,eAAe,aAAa;AAAA,EAC7B,CAAC,UAAU,aAAa;AAAA,EACxB,CAAC,QAAQ,aAAa;AAAA,EACtB,CAAC,WAAW,YAAY;AAAA,EACxB,CAAC,YAAY,YAAY;AAAA,EACzB,CAAC,WAAW,YAAY;AAAA,EACxB,CAAC,cAAc,YAAY;AAAA,EAC3B,CAAC,WAAW,YAAY;AAAA,EACxB,CAAC,WAAW,YAAY;AAAA,EACxB,CAAC,SAAS,YAAY;AACxB;AAGO,SAAS,kBAAmC;AACjD,SAAO;AACT;AAMO,SAAS,WAAW,SAIzB;AACA,QAAM,WAAW,gBAAgB,OAAO;AACxC,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,SAAS,QAAQ,SAAS,OAAO,KAAK,SAAS,QAAQ;AAChE;AAEA,IAAM,eAAe,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,EAAE;AAG/D,SAAS,iBAAiB,SAAyB;AACxD,QAAM,WAAW,gBAAgB,OAAO;AACxC,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,SAAS,gBAAgB,OAAO,KAAK,SAAS;AACvD;;;AZ5HO,IAAM,QAAN,MAAM,OAAM;AAAA,EACjB,YACS,eAAe,GACf,mBAAmB,GACnB,cAAc,GACd,uBAAuB,GACvB,wBAAwB,GAC/B;AALO;AACA;AACA;AACA;AACA;AAAA,EACN;AAAA,EALM;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGT,IAAI,gBAAwB;AAC1B,UAAM,QAAQ,KAAK,uBAAuB,KAAK;AAC/C,WAAO,QAAQ,IAAI,KAAK,uBAAuB,QAAQ;AAAA,EACzD;AAAA,EAEA,OAAO,QAAQ,KAAyC;AACtD,UAAM,IAAI,OAAO,CAAC;AAClB,WAAO,IAAI;AAAA,MACT,EAAE,iBAAiB;AAAA,MACnB,EAAE,qBAAqB;AAAA,MACvB,EAAE,gBAAgB;AAAA,MAClB,EAAE,2BAA2B;AAAA,MAC7B,EAAE,4BAA4B;AAAA,IAChC;AAAA,EACF;AACF;AA+FO,IAAM,qBAAN,MAAyB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACQ;AAAA,EAEjB,YAAY,OAAkC,CAAC,GAAG;AAEhD,QAAI,WAAwC,KAAK;AACjD,QAAI,CAAC,YAAY,KAAK,OAAO;AAC3B,iBAAW,gBAAgB,KAAK,KAAK;AAAA,IACvC;AACA,QAAI,CAAC,UAAU;AACb,iBAAW,gBAAgB;AAAA,IAC7B;AACA,SAAK,WAAW;AAMhB,UAAM,SACJ,KAAK,UACL,QAAQ,IAAI,SAAS,KAAK,MAAM,KAChC,QAAQ,IAAI;AAEd,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,GAAG,SAAS,KAAK,MAAM;AAAA,MACzB;AAAA,IACF;AACA,SAAK,SAAS;AAMd,QAAI,MAAM,KAAK,WAAW,QAAQ,IAAI,qBAAqB,SAAS,UAAU;AAC9E,WAAO,IAAI,SAAS,GAAG,EAAG,OAAM,IAAI,MAAM,GAAG,EAAE;AAC/C,SAAK,UAAU;AAEf,SAAK,YAAY,KAAK,aAAa;AACnC,SAAK,SAAS,KAAK,SAAS,WAAW,MAAM,KAAK,UAAU;AAC5D,SAAK,QAAQ,KAAK,SAAS,CAAC;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAMQ,aAAqC;AAC3C,UAAM,OAAO,KAAK,SAAS;AAC3B,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,QAAQ,SAAS,GAAG,MAAM,GAAG,KAAK,MAAM,KAAK,KAAK;AAExD,UAAM,UAAkC,EAAE,CAAC,MAAM,GAAG,MAAM;AAG1D,QAAI,KAAK,SAAS,cAAc;AAC9B,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,SAAS,YAAY,GAAG;AAC/D,gBAAQ,CAAC,IAAI;AAAA,MACf;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMQ,aAAa,MAA0B,QAA0C;AACvF,UAAM,UAAmC;AAAA,MACvC,OAAO,KAAK;AAAA,MACZ,UAAU,KAAK;AAAA,MACf;AAAA,IACF;AACA,QAAI,KAAK,OAAO,OAAQ,SAAQ,QAAQ,KAAK;AAC7C,QAAI,KAAK,gBAAgB,OAAW,SAAQ,cAAc,KAAK;AAC/D,QAAI,KAAK,cAAc,OAAW,SAAQ,aAAa,KAAK;AAC5D,QAAI,KAAK,eAAgB,SAAQ,kBAAkB,KAAK;AAMxD,UAAM,WAAW,KAAK,SAAS;AAC/B,UAAM,wBACJ,CAAC,SAAS,kBAAkB,SAAS,eAAe,SAAS,KAAK,KAAK;AAEzE,QACE,SAAS,cAAc,gBACvB,KAAK,aACJ,yBAAyB,KAAK,aAAa,aAC5C;AAEA,cAAQ,aAAa;AAAA,QACnB,GAAK,QAAQ,cAA0C,CAAC;AAAA,QACxD,UAAU,EAAE,MAAM,KAAK,SAAS;AAAA,MAClC;AAAA,IACF;AACA,QAAI,SAAS,cAAc,sBAAsB,KAAK,mBAAmB,uBAAuB;AAE9F,cAAQ,mBAAmB,KAAK;AAAA,IAClC;AACA,QAAI,SAAS,cAAc,aAAa,KAAK,aAAa,aAAa,uBAAuB;AAE5F,cAAQ,WAAW,EAAE,MAAM,WAAW,eAAe,KAAM;AAAA,IAC7D;AAGA,QAAI,KAAK,mBAAmB,SAAS,cAAc,oBAAoB;AACrE,cAAQ,mBAAmB,KAAK;AAAA,IAClC;AAGA,QAAI,KAAK,SAAS,WAAW;AAC3B,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,SAAS,SAAS,GAAG;AAC5D,YAAI,EAAE,KAAK,SAAU,SAAQ,CAAC,IAAI;AAAA,MACpC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,OAAiC,CAAC,GAAgC;AACjF,UAAM,KAAK,KAAK,SAAS,UAAU;AACnC,QAAI,CAAC,GAAI,QAAO;AAEhB,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,OAAO,GAAG,KAAK,OAAO,GAAG,EAAE,IAAI;AAAA,QACrD,QAAQ;AAAA,QACR,SAAS,KAAK,WAAW;AAAA,QACzB,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,UAAI,CAAC,KAAK,GAAI,QAAO;AACrB,YAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,UAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,aAAa,EAAG,QAAO;AACxD,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,OAAiC,CAAC,GAA8B;AAC/E,UAAM,KAAK,KAAK,SAAS,UAAU;AACnC,QAAI,CAAC,GAAI,QAAO;AAEhB,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,OAAO,GAAG,KAAK,OAAO,GAAG,EAAE,IAAI;AAAA,QACrD,QAAQ;AAAA,QACR,SAAS,KAAK,WAAW;AAAA,QACzB,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,UAAI,CAAC,KAAK,GAAI,QAAO;AACrB,YAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,UAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAI,EAAG,QAAO;AAC/C,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAK,MAAiD;AAC1D,UAAM,OAAO,IAAI,gBAAgB;AACjC,UAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,SAAS;AAC3D,UAAM,SAAS,KAAK,UAAU,KAAK;AAEnC,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,KAAK;AAAA,QACL,GAAG,KAAK,OAAO;AAAA,QACf;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,GAAG,KAAK,WAAW;AAAA,YACnB,gBAAgB;AAAA,UAClB;AAAA,UACA,MAAM,KAAK,UAAU,KAAK,aAAa,MAAM,KAAK,CAAC;AAAA,UACnD;AAAA,QACF;AAAA,QACA,EAAE,GAAG,KAAK,OAAO,OAAO;AAAA,MAC1B;AACA,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,IAAI;AAAA,UACR,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK,MAAM,KAAK,MAAM,KAAK,KAAK,CAAC;AAAA,QAC7D;AAAA,MACF;AACA,YAAM,OAAY,MAAM,KAAK,KAAK;AAClC,YAAM,SAAS,KAAK,UAAU,CAAC,GAAG,WAAW,CAAC;AAC9C,aAAO;AAAA,QACL,SAAS,OAAO,WAAW;AAAA,QAC3B,kBAAkB,OAAO,qBAAqB;AAAA,QAC9C,WAAW,OAAO,cAAc,CAAC;AAAA,QACjC,OAAO,MAAM,QAAQ,KAAK,KAAK;AAAA,QAC/B,KAAK;AAAA,MACP;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,OAAO,MAAuD;AACnE,UAAM,OAAO,IAAI,gBAAgB;AACjC,UAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,SAAS;AAC3D,UAAM,SAAS,KAAK,UAAU,KAAK;AAEnC,QAAI;AACJ,QAAI;AACF,aAAO,MAAM;AAAA,QACX,KAAK;AAAA,QACL,GAAG,KAAK,OAAO;AAAA,QACf;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,GAAG,KAAK,WAAW;AAAA,YACnB,gBAAgB;AAAA,YAChB,QAAQ;AAAA,UACV;AAAA,UACA,MAAM,KAAK,UAAU,KAAK,aAAa,MAAM,IAAI,CAAC;AAAA,UAClD;AAAA,QACF;AAAA,QACA,EAAE,GAAG,KAAK,OAAO,OAAO;AAAA,MAC1B;AAAA,IACF,SAAS,KAAK;AACZ,mBAAa,KAAK;AAClB,YAAM;AAAA,IACR;AACA,QAAI,CAAC,KAAK,MAAM,CAAC,KAAK,MAAM;AAC1B,mBAAa,KAAK;AAClB,YAAM,IAAI;AAAA,QACR,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK,MAAM,KAAK,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,EAAE,CAAC;AAAA,MAC7E;AAAA,IACF;AAEA,UAAM,QAAuB,CAAC;AAC9B,QAAI,OAAO;AACX,UAAM,SAAS,aAAa;AAAA,MAC1B,SAAS,CAAC,OAA2B;AACnC,YAAI,CAAC,GAAG,QAAQ,GAAG,SAAS,UAAU;AACpC,iBAAO;AACP;AAAA,QACF;AACA,YAAI;AACF,gBAAM,OAAO,KAAK,MAAM,GAAG,IAAI;AAC/B,gBAAM,QAAQ,KAAK,UAAU,CAAC,GAAG,SAAS,CAAC;AAC3C,gBAAM,eAAe,KAAK,UAAU,CAAC,GAAG,iBAAiB;AACzD,gBAAM,QAAqB,EAAE,KAAK,MAAM,aAAa;AACrD,cAAI,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,SAAS,GAAG;AACjE,kBAAM,eAAe,MAAM;AAAA,UAC7B;AACA,cACE,OAAO,MAAM,sBAAsB,YACnC,MAAM,kBAAkB,SAAS,GACjC;AACA,kBAAM,iBAAiB,MAAM;AAAA,UAC/B;AACA,cAAI,MAAM,QAAQ,MAAM,UAAU,KAAK,MAAM,WAAW,SAAS,GAAG;AAClE,kBAAM,KAAK,MAAM,WAAW,CAAC;AAC7B,kBAAM,gBAAgB;AAAA,cACpB,OAAO,GAAG,SAAS;AAAA,cACnB,IAAI,GAAG;AAAA,cACP,MAAM,GAAG,UAAU;AAAA,cACnB,gBAAgB,GAAG,UAAU;AAAA,YAC/B;AAAA,UACF;AACA,cAAI,KAAK,OAAO;AACd,kBAAM,QAAQ,MAAM,QAAQ,KAAK,KAAK;AAAA,UACxC;AACA,gBAAM,KAAK,KAAK;AAAA,QAClB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,SAAS,KAAK,KAAK,UAAU;AACnC,UAAM,UAAU,IAAI,YAAY;AAChC,QAAI;AACF,aAAO,MAAM;AACX,YAAI,MAAM,SAAS,GAAG;AACpB,gBAAM,MAAM,MAAM;AAClB;AAAA,QACF;AACA,YAAI,KAAM;AACV,cAAM,EAAE,OAAO,MAAM,WAAW,IAAI,MAAM,OAAO,KAAK;AACtD,YAAI,WAAY;AAChB,eAAO,KAAK,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC;AAAA,MACrD;AACA,aAAO,MAAM,SAAS,EAAG,OAAM,MAAM,MAAM;AAAA,IAC7C,UAAE;AACA,mBAAa,KAAK;AAClB,aAAO,YAAY;AAAA,IACrB;AAAA,EACF;AACF;AAmBO,IAAM,iBAAN,cAA6B,mBAAmB;AAAA,EACrD,YAAY,OAA8B,CAAC,GAAG;AAE5C,UAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM;AAAA,MACJ;AAAA,MACA,SAAS,KAAK;AAAA,MACd,WAAW,KAAK;AAAA,MAChB,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,UAAU,gBAAgB;AAAA,IAC5B,CAAC;AAAA,EACH;AACF;;;AajYO,IAAM,YAAN,MAAgB;AAAA,EACb,UAAU;AAAA,EACV,WAAW,oBAAI,IAAyE;AAAA,EACxF,aAAgC,oBAAI,IAAI;AAAA,EACxC,iBAAuC;AAAA;AAAA;AAAA,EAI/C,IAAyB,MAAqD;AAC5E,UAAM,EAAE,MAAM,QAAQ,IAAI;AAC1B,QAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,YAAM,IAAI;AAAA,QACR,GAAG,IAAI;AAAA,MACT;AAAA,IACF;AACA,WAAO,IAAI,QAAQ,CAACC,cAAY;AAC9B,YAAM,KAAK,KAAK;AAChB,YAAM,UAAwB,EAAE,IAAI,MAAM,QAAQ;AAClD,WAAK,SAAS,IAAI,IAAI,EAAE,SAASA,WAAiC,QAAQ,CAAC;AAC3E,iBAAW,MAAM,KAAK,YAAY;AAChC,YAAI;AACF,aAAG,OAAO;AAAA,QACZ,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,QAAQ,IAAY,MAAqB;AACvC,UAAM,IAAI,KAAK,SAAS,IAAI,EAAE;AAC9B,QAAI,CAAC,EAAG;AACR,SAAK,SAAS,OAAO,EAAE;AACvB,SAAK,eAAe,EAAE,SAAS,IAAI;AACnC,MAAE,QAAQ,IAAI;AAAA,EAChB;AAAA;AAAA,EAGA,YAAkB;AAChB,UAAM,MAAM,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC;AACpC,eAAW,MAAM,KAAK;AACpB,YAAM,IAAI,KAAK,SAAS,IAAI,EAAE;AAC9B,UAAI,CAAC,EAAG;AACR,WAAK,SAAS,OAAO,EAAE;AACvB,QAAE,QAAQ,kBAAkB,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA,EAGA,OAAO,IAAqB;AAC1B,UAAM,IAAI,KAAK,SAAS,IAAI,EAAE;AAC9B,QAAI,CAAC,EAAG,QAAO;AACf,SAAK,SAAS,OAAO,EAAE;AACvB,MAAE,QAAQ,kBAAkB,EAAE,QAAQ,IAAI,CAAC;AAC3C,WAAO;AAAA,EACT;AAAA,EAEA,iBAAiB,IAAgC;AAC/C,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA,EAGA,GAAG,IAA8B;AAC/B,SAAK,WAAW,IAAI,EAAE;AACtB,WAAO,MAAM;AACX,WAAK,WAAW,OAAO,EAAE;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,UAA+B;AACjC,eAAW,CAAC,EAAE,CAAC,KAAK,KAAK,SAAU,QAAO,EAAE;AAC5C,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe,SAAuB,MAAqB;AACjE,QAAI,CAAC,KAAK,eAAgB;AAC1B,QAAI,QAAQ,SAAS,iBAAiB,QAAQ,SAAS,iBAAkB;AACzE,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAM,SAAS;AACf,QAAI;AACF,cAAQ,OAAO,MAAM;AAAA,QACnB,KAAK;AACH,eAAK,eAAe;AAAA,YAClB,MAAM;AAAA,YACN,MAAM,QAAQ;AAAA,YACd,SAAS,QAAQ;AAAA,UACnB,CAAC;AACD;AAAA,QACF,KAAK;AACH,eAAK,eAAe;AAAA,YAClB,MAAM;AAAA,YACN,MAAM,QAAQ;AAAA,YACd,SAAS,QAAQ;AAAA,YACjB,aAAa,OAAO;AAAA,UACtB,CAAC;AACD;AAAA,QACF,KAAK;AACH,cAAI,OAAO,OAAO,WAAW,SAAU;AACvC,eAAK,eAAe;AAAA,YAClB,MAAM;AAAA,YACN,MAAM,QAAQ;AAAA,YACd,SAAS,QAAQ;AAAA,YACjB,QAAQ,OAAO;AAAA,UACjB,CAAC;AACD;AAAA,QACF;AACE;AAAA,MACJ;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,MAA0B;AACnD,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,MAAM,OAAO;AAAA,IACxB,KAAK;AACH,aAAO,EAAE,MAAM,SAAS;AAAA,IAC1B,KAAK;AACH,aAAO,EAAE,MAAM,OAAO;AAAA,IACxB,KAAK;AACH,aAAO,EAAE,MAAM,YAAY;AAAA,IAC7B,KAAK;AACH,aAAO,EAAE,MAAM,SAAS;AAAA,EAC5B;AACF;AAGO,IAAM,YAAY,IAAI,UAAU;;;ACjOvC,SAAS,aAAa;AACtB,SAAS,YAAY,gBAAAC,qBAAoB;AACzC,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;;;ACHrB,SAAS,WAAW,WAAW,cAAc,qBAAqB;AAClE,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;;;ACqD9B,SAAS,KAAK,IAAuB,MAAgD;AACnF,SAAO;AAAA,IACL,MAAM,EAAE,OAAO,GAAG,MAAM,OAAO,SAAI;AAAA,IACnC,WAAW,EAAE,OAAO,KAAK,QAAQ,OAAO,SAAI;AAAA,IAC5C,WAAW,EAAE,OAAO,KAAK,OAAO,OAAO,SAAI;AAAA,IAC3C,MAAM,EAAE,OAAO,KAAK,MAAM,OAAO,SAAI;AAAA,IACrC,MAAM,EAAE,OAAO,KAAK,MAAM,OAAO,SAAI;AAAA,IACrC,MAAM,EAAE,OAAO,KAAK,QAAQ,OAAO,SAAI;AAAA,IACvC,MAAM,EAAE,OAAO,KAAK,IAAI,OAAO,OAAI;AAAA,IACnC,OAAO,EAAE,OAAO,KAAK,KAAK,OAAO,SAAI;AAAA,IACrC,MAAM,EAAE,OAAO,KAAK,MAAM,OAAO,SAAI;AAAA,IACrC,OAAO,EAAE,OAAO,GAAG,MAAM,OAAO,SAAI;AAAA,IACpC,UAAU,EAAE,OAAO,KAAK,QAAQ,OAAO,SAAI;AAAA,IAC3C,UAAU,EAAE,OAAO,KAAK,MAAM,OAAO,IAAI;AAAA,IACzC,QAAQ,EAAE,OAAO,KAAK,MAAM,OAAO,SAAI;AAAA,IACvC,QAAQ,EAAE,OAAO,GAAG,MAAM,OAAO,SAAI;AAAA,IACrC,KAAK,EAAE,OAAO,KAAK,OAAO,OAAO,SAAI;AAAA,IACrC,QAAQ,EAAE,OAAO,GAAG,MAAM,OAAO,SAAI;AAAA,IACrC,QAAQ,EAAE,OAAO,KAAK,QAAQ,OAAO,SAAI;AAAA,EAC3C;AACF;AAEA,SAAS,YAAY,MAA8B;AACjD,SAAO,EAAE,GAAG,MAAM,MAAM,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE;AACnD;AAEA,IAAM,aAAa,YAAY;AAAA,EAC7B,IAAI;AAAA,IACF,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF,CAAC;AAED,IAAM,OAAO,YAAY;AAAA,EACvB,IAAI;AAAA,IACF,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF,CAAC;AAED,IAAM,QAAQ,YAAY;AAAA,EACxB,IAAI;AAAA,IACF,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF,CAAC;AAED,IAAM,aAAa,YAAY;AAAA,EAC7B,IAAI;AAAA,IACF,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF,CAAC;AAED,IAAM,cAAc,YAAY;AAAA,EAC9B,IAAI;AAAA,IACF,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF,CAAC;AAED,IAAM,eAAe,YAAY;AAAA,EAC/B,IAAI;AAAA,IACF,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF,CAAC;AAEM,IAAM,SAAS;AAAA,EACpB,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,iBAAiB;AACnB;AAEO,IAAM,qBAAgC;AAmBtC,IAAM,gBAAgB,OAAO,kBAAkB;AAEtD,IAAI,cAA2B;AAe/B,SAAS,YAA8B,QAAsC;AAC3E,QAAM,SAAS,OAAO,aAAa;AACnC,SAAO,IAAI,MAAM,QAAQ;AAAA,IACvB,IAAI,SAAS,MAAuB;AAClC,aAAO,OAAO,WAAW,EAAE,IAAe;AAAA,IAC5C;AAAA,IACA,yBAAyB,SAAS,MAAuB;AACvD,aAAO,QAAQ,yBAAyB,OAAO,WAAW,GAAG,IAAI;AAAA,IACnE;AAAA,IACA,IAAI,SAAS,MAAuB;AAClC,aAAO,QAAQ,OAAO,WAAW;AAAA,IACnC;AAAA,IACA,UAAU;AACR,aAAO,QAAQ,QAAQ,OAAO,WAAW,CAAC;AAAA,IAC5C;AAAA,EACF,CAAC;AACH;AAEO,IAAM,KAAK,YAAY,CAAC,UAAU,MAAM,EAAE;AAC1C,IAAM,OAAO,YAAY,CAAC,UAAU,MAAM,IAAI;AAC9C,IAAM,cAAc,YAAY,CAAC,UAAU,MAAM,UAAU;AAC3D,IAAM,UAAU,YAAY,CAAC,UAAU,MAAM,OAAO;AACpD,IAAM,OAAO,YAAY,CAAC,UAAU,MAAM,IAAI;;;ACjWrD,OAAO,eAAe;AA+Bf,IAAM,yBAAyB;AAAA,EACpC,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,IAAM,yBAAyB,MAAM;;;AF+C5C,IAAM,oBAA4C;AAAA,EAChD,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AACb;AAGO,SAAS,uBACd,MAAqB,WAAW,GACL;AAC3B,QAAM,MAAiC,CAAC;AACxC,aAAW,QAAQ,CAAC,QAAQ,YAAY,WAAW,WAAW,GAAG;AAC/D,QAAI,KAAK,EAAE,MAAM,SAAS,MAAM,aAAa,kBAAkB,IAAI,EAAE,CAAC;AAAA,EACxE;AACA,QAAM,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAC3C,aAAW,OAAO,IAAI,QAAQ,eAAe,CAAC,GAAG;AAC/C,QAAI,CAAC,OAAO,OAAO,IAAI,SAAS,SAAU;AAC1C,UAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,QAAI,CAAC,QAAQ,CAAC,gCAAgC,KAAK,IAAI,EAAG;AAC1D,QAAI,KAAK,IAAI,IAAI,EAAG;AACpB,SAAK,IAAI,IAAI;AACb,UAAM,QAAiC,EAAE,MAAM,SAAS,MAAM;AAC9D,QAAI,OAAO,IAAI,gBAAgB,SAAU,OAAM,cAAc,IAAI;AACjE,QAAI,IAAI,aAAa,SAAS,IAAI,aAAa,YAAY,IAAI,aAAa,QAAQ;AAClF,YAAM,WAAW,IAAI;AAAA,IACvB;AACA,QAAI,IAAI,YAAY,cAAe,OAAM,UAAU,IAAI;AACvD,QAAI,KAAK,KAAK;AAAA,EAChB;AACA,SAAO;AACT;AAEO,SAAS,mBACd,UACA,MAAqB,WAAW,GACmC;AACnE,QAAM,QAAQ,uBAAuB,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AACzE,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,QAAM,MAAyE,CAAC;AAChF,MAAI,MAAM,SAAU,KAAI,WAAW,MAAM;AACzC,MAAI,MAAM,QAAS,KAAI,UAAU,MAAM;AACvC,SAAO;AACT;AAMO,SAAS,oBAA4B;AAC1C,SAAO,KAAK,QAAQ,GAAG,YAAY,aAAa;AAClD;AAEO,SAAS,WAAWC,QAAe,kBAAkB,GAAkB;AAC5E,MAAI;AACF,UAAM,MAAM,aAAaA,OAAM,MAAM;AACrC,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,UAAU,OAAO,WAAW,SAAU,QAAO;AAAA,EACnD,QAAQ;AAAA,EAER;AACA,SAAO,CAAC;AACV;AAEO,SAAS,YAAY,KAAoBA,QAAe,kBAAkB,GAAS;AACxF,YAAU,QAAQA,KAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAcA,OAAM,KAAK,UAAU,KAAK,MAAM,CAAC,GAAG,MAAM;AACxD,MAAI;AACF,cAAUA,OAAM,GAAK;AAAA,EACvB,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,aAAaA,QAAe,kBAAkB,GAA6B;AACzF,SAAO,WAAWA,KAAI,EAAE;AAC1B;AA6BO,SAAS,WAAWC,QAAe,kBAAkB,GAAuB;AACjF,MAAI,QAAQ,IAAI,iBAAkB,QAAO,QAAQ,IAAI;AACrD,SAAO,WAAWA,KAAI,EAAE;AAC1B;AA4BO,SAAS,YAAYC,QAAe,kBAAkB,GAAuB;AAClF,MAAI,QAAQ,IAAI,kBAAmB,QAAO,QAAQ,IAAI;AACtD,SAAO,WAAWA,KAAI,EAAE;AAC1B;AA4BO,SAAS,YAAY,KAAaC,QAAe,kBAAkB,GAAS;AACjF,QAAM,MAAM,WAAWA,KAAI;AAC3B,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,SAAS;AACX,QAAI,UAAU;AAAA,EAChB,OAAO;AACL,QAAI,UAAU;AAAA,EAChB;AACA,cAAY,KAAKA,KAAI;AACvB;AAUO,SAAS,gBAAgBC,QAAe,kBAAkB,GAAyB;AACxF,QAAM,MAAM,WAAWA,KAAI,EAAE;AAC7B,MAAI,QAAQ,UAAW,QAAO;AAC9B,SAAO;AACT;AAEO,SAAS,kBAAkBA,QAAe,kBAAkB,GAAW;AAC5E,QAAM,MAAM,WAAWA,KAAI,EAAE;AAC7B,MAAI,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC3C,SAAO;AACT;AAEO,SAAS,WAAW,KAAaA,QAAe,kBAAkB,GAAS;AAChF,QAAM,MAAM,WAAWA,KAAI;AAC3B,MAAI,SAAS,IAAI,KAAK;AACtB,cAAY,KAAKA,KAAI;AACvB;AAGA,SAAS,eAAe,KAAoB,SAAqC;AAC/E,QAAM,WAAW,IAAI;AACrB,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,OAAO,OAAO,UAAU,OAAO,EAAG,QAAO;AAC7C,MAAI,QAAQ,aAAa,QAAS,QAAO;AACzC,QAAM,QAAQ,QAAQ,YAAY;AAClC,aAAW,KAAK,OAAO,KAAK,QAAQ,GAAG;AACrC,QAAI,EAAE,YAAY,MAAM,MAAO,QAAO;AAAA,EACxC;AACA,SAAO;AACT;AAYO,SAAS,uBACd,SACA,QACAC,QAAe,kBAAkB,GAC3B;AACN,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,CAAC,QAAS;AACd,QAAM,MAAM,WAAWA,KAAI;AAC3B,MAAI,CAAC,IAAI,SAAU,KAAI,WAAW,CAAC;AACnC,QAAM,MAAM,eAAe,KAAK,OAAO,KAAK;AAC5C,MAAI,CAAC,IAAI,SAAS,GAAG,EAAG,KAAI,SAAS,GAAG,IAAI,CAAC;AAC7C,QAAM,WAAW,IAAI,SAAS,GAAG,EAAE,gBAAgB,CAAC;AACpD,MAAI,SAAS,SAAS,OAAO,EAAG;AAChC,MAAI,SAAS,GAAG,EAAE,eAAe,CAAC,GAAG,UAAU,OAAO;AACtD,cAAY,KAAKA,KAAI;AACvB;AAuCO,SAAS,uBACd,SACAC,QAAe,kBAAkB,GACvB;AACV,QAAM,MAAM,WAAWA,KAAI;AAC3B,QAAM,MAAM,eAAe,KAAK,OAAO;AACvC,MAAI,QAAQ,OAAW,QAAO,CAAC;AAC/B,SAAO,IAAI,WAAW,GAAG,GAAG,eAAe,CAAC;AAC9C;AAEO,SAAS,sBACd,SACA,QACAA,QAAe,kBAAkB,GAC3B;AACN,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,CAAC,QAAS;AACd,QAAM,MAAM,WAAWA,KAAI;AAC3B,MAAI,CAAC,IAAI,SAAU,KAAI,WAAW,CAAC;AACnC,QAAM,MAAM,eAAe,KAAK,OAAO,KAAK;AAC5C,MAAI,CAAC,IAAI,SAAS,GAAG,EAAG,KAAI,SAAS,GAAG,IAAI,CAAC;AAC7C,QAAM,WAAW,IAAI,SAAS,GAAG,EAAE,eAAe,CAAC;AACnD,MAAI,SAAS,SAAS,OAAO,EAAG;AAChC,MAAI,SAAS,GAAG,EAAE,cAAc,CAAC,GAAG,UAAU,OAAO;AACrD,cAAY,KAAKA,KAAI;AACvB;AAyPO,SAAS,eAAe,KAAsB;AACnD,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,SAAS,GAAI,QAAO;AAChC,SAAO,CAAC,KAAK,KAAK,OAAO;AAC3B;AAGO,SAAS,UAAU,KAAqB;AAC7C,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,UAAU,GAAI,QAAO;AAC7B,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,SAAI,IAAI,MAAM,EAAE,CAAC;AAC5C;;;AGvtBO,IAAM,KAAwB;AAAA,EACnC,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA,KAAK;AAAA,IACH,aAAa;AAAA,IACb,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,KAAK;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,EACT;AAAA,EACA,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,IACb,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBACE;AAAA,IACF,sBACE;AAAA,IACF,0BACE;AAAA,IACF,kBACE;AAAA,IACF,sBACE;AAAA,IACF,gBACE;AAAA,IACF,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,eACE;AAAA,IACF,aAAa;AAAA,IACb,iBAAiB;AAAA,MACf,OAAO;AAAA,MACP,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,YACJ,EAAE,KAAK,SAAS,MAAM,+BAA+B;AAAA,YACrD;AAAA,cACE,KAAK;AAAA,cACL,MAAM;AAAA,YACR;AAAA,YACA,EAAE,KAAK,KAAK,MAAM,0DAA0D;AAAA,UAC9E;AAAA,QACF;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA,mBAAmB;AAAA,MACjB,OAAO;AAAA,MACP,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,YACJ,EAAE,KAAK,QAAQ,MAAM,yDAAoD;AAAA,YACzE;AAAA,cACE,KAAK;AAAA,cACL,MAAM;AAAA,YACR;AAAA,YACA,EAAE,KAAK,SAAS,MAAM,8DAA8D;AAAA,YACpF;AAAA,cACE,KAAK;AAAA,cACL,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA,eAAe;AAAA,MACb,OAAO;AAAA,MACP,UAAU;AAAA,QACR;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,EAAE,KAAK,SAAS,MAAM,oBAAoB;AAAA,YAC1C,EAAE,KAAK,eAAe,MAAM,iCAAiC;AAAA,YAC7D,EAAE,KAAK,mBAAS,MAAM,oDAAoD;AAAA,YAC1E;AAAA,cACE,KAAK;AAAA,cACL,MAAM;AAAA,YACR;AAAA,YACA,EAAE,KAAK,mBAAmB,MAAM,0CAA0C;AAAA,YAC1E,EAAE,KAAK,UAAU,MAAM,oCAAoC;AAAA,YAC3D,EAAE,KAAK,UAAU,MAAM,iCAAiC;AAAA,YACxD,EAAE,KAAK,OAAO,MAAM,iEAA2D;AAAA,YAC/E,EAAE,KAAK,aAAa,MAAM,4CAAuC;AAAA,YACjE,EAAE,KAAK,OAAO,MAAM,mDAAgD;AAAA,YACpE,EAAE,KAAK,UAAU,MAAM,+DAA0D;AAAA,YACjF,EAAE,KAAK,eAAe,MAAM,uCAAuC;AAAA,YACnE,EAAE,KAAK,OAAO,MAAM,oCAAoC;AAAA,UAC1D;AAAA,QACF;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,EAAE,KAAK,SAAS,MAAM,8DAA8D;AAAA,YACpF,EAAE,KAAK,QAAQ,MAAM,8DAAyD;AAAA,YAC9E,EAAE,KAAK,eAAe,MAAM,wDAAwD;AAAA,UACtF;AAAA,QACF;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,EAAE,KAAK,eAAe,MAAM,6DAAwD;AAAA,YACpF;AAAA,cACE,KAAK;AAAA,cACL,MAAM;AAAA,YACR;AAAA,YACA;AAAA,cACE,KAAK;AAAA,cACL,MAAM;AAAA,YACR;AAAA,YACA,EAAE,KAAK,SAAS,MAAM,wDAAqD;AAAA,YAC3E;AAAA,cACE,KAAK;AAAA,cACL,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,EAAE,KAAK,SAAS,MAAM,mDAAmD;AAAA,YACzE,EAAE,KAAK,aAAa,MAAM,wDAAmD;AAAA,YAC7E,EAAE,KAAK,KAAK,MAAM,0DAA0D;AAAA,UAC9E;AAAA,QACF;AAAA,MACF;AAAA,MACA,QACE;AAAA,IACJ;AAAA,IACA,cAAc;AAAA,IACd,eAAe;AAAA,IACf,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,IACf,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,IACrB,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,UAAU;AAAA,IACV,UAAU;AAAA,IACV,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,sBAAsB;AAAA,IACtB,uBAAuB;AAAA,IACvB,sBAAsB;AAAA,IACtB,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,iBAAiB;AAAA,EACnB;AAAA,EACA,OAAO;AAAA,IACL,MAAM,EAAE,aAAa,kCAAkC;AAAA,IACvD,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,QAAQ,EAAE,aAAa,yCAAyC;AAAA,IAChE,QAAQ;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,OAAO,EAAE,aAAa,4BAA4B,UAAU,OAAO;AAAA,IACnE,QAAQ,EAAE,aAAa,sDAAsD;AAAA,IAC7E,OAAO;AAAA,MACL,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,aAAa;AAAA,MACb,UAAU;AAAA,MACV,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,IACA,KAAK;AAAA,MACH,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,QAAQ;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK,EAAE,aAAa,oDAAoD;AAAA,IACxE,UAAU;AAAA,MACR,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,QAAQ;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,QAAQ;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,WAAW;AAAA,MACT,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,QAAQ,EAAE,aAAa,gEAAgE;AAAA,IACvF,OAAO;AAAA,MACL,aACE;AAAA,IACJ;AAAA,IACA,MAAM;AAAA,MACJ,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,QAAQ,EAAE,aAAa,oEAAoE;AAAA,IAC3F,SAAS,EAAE,aAAa,+DAA+D;AAAA,IACvF,OAAO,EAAE,aAAa,qDAAqD;AAAA,IAC3E,SAAS;AAAA,MACP,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,MAAM,EAAE,aAAa,0DAA0D;AAAA,IAC/E,UAAU,EAAE,aAAa,+DAA+D;AAAA,IACxF,MAAM,EAAE,aAAa,0CAA0C;AAAA,IAC/D,OAAO,EAAE,aAAa,4DAA4D;AAAA,IAClF,QAAQ;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,UAAU,EAAE,aAAa,mDAA8C;AAAA,IACvE,OAAO,EAAE,aAAa,8CAA8C;AAAA,IACpE,UAAU;AAAA,MACR,aAAa;AAAA,IACf;AAAA,IACA,OAAO,EAAE,aAAa,mDAAmD;AAAA,IACzE,KAAK,EAAE,aAAa,0DAA0D;AAAA,IAC9E,MAAM;AAAA,MACJ,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,MAAM,EAAE,aAAa,eAAe;AAAA,IACpC,MAAM;AAAA,MACJ,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,SAAS;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,MAAM,EAAE,aAAa,wCAAwC;AAAA,IAC7D,SAAS,EAAE,aAAa,qEAAqE;AAAA,IAC7F,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,QAAQ,EAAE,aAAa,mCAAmC,UAAU,QAAQ;AAAA,IAC5E,YAAY;AAAA,MACV,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,SAAS;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,MACJ,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,MAAM,EAAE,aAAa,iDAAiD;AAAA,IACtE,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,cAAc;AAAA,IACd,cAAc;AAAA,IACd,cAAc;AAAA,IACd,oBAAoB;AAAA,IACpB,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,gBACE;AAAA,IACF,mBACE;AAAA,IACF,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,oBAAoB;AAAA,IACpB,aAAa;AAAA,IACb,cAAc;AAAA,MACZ,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,MACP,eAAe;AAAA,MACf,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IACnB;AAAA,IACA,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,gBACE;AAAA,IACF,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,sBAAsB;AAAA,IACtB,wBAAwB;AAAA,IACxB,sBAAsB;AAAA,IACtB,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,aAAa;AAAA,IACb,UAAU;AAAA,IACV,mBAAmB;AAAA,IACnB,sBAAsB;AAAA,IACtB,mBAAmB;AAAA,IACnB,sBAAsB;AAAA,IACtB,wBAAwB;AAAA,IACxB,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,IACpB,sBAAsB;AAAA,IACtB,wBAAwB;AAAA,IACxB,oBAAoB;AAAA,IACpB,sBAAsB;AAAA,EACxB;AAAA,EACA,aAAa;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,EACZ;AAAA,EACA,UAAU;AAAA,IACR,kBAAkB;AAAA,IAClB,sBAAsB;AAAA,IACtB,qBACE;AAAA,IACF,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,yBAAyB;AAAA,IACzB,QAAQ;AAAA,MACN,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,YAAY;AAAA,IACd;AAAA,IACA,cAAc;AAAA,IACd,wBAAwB;AAAA,IACxB,OAAO;AAAA,MACL,SAAS;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,MACA,QAAQ;AAAA,QACN,OAAO;AAAA,QACP,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,MACA,QAAQ;AAAA,QACN,OAAO;AAAA,QACP,MAAM;AAAA,QACN,WACE;AAAA,MACJ;AAAA,MACA,qBAAqB;AAAA,QACnB,OAAO;AAAA,QACP,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,MACA,iBAAiB;AAAA,QACf,OAAO;AAAA,QACP,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,YAAY;AAAA,MACV,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,aAAa;AAAA,MACb,qBAAqB;AAAA,IACvB;AAAA,IACA,eAAe;AAAA,IACf,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,IACpB,cAAc;AAAA,IACd,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,aAAa;AAAA,IACb,cACE;AAAA,IACF,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,wBAAwB;AAAA,IACxB,eAAe;AAAA,IACf,cACE;AAAA,IACF,cACE;AAAA,IACF,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,UAAU;AAAA,IACV,WAAW;AAAA,IACX,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,sBAAsB;AAAA,IACtB,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,iBACE;AAAA,IACF,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,eAAe;AAAA,EACjB;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,eAAe;AAAA,IACf,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,eAAe;AAAA,EACjB;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,IACR,sBACE;AAAA,IACF,mBACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,iBACE;AAAA,IACF,aAAa;AAAA,IACb,UAAU;AAAA,IACV,eACE;AAAA,IACF,kBAAkB;AAAA,IAClB,mBACE;AAAA,IACF,qBAAqB;AAAA,IACrB,iBACE;AAAA,IACF,iBACE;AAAA,IACF,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,gBACE;AAAA,IACF,wBACE;AAAA,IACF,uBACE;AAAA,IACF,YACE;AAAA,IACF,iBAAiB;AAAA,IACjB,yBAAyB;AAAA,IACzB,eAAe;AAAA,IACf,eACE;AAAA,IACF,2BACE;AAAA,IACF,gBACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,iBACE;AAAA,IACF,wBAAwB;AAAA,IACxB,SACE;AAAA,IACF,YACE;AAAA,IACF,aAAa;AAAA,IACb,eAAe;AAAA,IACf,iBACE;AAAA,IACF,sBACE;AAAA,IACF,wBACE;AAAA,IACF,0BACE;AAAA,IACF,wBACE;AAAA,IACF,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,oBACE;AAAA,IACF,aACE;AAAA,IACF,cAAc;AAAA,IACd,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,aAAa;AAAA,EACf;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,MACL,SACE;AAAA,MACF,iBACE;AAAA,MACF,uBACE;AAAA,MACF,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,iBACE;AAAA,MACF,kBACE;AAAA,MACF,kBAAkB;AAAA,MAClB,iBAAiB;AAAA,MACjB,eACE;AAAA,MACF,iBACE;AAAA,MACF,kBACE;AAAA,MACF,oBAAoB;AAAA,MACpB,mBACE;AAAA,MACF,kBACE;AAAA,MACF,eAAe;AAAA,MACf,UAAU;AAAA,MACV,gBACE;AAAA,MACF,cAAc;AAAA,MACd,SACE;AAAA,MACF,cACE;AAAA,MACF,cACE;AAAA,MACF,kBAAkB;AAAA,MAClB,gBACE;AAAA,MACF,iBACE;AAAA,MACF,eACE;AAAA,MACF,mBAAmB;AAAA,MACnB,mBA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","names":["resolve","resolve","readFileSync","homedir","join","path","path","path","path","path","path","path","path","join","homedir","path","readFileSync","settings","resolve","existsSync","readFileSync","dirname","join","t","process","path","t","resolve","chmodSync","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","join","homedir","path","existsSync","readFileSync","mkdirSync","dirname","chmodSync","join","stat","join","existsSync","readFileSync","existsSync","path","mkdirSync","dirname","writeFileSync","chmodSync","existsSync","readFileSync","t","t","t","decision","existsSync","readFileSync","readdirSync","statSync","join","readFileSync","walk","readdirSync","join","statSync","fs","existsSync","readFileSync","existsSync","readFileSync","statSync","join","path","join","existsSync","readFileSync","createHash","existsSync","mkdirSync","readFileSync","readdirSync","unlinkSync","writeFileSync","homedir","join","resolve","existsSync","mkdirSync","readFileSync","readdirSync","statSync","writeFileSync","homedir","dirname","join","resolve","homedir","resolve","join","existsSync","readdirSync","mkdirSync","dirname","writeFileSync","statSync","path","readFileSync","resolve","createHash","join","existsSync","mkdirSync","homedir","readFileSync","readdirSync","writeFileSync","unlinkSync","path","fs","pathMod","picomatch","i","j","fs","pathMod","picomatch","displayRel","walk","pathMod","fs","pathMod","displayRel","walk","pct","sep","OUTLINE_MAX_ENTRIES","displayRel","picomatch","stat","fs","walk","indent","path","t","DEFAULT_MAX_RESULT_CHARS","errorMessage","costUsd","t","pathMod","spawn","pathMod","spawn","id","job","resolve","spawn","existsSync","statSync","pathMod","spawn","pathMod","isAllowed","killProcessTree","spawn","resolve","killProcessTree","resolve","spawn","delimiter","existsSync","statSync","snapshot","readFileSync","resolve","path","readFileSync","path","path","t","round","t","p","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","fileURLToPath","cached","resolve","spawn","resolve","createParser","resolve","createParser","resolve","closeSync","existsSync","mkdirSync","openSync","readFileSync","unlinkSync","writeFileSync","dirname","resolve","stat","resolve","existsSync","readFileSync","unlinkSync","writeFileSync","existsSync","readFileSync","join","join","existsSync","readFileSync","appendFileSync","closeSync","existsSync","fstatSync","mkdirSync","openSync","readFileSync","readSync","renameSync","statSync","unlinkSync","writeFileSync","homedir","dirname","join","join","homedir","path","openSync","stat","fstatSync","readSync","closeSync","writeFileSync","renameSync","unlinkSync","mkdirSync","dirname","appendFileSync","existsSync","readFileSync","statSync"]}