reasonix 0.33.2 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/app.js +1 -21
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/{chat-ZMSAXE77.js → chat-TD6GR3QK.js} +14 -13
- package/dist/cli/chunk-2EBODRRO.js +149 -0
- package/dist/cli/chunk-2EBODRRO.js.map +1 -0
- package/dist/cli/{chunk-DULSP7JH.js → chunk-5JXXEPDM.js} +34 -1
- package/dist/cli/chunk-5JXXEPDM.js.map +1 -0
- package/dist/cli/{chunk-OW7IHE6M.js → chunk-EINEIIIW.js} +693 -364
- package/dist/cli/chunk-EINEIIIW.js.map +1 -0
- package/dist/cli/{chunk-WVJL7ZO2.js → chunk-F3ILWP2L.js} +4 -4
- package/dist/cli/{chunk-SDE5U32Z.js → chunk-KZHMKOJH.js} +13 -8
- package/dist/cli/{chunk-SDE5U32Z.js.map → chunk-KZHMKOJH.js.map} +1 -1
- package/dist/cli/{chunk-G7M3QWEN.js → chunk-LNTORE5K.js} +225 -132
- package/dist/cli/chunk-LNTORE5K.js.map +1 -0
- package/dist/cli/chunk-MRLXEMZ7.js +26 -0
- package/dist/cli/chunk-MRLXEMZ7.js.map +1 -0
- package/dist/cli/{chunk-WBDE4IRI.js → chunk-OERAGRJX.js} +2 -2
- package/dist/cli/{chunk-QGE6AF76.js → chunk-Q36KBLSU.js} +207 -8
- package/dist/cli/chunk-Q36KBLSU.js.map +1 -0
- package/dist/cli/{chunk-RZILUXUC.js → chunk-RXGEGA7K.js} +2 -2
- package/dist/cli/{chunk-FXGQ5NHE.js → chunk-SA4UGZPG.js} +21 -1
- package/dist/cli/chunk-SA4UGZPG.js.map +1 -0
- package/dist/cli/{chunk-J5VLP23S.js → chunk-SW3CCXEV.js} +2 -2
- package/dist/cli/{chunk-W4LDFAZ6.js → chunk-SX6L4HZZ.js} +2 -2
- package/dist/cli/chunk-WUI3P4RA.js +319 -0
- package/dist/cli/chunk-WUI3P4RA.js.map +1 -0
- package/dist/cli/{code-R4TXQQEE.js → code-TGUOQBRJ.js} +15 -14
- package/dist/cli/{code-R4TXQQEE.js.map → code-TGUOQBRJ.js.map} +1 -1
- package/dist/cli/{commands-JWT2MWVH.js → commands-MEZPSEHV.js} +4 -3
- package/dist/cli/{commands-JWT2MWVH.js.map → commands-MEZPSEHV.js.map} +1 -1
- package/dist/cli/{commit-RPZBOZS2.js → commit-CE4EFTUQ.js} +3 -2
- package/dist/cli/{commit-RPZBOZS2.js.map → commit-CE4EFTUQ.js.map} +1 -1
- package/dist/cli/{doctor-V5HLCMSQ.js → doctor-YASM64X6.js} +7 -6
- package/dist/cli/index.js +22 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-ARTNQ24O.js → mcp-LDFK5QJI.js} +3 -2
- package/dist/cli/{mcp-ARTNQ24O.js.map → mcp-LDFK5QJI.js.map} +1 -1
- package/dist/cli/{mcp-browse-HLO2ENDL.js → mcp-browse-FYHEITCM.js} +3 -2
- package/dist/cli/{mcp-browse-HLO2ENDL.js.map → mcp-browse-FYHEITCM.js.map} +1 -1
- package/dist/cli/{replay-Q43DSMG6.js → replay-JEDLU7F2.js} +8 -6
- package/dist/cli/{replay-Q43DSMG6.js.map → replay-JEDLU7F2.js.map} +1 -1
- package/dist/cli/{run-HK3FP266.js → run-NHD2RSTD.js} +7 -6
- package/dist/cli/{run-HK3FP266.js.map → run-NHD2RSTD.js.map} +1 -1
- package/dist/cli/{server-SYC3OVOP.js → server-MC4A4WAJ.js} +9 -8
- package/dist/cli/{server-SYC3OVOP.js.map → server-MC4A4WAJ.js.map} +1 -1
- package/dist/cli/{sessions-3XU2GGHX.js → sessions-ZHWJEW4L.js} +7 -6
- package/dist/cli/{sessions-3XU2GGHX.js.map → sessions-ZHWJEW4L.js.map} +1 -1
- package/dist/cli/{setup-CCJZAWTY.js → setup-DK43MT47.js} +6 -5
- package/dist/cli/{setup-CCJZAWTY.js.map → setup-DK43MT47.js.map} +1 -1
- package/dist/cli/{version-5SGI2SEE.js → version-O362UKPM.js} +7 -6
- package/dist/cli/{version-5SGI2SEE.js.map → version-O362UKPM.js.map} +1 -1
- package/dist/index.d.ts +45 -1
- package/dist/index.js +569 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-63KAV5DX.js +0 -106
- package/dist/cli/chunk-63KAV5DX.js.map +0 -1
- package/dist/cli/chunk-DULSP7JH.js.map +0 -1
- package/dist/cli/chunk-FXGQ5NHE.js.map +0 -1
- package/dist/cli/chunk-G7M3QWEN.js.map +0 -1
- package/dist/cli/chunk-OW7IHE6M.js.map +0 -1
- package/dist/cli/chunk-QGE6AF76.js.map +0 -1
- package/dist/cli/chunk-ZPTSJGX5.js +0 -88
- package/dist/cli/chunk-ZPTSJGX5.js.map +0 -1
- /package/dist/cli/{chat-ZMSAXE77.js.map → chat-TD6GR3QK.js.map} +0 -0
- /package/dist/cli/{chunk-WVJL7ZO2.js.map → chunk-F3ILWP2L.js.map} +0 -0
- /package/dist/cli/{chunk-WBDE4IRI.js.map → chunk-OERAGRJX.js.map} +0 -0
- /package/dist/cli/{chunk-RZILUXUC.js.map → chunk-RXGEGA7K.js.map} +0 -0
- /package/dist/cli/{chunk-J5VLP23S.js.map → chunk-SW3CCXEV.js.map} +0 -0
- /package/dist/cli/{chunk-W4LDFAZ6.js.map → chunk-SX6L4HZZ.js.map} +0 -0
- /package/dist/cli/{doctor-V5HLCMSQ.js.map → doctor-YASM64X6.js.map} +0 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/retry.ts","../src/core/pause-gate.ts","../src/hooks.ts","../src/config.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/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/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/skills.ts","../src/prompt-fragments.ts","../src/tools/filesystem.ts","../src/tools/fs/edit.ts","../src/tools/fs/glob.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 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\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\nexport interface DeepSeekClientOptions {\n apiKey?: string;\n baseUrl?: string;\n timeoutMs?: number;\n fetch?: typeof fetch;\n /** Retry configuration. Pass `{ maxAttempts: 1 }` to disable retries. */\n retry?: RetryOptions;\n}\n\nexport class DeepSeekClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeoutMs: number;\n readonly retry: RetryOptions;\n private readonly _fetch: typeof fetch;\n\n constructor(opts: DeepSeekClientOptions = {}) {\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 this.apiKey = apiKey;\n let url = opts.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? \"https://api.deepseek.com\";\n // Manual trim — `/\\/+$/` is O(n²) on slash-heavy non-matches per CodeQL js/polynomial-redos.\n while (url.endsWith(\"/\")) url = url.slice(0, -1);\n this.baseUrl = url;\n // 11 min. DeepSeek's load-balancer may keep a connection open for\n // up to 10 minutes while the request waits in queue (non-streaming\n // sends empty lines, streaming sends `:` SSE keep-alive comments —\n // both are invisible to our parsers, so neither surfaces until the\n // real response starts). Timing out at the legacy 2-min default\n // killed queued requests prematurely, burned the queue slot on\n // retry, and could loop through the whole queue repeatedly.\n // Setting 11 min lets the server's own 10-min cap close the\n // connection first (clean EOF → natural retry), and our timer\n // is a safety net for genuinely hung sockets.\n this.timeoutMs = opts.timeoutMs ?? 660_000;\n this._fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry ?? {};\n }\n\n private buildPayload(opts: ChatRequestOptions, stream: boolean) {\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 // V4 thinking-mode toggle: lives under `extra_body.thinking.type` per\n // DeepSeek's docs. Docs also note that in thinking mode `temperature`,\n // `top_p`, `presence_penalty`, `frequency_penalty` are silently\n // ignored — we don't strip them here because the server's explicit\n // \"setting won't report an error\" contract means leaving them in is\n // safe and keeps the request payload diffable against OpenAI tooling.\n if (opts.thinking) {\n payload.extra_body = { thinking: { type: opts.thinking } };\n }\n if (opts.reasoningEffort) {\n payload.reasoning_effort = opts.reasoningEffort;\n }\n return payload;\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 try {\n const resp = await this._fetch(`${this.baseUrl}/user/balance`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\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 /** Returns null on failure — callers fall back to a hardcoded model hint. */\n async listModels(opts: { signal?: AbortSignal } = {}): Promise<ModelList | null> {\n try {\n const resp = await this._fetch(`${this.baseUrl}/models`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\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 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 Authorization: `Bearer ${this.apiKey}`,\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(`DeepSeek ${resp.status}: ${await resp.text()}`);\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 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 // Only the initial fetch is retried. Once the server has started sending\n // the stream body we do NOT retry — a mid-stream retry would re-bill and\n // desync the session context.\n resp = await fetchWithRetry(\n this._fetch,\n `${this.baseUrl}/chat/completions`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\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(`DeepSeek ${resp.status}: ${await resp.text().catch(() => \"\")}`);\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 (typeof delta.reasoning_content === \"string\" && delta.reasoning_content.length > 0) {\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\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","/** 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 = { type: \"approve\" } | { type: \"refine\" } | { type: \"cancel\" };\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 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 };\n run_background: { command: string };\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 Reasonix 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 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\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>/.reasonix/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 = \".reasonix\";\n\n/** Where the global settings.json lives. Equivalent to `~/.reasonix/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 { 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 ReasonixConfig {\n apiKey?: string;\n baseUrl?: string;\n lang?: LanguageCode;\n preset?: PresetName;\n editMode?: EditMode;\n editModeHintShown?: boolean;\n reasoningEffort?: ReasoningEffort;\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 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 projects?: {\n [absoluteRootDir: string]: {\n shellAllowed?: string[];\n };\n };\n index?: IndexUserConfig;\n semantic?: SemanticEmbeddingUserConfig;\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(), \".reasonix\", \"config.json\");\n}\n\nexport function readConfig(path: string = defaultConfigPath()): ReasonixConfig {\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") return parsed as ReasonixConfig;\n } catch {\n /* missing or malformed → empty config */\n }\n return {};\n}\n\nexport function writeConfig(cfg: ReasonixConfig, 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\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. */\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\nexport function searchEnabled(path: string = defaultConfigPath()): boolean {\n const env = process.env.REASONIX_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: ReasonixConfig, 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\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 return v === \"auto\" ? \"auto\" : \"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/** 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\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 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.REASONIX_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.REASONIX_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\nexport function isPlausibleKey(key: string): boolean {\n const trimmed = key.trim();\n return /^sk-[A-Za-z0-9_-]{16,}$/.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","/** 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 \".reasonix\",\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 },\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 Reasonix version.\",\n update: \"Check for a newer Reasonix and install it.\",\n index: \"Build (or incrementally refresh) a local semantic search index.\",\n },\n ui: {\n welcome: \"Run `reasonix` 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 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 · /forget to start over · /sessions to list',\n newSession:\n '▸ session \"{name}\" (new) — auto-saved as you chat · /forget to delete · /sessions to list',\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 \"▸ TIP: edit-gate keybindings\\n y / n accept or drop pending edits\\n Shift+Tab switch review ↔ AUTO (persisted; AUTO applies instantly)\\n u undo the last auto-applied batch (within the 5s banner)\\n Current mode is shown in the bottom status bar. Run /keys anytime for the full list.\\n (This tip shows once — suppressed after.)\",\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 ~/.reasonix/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 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 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 (REASONIX.md + ~/.reasonix/memory)\",\n argsHint: \"[list|show <name>|forget <name>|clear <scope> confirm]\",\n },\n skill: {\n description: \"list / run user skills (<project>/.reasonix/skills + ~/.reasonix/skills)\",\n argsHint: \"[list|show <name>|<name> [args]]\",\n },\n hooks: {\n description: \"list active hooks (settings.json under .reasonix/) · reload re-reads from disk\",\n argsHint: \"[reload]\",\n },\n permissions: {\n description:\n \"show / edit shell allowlist (builtin read-only · per-project: ~/.reasonix/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 \"shrink oversized tool results AND tool-call args (edit_file search/replace) in the log; cap in tokens, default 4000\",\n argsHint: \"[tokens]\",\n },\n keys: { description: \"show all keyboard shortcuts and prompt prefixes\" },\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 `reasonix 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 REASONIX.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 Reasonix.\",\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: \"Doesn't look like a DeepSeek key. They start with 'sk-' and are 30+ chars.\",\n apiKeyPreview: \"preview: {redacted}\",\n presetTitle: \"Pick a preset\",\n mcpTitle: \"Which MCP servers should Reasonix 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 },\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 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 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. Reasonix 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 /forget (delete the session) or /clear (drop the displayed history) to start fresh.\",\n contextOverflowTooMany: \"too many tokens\",\n auth401:\n \"Authentication failed (DeepSeek 401): {inner}. Your API key is rejected. Fix with `reasonix 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 Reasonix. 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 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>/REASONIX.md (committable).\",\n helpMemoryPinEx:\n \" Example: #findByEmail must be case-insensitive\",\n helpMemoryGlobal:\n \" #g <note> append <note> to ~/.reasonix/REASONIX.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: \" reasonix chat --session <name> use a different named session\",\n helpSessionNone: \" reasonix 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 },\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>/.reasonix/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: reasonix {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 `reasonix update` in another terminal to force it synchronously.\",\n updateLatest: \"latest: reasonix {version}\",\n updateUpToDate: \"you're on the latest. nothing to do.\",\n updateNpxHint: \"you're running via npx — the next `npx reasonix ...` 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 \" reasonix update (interactive, dry-run supported via --dry-run)\",\n updateUpgradeCmd2: \" npm install -g reasonix@latest (direct)\",\n updateInSessionDisabled: \"in-session install is deliberately disabled — the npm 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 `reasonix code` — chat mode doesn't apply edits.\",\n historyCodeOnly: \"/history is only available inside `reasonix code`.\",\n showCodeOnly: \"/show is only available inside `reasonix code`.\",\n applyCodeOnly: \"/apply is only available inside `reasonix code` (nothing to apply here).\",\n discardCodeOnly: \"/discard is only available inside `reasonix code`.\",\n planCodeOnly:\n \"/plan is only available inside `reasonix 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 `reasonix 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 `reasonix 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 `reasonix code`.\",\n checkpointCodeOnly:\n \"/checkpoint is only available inside `reasonix 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 `reasonix 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 `reasonix 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 — Reasonix 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 `reasonix code` — they edit the project-scoped allowlist (`~/.reasonix/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 `reasonix 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 `reasonix 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 },\n jobs: {\n codeOnly: \"/jobs is only available inside `reasonix code`.\",\n killCodeOnly: \"/kill is only available inside `reasonix code`.\",\n logsCodeOnly: \"/logs is only available inside `reasonix 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 (REASONIX_MEMORY=off in env). Unset the var to re-enable — no REASONIX.md or ~/.reasonix/memory content will be pinned in the meantime.\",\n noRoot:\n \"no working directory on this session — `/memory` needs a root to resolve REASONIX.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 ~/.reasonix/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. ~/.reasonix/memory/global/ — your cross-project private memory.\",\n layerProjectHash: \" 3. ~/.reasonix/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 `reasonix setup` to pick some, or launch with --mcp \"<spec>\". `reasonix 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: `reasonix mcp list` · deeper diagnosis: `reasonix mcp inspect <spec>`.\",\n fallbackServers: \"MCP servers ({count}):\",\n fallbackTools: \"Tools in registry ({count}):\",\n fallbackChange: \"To change this set, exit and run `reasonix 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 },\n init: {\n codeOnly:\n \"/init only works in code mode (it needs filesystem tools).\\nRun `reasonix code [path]` to start a session rooted at the\\nproject you want to initialize, then run /init.\",\n exists: \"▸ REASONIX.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 REASONIX.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. Reasonix reads skills from:\",\n listProjectScope:\n \" · <project>/.reasonix/skills/<name>/SKILL.md (or <name>.md) — project scope\",\n listGlobalScope: \" · ~/.reasonix/skills/<name>/SKILL.md (or <name>.md) — global scope\",\n listProjectOnly: \" (project scope is only active in `reasonix 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};\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 },\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: \"打印 Reasonix 版本。\",\n update: \"检查较新版本的 Reasonix 并安装。\",\n index: \"构建(或增量刷新)本地语义搜索索引。\",\n },\n ui: {\n welcome: \"随时运行 `reasonix` 开始聊天 — 您的设置将被记住。\",\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 dashboardAutoStartFailed:\n \"▲ 仪表板自动启动失败 ({reason}) — 尝试 /dashboard,或传递 --no-dashboard 以静默\",\n systemAppendHint: \"追加指令到代码系统提示词。不替换默认提示词 — 在其后添加。\",\n systemAppendFileHint:\n \"追加文件内容到代码系统提示词。不替换默认提示词。UTF-8,相对于 cwd 或绝对路径。\",\n resumedSession:\n '▸ 已恢复会话 \"{name}\",包含 {count} 条历史消息 · /forget 重新开始 · /sessions 列出',\n newSession: '▸ 会话 \"{name}\" (新) — 随聊随存 · /forget 删除 · /sessions 列出',\n ephemeralSession: \"▸ 临时聊天 (不保存会话) — 去掉 --no-session 以启用保存\",\n restoredEdits:\n \"▸ 从中断的运行中恢复了 {count} 个待处理的编辑块 — /apply 提交或 /discard 放弃。\",\n resumedPlan: \"已恢复计划 · {when}{summary}\",\n tipEditBindings:\n \"▸ 提示:编辑门控快捷键\\n y / n 接受或放弃待处理的编辑\\n Shift+Tab 切换 预览 ↔ 自动 (持久化;自动模式立即应用)\\n u 撤消上次自动应用的批处理 (在 5 秒横幅内)\\n 当前模式显示在底部状态栏。随时运行 /keys 查看完整列表。\\n (此提示仅显示一次 — 之后将隐藏。)\",\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: \"本次运行忽略 ~/.reasonix/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 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 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: \"显示 / 管理固定记忆(REASONIX.md + ~/.reasonix/memory)\",\n argsHint: \"[list|show <name>|forget <name>|clear <scope> confirm]\",\n },\n skill: {\n description: \"列出 / 运行用户技能(<project>/.reasonix/skills + ~/.reasonix/skills)\",\n argsHint: \"[list|show <name>|<name> [args]]\",\n },\n hooks: {\n description: \"列出活跃的 hooks(.reasonix/ 下的 settings.json)· reload 从磁盘重新读取\",\n argsHint: \"[reload]\",\n },\n permissions: {\n description: \"显示 / 编辑 shell 允许列表(内置只读 · 项目级:~/.reasonix/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 plans: { description: \"列出此会话的活跃 + 归档计划(最新在前)\" },\n replay: {\n description: \"加载归档计划为只读的时间旅行快照(默认:最新)\",\n argsHint: \"[N]\",\n },\n sessions: { description: \"列出已保存的会话(当前标记为 ▸)\" },\n setup: { description: \"提醒您退出并运行 `reasonix 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 \"扫描项目并合成基线 REASONIX.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: \"欢迎使用 Reasonix。\",\n apiKeyPrompt: \"粘贴你的 DeepSeek API key 开始使用。\",\n apiKeyGetOne: \"在此获取:https://platform.deepseek.com/api_keys\",\n apiKeySavedLocally: \"保存在本地:{path}\",\n apiKeyInputLabel: \"key › \",\n apiKeyInvalid: \"这看起来不像 DeepSeek 的 key。它们以 'sk-' 开头,30 字符以上。\",\n apiKeyPreview: \"预览:{redacted}\",\n presetTitle: \"选择预设\",\n mcpTitle: \"Reasonix 要为你接入哪些 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 },\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 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 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)。通常是单个工具结果太大。Reasonix 默认将新工具结果限制在 8k tokens,并在会话加载时自动修复超大历史 — 重启常能清掉。如果仍然溢出,运行 /forget(删除会话)或 /clear(丢弃显示中的历史)从头开始。\",\n contextOverflowTooMany: \"tokens 数量过多\",\n auth401:\n \"认证失败(DeepSeek 401):{inner}。你的 API key 被拒绝。运行 `reasonix 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 服务端问题,不是 Reasonix 故障。已按指数退避重试 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 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>/REASONIX.md(可提交)。\",\n helpMemoryPinEx: \" 示例:#findByEmail 必须区分大小写\",\n helpMemoryGlobal:\n \" #g <note> 将 <note> 追加到 ~/.reasonix/REASONIX.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: \" reasonix chat --session <name> 使用不同的命名会话\",\n helpSessionNone: \" reasonix 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 },\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>/.reasonix/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: \"当前:reasonix {version}\",\n updateLatestPending: \"最新:(尚未解析 — 后台检查进行中或离线)\",\n updateRetryHint: \"已触发新的注册表获取 — 几秒后重试 `/update`,\",\n updateRetryHint2: \"或在另一个终端运行 `reasonix update` 强制同步执行。\",\n updateLatest: \"最新:reasonix {version}\",\n updateUpToDate: \"您已是最新版本。无需操作。\",\n updateNpxHint: \"您正在通过 npx 运行 — 下次 `npx reasonix ...` 启动时将自动获取。\",\n updateNpxForce: \"要强制刷新:`npm cache clean --force`。\",\n updateUpgradeHint: \"要升级,请退出此会话并运行:\",\n updateUpgradeCmd1: \" reasonix update (交互式,支持 --dry-run 预览)\",\n updateUpgradeCmd2: \" npm install -g reasonix@latest (直接安装)\",\n updateInSessionDisabled: \"会话内安装被刻意禁用 — npm spawn 会\",\n updateInSessionDisabled2: \"破坏此 TUI 的渲染,且 Windows 可能锁定运行中的二进制文件。\",\n statsNoData: \"尚无使用数据。\",\n statsEveryTurn: \"您在此运行的每一轮都会追加一条记录 — 此会话的轮次\",\n statsWillAppear: \"将在您发送消息后显示在仪表板中。\",\n },\n edits: {\n undoCodeOnly: \"/undo 仅在 `reasonix code` 中可用 — 聊天模式不应用编辑。\",\n historyCodeOnly: \"/history 仅在 `reasonix code` 中可用。\",\n showCodeOnly: \"/show 仅在 `reasonix code` 中可用。\",\n applyCodeOnly: \"/apply 仅在 `reasonix code` 中可用(此处无内容可应用)。\",\n discardCodeOnly: \"/discard 仅在 `reasonix code` 中可用。\",\n planCodeOnly: \"/plan 仅在 `reasonix code` 中可用 — 聊天模式不限制工具写入。\",\n planOn:\n \"▸ 计划模式开启 — 写入工具被限制;模型必须先调用 `submit_plan` 才能执行任何操作。(模型也可以在计划模式关闭时自主调用 submit_plan 处理大型任务 — 此开关是更强的显式约束。)输入 /plan off 退出。\",\n planOff: \"▸ 计划模式关闭 — 写入工具再次可用。模型仍可为大型任务自主提出计划。\",\n modeCodeOnly: \"/mode 仅在 `reasonix 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 仅在 `reasonix code` 中可用(需要有根的 git 仓库)。\",\n commitUsage: '用法:/commit \"提交消息\" — 在 {root} 中运行 `git add -A && git commit -m \"…\"`',\n walkCodeOnly: \"/walk 仅在 `reasonix code` 中可用。\",\n checkpointCodeOnly: \"/checkpoint 仅在 `reasonix 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 仅在 `reasonix 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 仅在 `reasonix 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 \"未设置会话预算 — Reasonix 将持续运行直到您停止。使用以下方式设置:/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 仅在 `reasonix code` 中可用 — 它们编辑项目范围的允许列表(`~/.reasonix/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` 是按会话的。在项目中运行 `reasonix code` 以获取会话。\",\n activePlan: \"▸ 活跃计划{label} — {done}/{total} 步骤已完成 · 最后触及 {when}\",\n activeNone: \"▸ 活跃计划:(无)\",\n noArchives: \"此会话尚无归档计划 — 当每个步骤完成时自动归档\",\n archivedHeader: \"已归档({count}):\",\n replayNoSession:\n \"未附加会话 — `/replay` 是按会话的。在项目中运行 `reasonix code` 以获取会话。\",\n replayNoArchives:\n \"此会话尚无归档计划 — `/replay` 在计划完成后启用(每个步骤完成时自动归档)。\",\n replayInvalidIndex:\n \"无效索引 — `/replay` 接受 1..{max}(最新 = 1)。使用 `/plans` 查看列表。\",\n archivedRow: \" ✓ {when} {total}步 · {completion} {label}\",\n completionComplete: \"已完成\",\n stopAborted: \"▸ 计划已停止 — 模型已中止;输入后续内容继续,或开始新任务。\",\n },\n jobs: {\n codeOnly: \"/jobs 仅在 `reasonix code` 中可用。\",\n killCodeOnly: \"/kill 仅在 `reasonix code` 中可用。\",\n logsCodeOnly: \"/logs 仅在 `reasonix 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 \"记忆已禁用(环境变量 REASONIX_MEMORY=off)。取消设置该变量以重新启用 — 此期间不会固定任何 REASONIX.md 或 ~/.reasonix/memory 内容。\",\n noRoot:\n \"此会话无工作目录 — `/memory` 需要一个根目录来解析 REASONIX.md。(在测试环境中运行?)\",\n listEmpty:\n \"尚无用户记忆。模型可以调用 `remember` 保存一个,或您可以在 ~/.reasonix/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. ~/.reasonix/memory/global/ — 您的跨项目私有记忆。\",\n layerProjectHash: \" 3. ~/.reasonix/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 服务器。运行 `reasonix setup` 选择一些,或使用 --mcp \"<spec>\" 启动。`reasonix mcp list` 显示目录。',\n toolsLabel: \" 工具 {count}\",\n resourcesHint: \"`/resource` 浏览+读取\",\n promptsHint: \"`/prompt` 浏览+获取\",\n awarenessOnly: \"聊天模式目前消耗工具;资源+提示在此展示供了解。\",\n catalogHint: \"完整目录:`reasonix mcp list` · 深度诊断:`reasonix mcp inspect <spec>`。\",\n fallbackServers: \"MCP 服务器({count}):\",\n fallbackTools: \"注册表中的工具({count}):\",\n fallbackChange: \"要更改此设置,请退出并运行 `reasonix 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 },\n init: {\n codeOnly:\n \"/init 仅在代码模式下工作(需要文件系统工具)。\\n运行 `reasonix code [path]` 启动一个以您要初始化的项目为根的会话,\\n然后运行 /init。\",\n exists: \"▸ REASONIX.md 已存在于 {path}\",\n existsForce: \" /init force 从头重新生成(覆盖)\",\n existsEdit: \" 或手动编辑 — 它只是 markdown。当前文件已\",\n existsPinned: \" 固定到每次启动的系统提示词中。\",\n info: \"▸ /init — 模型将扫描项目并合成 REASONIX.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: \"未找到技能。Reasonix 从以下位置读取技能:\",\n listProjectScope:\n \" · <project>/.reasonix/skills/<name>/SKILL.md (或 <name>.md) — 项目范围\",\n listGlobalScope: \" · ~/.reasonix/skills/<name>/SKILL.md (或 <name>.md) — 全局范围\",\n listProjectOnly: \" (项目范围仅在 `reasonix 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};\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/** 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.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_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(\"reasonix/package.json\")), \"data\", \"deepseek-tokenizer.json.gz\"),\n );\n } catch {\n /* Not installed as `reasonix/` — 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\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\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 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 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 JSON.stringify({\n error: `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 // 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 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 return 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 return JSON.stringify(e.toToolResult());\n } catch {\n /* fall through to the default shape */\n }\n }\n return JSON.stringify({\n error: `${e.name}: ${e.message}`,\n });\n }\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 {\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","/** 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}\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\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}\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 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\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 };\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 `~/.reasonix/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(), \".reasonix\", \"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/** 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 — concurrent crash here = `/forget`. */\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\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\";\n\n/** USD per 1M tokens; CNY sheet converted at fixed 7.2 — revisit if FX moves >±5%. */\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 // Compat aliases — priced as v4-flash per the deprecation notice.\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/** Prompt-side window only; completion caps live server-side and don't affect this gauge. */\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 the table — safe lower bound. */\nexport const DEFAULT_CONTEXT_TOKENS = 131_072;\n\nexport function costUsd(model: string, usage: Usage): number {\n const p = DEEPSEEK_PRICING[model];\n if (!p) 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 = DEEPSEEK_PRICING[model];\n if (!p) 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 = DEEPSEEK_PRICING[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 = DEEPSEEK_PRICING[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","import type { DeepSeekClient } from \"../client.js\";\nimport { t } from \"../i18n/index.js\";\n\nexport interface DeepSeekProbeResult {\n reachable: boolean;\n}\n\nexport function formatLoopError(err: Error, probe?: DeepSeekProbeResult): 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 const m = /^DeepSeek (\\d{3}):\\s*([\\s\\S]*)$/.exec(msg);\n if (!m) return msg;\n const status = m[1] ?? \"\";\n const body = m[2] ?? \"\";\n const inner = extractDeepSeekErrorMessage(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 formatDeepSeek5xx(status, probe);\n return msg;\n}\n\nexport function is5xxError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const m = /^DeepSeek (5\\d{2}):/.exec(err.message ?? \"\");\n return m !== null;\n}\n\nexport async function probeDeepSeekReachable(\n client: DeepSeekClient,\n timeoutMs = 1500,\n): Promise<DeepSeekProbeResult> {\n const balance = await client.getBalance({ signal: AbortSignal.timeout(timeoutMs) });\n return { reachable: balance !== null };\n}\n\nfunction is5xxStatus(status: string): boolean {\n return status === \"500\" || status === \"502\" || status === \"503\" || status === \"504\";\n}\n\nfunction formatDeepSeek5xx(status: string, probe?: DeepSeekProbeResult): 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 extractDeepSeekErrorMessage(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","/** True when the model emits reasoning_content and requires it round-tripped on follow-ups. */\nexport function isThinkingModeModel(model: string): boolean {\n if (model.includes(\"reasoner\")) return true;\n if (model === \"deepseek-v4-flash\" || model === \"deepseek-v4-pro\") return true;\n return false;\n}\n\n/** Pins extra_body.thinking.type; `undefined` lets third-party endpoints skip the field. */\nexport function thinkingModeForModel(model: string): \"enabled\" | \"disabled\" | undefined {\n if (model === \"deepseek-chat\") return \"disabled\";\n if (model.includes(\"reasoner\")) return \"enabled\";\n if (model === \"deepseek-v4-flash\" || model === \"deepseek-v4-pro\") return \"enabled\";\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 } from \"../types.js\";\nimport { shrinkOversizedToolResults, shrinkOversizedToolResultsByTokens } from \"./shrink.js\";\nimport { isThinkingModeModel } from \"./thinking.js\";\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 const needed = new Set<string>();\n for (const call of msg.tool_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);\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","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\n reset(): void {\n this.count = 0;\n this.types = {};\n }\n\n /** True ONLY on the call where the count crosses FAILURE_ESCALATION_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 < FAILURE_ESCALATION_THRESHOLD && this.count >= FAILURE_ESCALATION_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 readonly 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 only via `addTool`; bypassing it 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 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 DeepSeekClient, 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 { t } from \"./i18n/index.js\";\nimport { formatLoopError, is5xxError, probeDeepSeekReachable } 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 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 { 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 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\nconst ESCALATION_MODEL = \"deepseek-v4-pro\";\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: DeepSeekClient;\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 session?: string;\n /** PreToolUse + PostToolUse only — UserPromptSubmit / Stop live at the App boundary. */\n hooks?: ResolvedHook[];\n /** `cwd` reported to hooks; `reasonix 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}\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: DeepSeekClient;\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 _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\n private _proArmedForNextTurn = false;\n private _escalateThisTurn = false;\n private readonly _turnFailures = new TurnFailureTracker();\n private _turnSelfCorrected = false;\n private _foldedThisTurn = false;\n private context!: ContextManager;\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 // 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\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 isMutating = (call: ToolCall): boolean => {\n const name = call.function?.name;\n if (!name) return false;\n const def = registry.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 {\n /* ignore — fall through */\n }\n }\n return def.readOnly !== true;\n };\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,\n isStormExempt,\n stormThreshold: parsePositiveIntEnv(process.env.REASONIX_STORM_THRESHOLD),\n stormWindow: parsePositiveIntEnv(process.env.REASONIX_STORM_WINDOW),\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 // 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 messages but keeps session + immutable prefix (cache-first invariant). */\n clearLog(): { dropped: number } {\n const dropped = this.log.length;\n this.log.compactInPlace([]);\n if (this.sessionName) {\n try {\n rewriteSession(this.sessionName, []);\n } catch {\n /* disk issue shouldn't block the in-memory clear */\n }\n }\n this.scratch.reset();\n return { dropped };\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 private modelForCurrentCall(): string {\n return this._escalateThisTurn ? ESCALATION_MODEL : 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 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\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 = (blocking?.stderr || blocking?.stdout || \"blocked by PreToolUse hook\").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 }\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._turnSelfCorrected = false;\n this._escalateThisTurn = false;\n this._foldedThisTurn = false;\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 !== ESCALATION_MODEL;\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 probeDeepSeekReachable(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() !== ESCALATION_MODEL &&\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: ESCALATION_MODEL, 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: ESCALATION_MODEL,\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.REASONIX_TOOL_DISPATCH ?? \"auto\").toLowerCase() === \"serial\";\n const parallelMaxParsed = Number.parseInt(process.env.REASONIX_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.\n for (const call of chunk) {\n yield {\n turn: this._turn,\n role: \"tool_start\",\n content: \"\",\n toolName: call.function?.name ?? \"\",\n toolArgs: call.function?.arguments ?? \"{}\",\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: ESCALATION_MODEL,\n breakdown: this._turnFailures.formatBreakdown(),\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 };\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","/** 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 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 = async (\n dirAbs: string,\n dirRel: string,\n layers: readonly GitignoreLayer[],\n ): Promise<void> => {\n if (out.length >= maxResults) 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 // Stats batched per directory to amortize syscall overhead. Recursion stays\n // sequential so the merged DFS order matches the sync walker's contract.\n const fileEnts: Dirent[] = [];\n for (const ent of entries) {\n if (out.length >= maxResults) break;\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 // Drain pending file stats from THIS directory before\n // descending so the output order stays DFS-alphabetical.\n if (fileEnts.length > 0) {\n await statBatch(fileEnts, dirAbs, dirRel, out, maxResults, effectiveLayers);\n fileEnts.length = 0;\n if (out.length >= maxResults) return;\n }\n await walk(absPath, relPath, effectiveLayers);\n } else if (ent.isFile() || ent.isSymbolicLink()) {\n // Symlinks land in the same batch — statBatch resolves them and drops\n // any whose target isn't a regular file (broken or symlink-to-dir).\n fileEnts.push(ent);\n }\n }\n if (fileEnts.length > 0 && out.length < maxResults) {\n await statBatch(fileEnts, dirAbs, dirRel, out, maxResults, effectiveLayers);\n }\n };\n\n await walk(rootAbs, \"\", []);\n return out;\n}\n\nasync function statBatch(\n ents: readonly Dirent[],\n dirAbs: string,\n dirRel: string,\n out: FileWithStats[],\n maxResults: number,\n layers: readonly GitignoreLayer[],\n): Promise<void> {\n const accepted: Dirent[] = [];\n for (const e of ents) {\n if (out.length + accepted.length >= maxResults) break;\n if (ignoredByLayers(layers, join(dirAbs, e.name), false)) continue;\n accepted.push(e);\n }\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()) {\n // Drop broken symlinks and symlinks-to-dirs (latter would cycle).\n if (!s || !s.isFile) continue;\n }\n out.push({\n path: dirRel ? `${dirRel}/${ent.name}` : ent.name,\n mtimeMs: s?.mtimeMs ?? 0,\n });\n }\n}\n\n/** Trailing-token only, anchored at end-of-input — distinct from `AT_MENTION_PATTERN` which scans all. */\nexport const AT_PICKER_PREFIX = /(?:^|\\s)@([a-zA-Z0-9_./\\\\-]*)$/;\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)@([a-zA-Z0-9_./\\\\-]+)/g;\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\n// @url mentions — async sibling of @path. Matches `@http(s)://...` after a\n// word boundary, fetches each URL once per session (in-memory cache), and\n// appends a \"Referenced URLs\" block under the prompt the model sees. Uses\n// the same web-fetch + HTML-strip pipeline as the model's `web_fetch` tool\n// so a `@url` reference and a model-issued fetch produce identical content.\n\n/** Trailing punctuation stripped separately — URLs legitimately contain `,` `.` `)` in query strings. */\nexport const AT_URL_PATTERN = /(?<=^|\\s)@(https?:\\/\\/\\S+)/g;\n\n/** Default cap on inlined URL body (chars). Matches DEFAULT_AT_MENTION_MAX_BYTES order-of-magnitude. */\nexport const DEFAULT_AT_URL_MAX_CHARS = 32_000;\n\nexport interface AtUrlExpansion {\n /** The raw `@url` token as it appeared in the text. */\n token: string;\n /** Absolute URL (after trailing-punctuation strip). */\n url: string;\n /** True if content was inlined. False = skipped (reason in `skip`). */\n ok: boolean;\n /** Page title when extractable from `<title>`. */\n title?: string;\n /** Char count of the (post-truncation) inlined body. */\n chars?: number;\n /** True iff the original page exceeded `maxChars` and was clipped. */\n truncated?: boolean;\n /** Why the mention was skipped — set when ok=false. */\n skip?: \"fetch-error\" | \"non-text\" | \"timeout\" | \"blocked\";\n /** Free-form error message attached to skip outcomes. */\n error?: string;\n}\n\nexport interface AtUrlOptions {\n /** Max chars of inlined body per URL. Default DEFAULT_AT_URL_MAX_CHARS. */\n maxChars?: number;\n /** Per-URL fetch timeout in ms. */\n timeoutMs?: number;\n fetcher?: (\n url: string,\n opts: { maxChars?: number; timeoutMs?: number; signal?: AbortSignal },\n ) => Promise<{ url: string; title?: string; text: string; truncated: boolean }>;\n cache?: Map<string, AtUrlExpansion & { body?: string }>;\n /** Forward Esc/abort to the fetcher. */\n signal?: AbortSignal;\n}\n\nexport async function expandAtUrls(\n text: string,\n opts: AtUrlOptions = {},\n): Promise<{ text: string; expansions: AtUrlExpansion[] }> {\n const maxChars = opts.maxChars ?? DEFAULT_AT_URL_MAX_CHARS;\n const fetcher = opts.fetcher;\n if (!fetcher) {\n throw new Error(\"expandAtUrls: fetcher option is required (wire src/tools/web.ts:webFetch)\");\n }\n\n // De-dupe by URL so the same `@https://x.com` referenced twice fetches once.\n const seen = new Map<string, AtUrlExpansion>();\n const bodies = new Map<string, string>();\n const order: string[] = [];\n\n for (const match of text.matchAll(AT_URL_PATTERN)) {\n const rawUrl = match[1] ?? \"\";\n const url = stripUrlTail(rawUrl);\n if (!url) continue;\n if (seen.has(url)) continue;\n\n const cached = opts.cache?.get(url);\n if (cached) {\n seen.set(url, cached);\n if (cached.body) bodies.set(url, cached.body);\n order.push(url);\n continue;\n }\n\n let expansion: AtUrlExpansion;\n let body = \"\";\n try {\n const page = await fetcher(url, {\n maxChars,\n timeoutMs: opts.timeoutMs,\n signal: opts.signal,\n });\n body = page.text;\n expansion = {\n token: `@${url}`,\n url,\n ok: true,\n title: page.title,\n chars: body.length,\n truncated: page.truncated,\n };\n } catch (err) {\n const message = (err as Error).message ?? String(err);\n // Tag a few common shapes so the UI can hint at causes.\n let skip: AtUrlExpansion[\"skip\"] = \"fetch-error\";\n if (/aborted|timeout/i.test(message)) skip = \"timeout\";\n else if (/40\\d|forbidden|access denied|captcha/i.test(message)) skip = \"blocked\";\n expansion = {\n token: `@${url}`,\n url,\n ok: false,\n skip,\n error: message,\n };\n }\n seen.set(url, expansion);\n if (body) bodies.set(url, body);\n if (opts.cache) opts.cache.set(url, { ...expansion, body });\n order.push(url);\n }\n\n if (seen.size === 0) return { text, expansions: [] };\n\n const expansions = order.map((u) => seen.get(u)!).filter(Boolean);\n const blocks: string[] = [];\n for (const ex of expansions) {\n if (ex.ok) {\n const titleAttr = ex.title ? ` title=\"${escapeAttr(ex.title)}\"` : \"\";\n const truncTag = ex.truncated ? ' truncated=\"true\"' : \"\";\n const body = bodies.get(ex.url) ?? \"\";\n blocks.push(`<url href=\"${ex.url}\"${titleAttr}${truncTag}>\\n${body}\\n</url>`);\n } else {\n const reasonAttr = ex.skip ?? \"fetch-error\";\n blocks.push(`<url href=\"${ex.url}\" skipped=\"${reasonAttr}\" />`);\n }\n }\n const augmented = `${text}\\n\\n[Referenced URLs]\\n${blocks.join(\"\\n\\n\")}`;\n return { text: augmented, expansions };\n}\n\n/** Only strips `.,;:!?` and unmatched close-brackets — internal path / query punctuation preserved. */\nexport function stripUrlTail(raw: string): string {\n let s = raw;\n while (s.length > 0) {\n const last = s[s.length - 1]!;\n if (\".,;:!?\".includes(last)) {\n s = s.slice(0, -1);\n continue;\n }\n if (\")]}>\".includes(last)) {\n // Only strip if the matching open bracket isn't elsewhere in the\n // URL — avoids butchering legitimate `(thing)` query fragments.\n const open = ({ \")\": \"(\", \"]\": \"[\", \"}\": \"{\", \">\": \"<\" } as const)[\n last as \")\" | \"]\" | \"}\" | \">\"\n ];\n if (!s.includes(open)) {\n s = s.slice(0, -1);\n continue;\n }\n }\n break;\n }\n return s;\n}\n\nfunction escapeAttr(s: string): string {\n return s\n .replace(/\"/g, \""\")\n .replace(/[\\r\\n]+/g, \" \")\n .trim();\n}\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","/** REASONIX.md pinned into ImmutablePrefix.system; edits invalidate the prefix-cache fingerprint. */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport const PROJECT_MEMORY_FILE = \"REASONIX.md\";\nexport const PROJECT_MEMORY_MAX_CHARS = 8000;\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 = join(rootDir, PROJECT_MEMORY_FILE);\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 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.REASONIX_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 return `${basePrompt}\n\n# Project memory (REASONIX.md)\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 REASONIX.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 { 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 type MemoryType = \"user\" | \"feedback\" | \"project\" | \"reference\";\nexport type MemoryScope = \"global\" | \"project\";\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}\n\nexport interface MemoryStoreOptions {\n /** Override `~/.reasonix` — 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}\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 parseFrontmatter(raw: string): { data: Record<string, string>; body: string } {\n const lines = raw.split(/\\r?\\n/);\n if (lines[0] !== \"---\") return { data: {}, body: raw };\n const end = lines.indexOf(\"---\", 1);\n if (end < 0) return { data: {}, body: raw };\n const data: Record<string, string> = {};\n for (let i = 1; i < end; i++) {\n const line = lines[i];\n if (!line) continue;\n const m = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\\s*(.*)$/);\n if (m?.[1]) data[m[1]] = (m[2] ?? \"\").trim();\n }\n return {\n data,\n body: lines\n .slice(end + 1)\n .join(\"\\n\")\n .replace(/^\\n+/, \"\"),\n };\n}\n\nfunction formatFrontmatter(e: WriteInput & { createdAt: string }): string {\n return [\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 \"\",\n ].join(\"\\n\");\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(), \".reasonix\");\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 return {\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 }\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 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 readGlobalReasonixMemory(\n homeDir: string = join(homedir(), \".reasonix\"),\n): { path: string; content: string; originalChars: number; truncated: boolean } | null {\n const path = join(homeDir, \"REASONIX.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 applyGlobalReasonixMemory(basePrompt: string, homeDir?: string): string {\n if (!memoryEnabled()) return basePrompt;\n const dir = homeDir ?? join(homedir(), \".reasonix\");\n const mem = readGlobalReasonixMemory(dir);\n if (!mem) return basePrompt;\n return [\n basePrompt,\n \"\",\n \"# Global memory (~/.reasonix/REASONIX.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/** 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 } = {},\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 if (!global && !project) return basePrompt;\n const parts: string[] = [basePrompt];\n if (global) {\n parts.push(\n \"\",\n \"# User memory — global (~/.reasonix/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 = applyGlobalReasonixMemory(withProject);\n const withMemory = applyUserMemory(withGlobal, { projectRoot: rootDir });\n return applySkillsIndex(withMemory, { projectRoot: rootDir });\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 { 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}\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\nfunction parseFrontmatter(raw: string): { data: Record<string, string>; body: string } {\n const lines = raw.split(/\\r?\\n/);\n if (lines[0] !== \"---\") return { data: {}, body: raw };\n const end = lines.indexOf(\"---\", 1);\n if (end < 0) return { data: {}, body: raw };\n const data: Record<string, string> = {};\n for (let i = 1; i < end; i++) {\n const line = lines[i];\n if (!line) continue;\n const m = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\\s*(.*)$/);\n if (m?.[1]) data[m[1]] = (m[2] ?? \"\").trim();\n }\n return {\n data,\n body: lines\n .slice(end + 1)\n .join(\"\\n\")\n .replace(/^\\n+/, \"\"),\n };\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\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, \".reasonix\", SKILLS_DIRNAME),\n scope: \"project\",\n });\n }\n out.push({ dir: join(this.homeDir, \".reasonix\", 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 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 `reasonix code`\" };\n }\n const root =\n scope === \"project\"\n ? join(this.projectRoot ?? \"\", \".reasonix\", SKILLS_DIRNAME)\n : join(this.homeDir, \".reasonix\", 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, skillStubBody(name), { 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 };\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`;\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\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().filter((s) => s.description);\n if (skills.length === 0) return basePrompt;\n const lines = skills.map(skillIndexLine);\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\nexport const ESCALATION_CONTRACT = `Cost-aware escalation (when you're running on deepseek-v4-flash):\n\nIf a task CLEARLY exceeds what flash 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 flash. Request escalation ONLY when you would otherwise produce a guess or a visibly-mediocre answer. If in doubt, attempt the task on flash first; the system also escalates automatically if you hit 3+ repair / SEARCH-mismatch errors in a single turn (the user sees a typed breakdown).`;\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 { DEFAULT_INDEX_EXCLUDES } from \"../index/config.js\";\nimport type { ToolRegistry } from \"../tools.js\";\nimport { applyEdit, applyMultiEdit } from \"./fs/edit.js\";\nimport { globFiles } from \"./fs/glob.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;\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 /** Resolve path, enforce it's under rootDir, return absolute. */\n const safePath = (raw: unknown): string => {\n if (typeof raw !== \"string\" || raw.length === 0) {\n throw new Error(\"path must be a non-empty string\");\n }\n // Sandbox-root semantics: a leading POSIX-style `/` (or `\\` on\n // Windows) means \"from the project root\", not \"from the filesystem\n // root\". Models routinely write `path: \"/\"` or `path: \"/src/foo.ts\"`\n // intending the sandbox root — without this normalization,\n // path.resolve interprets `/` as the actual drive root (`F:\\` on\n // Windows, `/` on POSIX) and the escape check rightly rejects it,\n // confusing the model. Strip leading separators so the rest of the\n // resolution treats the input as relative to rootDir. Drive-letter\n // absolutes (`C:\\foo`) and Unix absolutes outside rootDir still\n // get caught by the relative-escape check below.\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 const normRoot = pathMod.resolve(rootDir);\n // Use relative() to catch any `..` segments that escape.\n const rel = pathMod.relative(normRoot, resolved);\n if (rel.startsWith(\"..\") || pathMod.isAbsolute(rel)) {\n throw new Error(\n `path escapes sandbox root (${normRoot}): ${raw} — workspace is pinned at launch; quit and relaunch with \\`reasonix code --dir <path>\\` to work in a different folder`,\n );\n }\n return resolved;\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 rather than dumping everything. If you need the middle, re-call with a range. Prefer search_content to locate a symbol first, then read_file with a range around the hit — 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 (args: { path: string; head?: number; tail?: number; range?: string }) => {\n const abs = safePath(args.path);\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 return [\n `[auto-preview: head ${AUTO_PREVIEW_HEAD_LINES} + tail ${AUTO_PREVIEW_TAIL_LINES} of ${totalLines} lines]`,\n head,\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 ].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 }) => {\n const abs = safePath(args.path ?? \".\");\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 (args: { path?: string; maxDepth?: number; include_deps?: boolean }) => {\n const startAbs = safePath(args.path ?? \".\");\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 safePath(args.path ?? \".\"),\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. 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 },\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 },\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 safePath(args.path ?? \".\"),\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({ rootDir, skipDirNames: SKIP_DIR_NAMES }, safePath(args.path ?? \".\"), {\n ...args,\n 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 }) => {\n const abs = safePath(args.path);\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 }) => {\n const abs = safePath(args.path);\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 }) =>\n applyEdit(rootDir, safePath(args.path), 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 (args: { edits: Array<{ path: string; search: string; replace: string }> }) => {\n const resolved = (args.edits ?? []).map((e) => ({\n abs: safePath(e?.path),\n search: e?.search,\n replace: e?.replace,\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 }) => {\n const abs = safePath(args.path);\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 }) => {\n const src = safePath(args.source);\n const dst = safePath(args.destination);\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 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","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\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 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 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\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 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 if (ctxLines === 0) {\n for (const li of hits) {\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 continue;\n }\n const hitSet = new Set(hits);\n let prevWindowEnd = -2;\n for (const li of hits) {\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 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 {\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 `~/.reasonix` (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 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 enum: [\"user\", \"feedback\", \"project\", \"reference\"],\n description:\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 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 `reasonix 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 },\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 }) => {\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 `reasonix 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 });\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. Call this after finishing each step, then immediately continue with the NEXT step — do not stop or wait for the user. The TUI updates the plan card's progress in place. 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.\";\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 if (verdict.type === \"approve\") return \"plan approved\";\n if (verdict.type === \"refine\") throw new Error(\"user requested refinement\");\n throw new Error(\"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 ESCALATION_CONTRACT,\n NEGATIVE_CLAIM_RULE,\n TUI_FORMATTING_RULES,\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\nconst DEFAULT_SUBAGENT_SYSTEM = `You are a Reasonix 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${ESCALATION_CONTRACT}\n\n${TUI_FORMATTING_RULES}`;\n\nconst DEFAULT_MAX_RESULT_CHARS = 8000;\nconst DEFAULT_MAX_ITERS = 16;\nconst MIN_MAX_ITERS = 1;\nconst MAX_MAX_ITERS = 32;\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/** 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 const childPrefix = new ImmutablePrefix({\n system: opts.system,\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 — `reasonix 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 ?? DEFAULT_SUBAGENT_SYSTEM;\n // Bake project memory into the default once — re-reading on every\n // spawn would (a) make the child prefix unstable when REASONIX.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.\n const defaultSystem = 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\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. Use for: deep codebase exploration that would flood the main context, multi-step research where you only need the conclusion, or any focused subtask whose intermediate reasoning the user does not need to see. The subagent inherits all your tools (filesystem, shell, web, MCP, etc.) but runs in its own isolated message log — its tool calls and reasoning never enter your context. Only the final assistant message comes back as this tool's result. 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 system =\n typeof args.system === \"string\" && args.system.trim().length > 0\n ? args.system.trim()\n : (typeSpec?.system ?? defaultSystem);\n const model =\n typeof args.model === \"string\" && args.model.startsWith(\"deepseek-\")\n ? args.model\n : defaultModel;\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 return formatSubagentResult(result);\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 and return its combined stdout+stderr.\\n\\nConstraints (read these before the first call):\\n• Chain operators `|`, `||`, `&&`, `;` ARE supported — parsed natively, no shell invoked, so semantics are identical on Windows / macOS / Linux. Each chain segment is allowlist-checked individually: `git status | grep main` runs if both halves are allowed.\\n• File redirects ARE supported: `>` truncate, `>>` append, `<` stdin from file, `2>` / `2>>` stderr to file, `2>&1` merge stderr→stdout, `&>` both to file. Targets resolve relative to the project root. At most one redirect per fd per segment.\\n• Background `&`, heredoc `<<`, command substitution `$(…)`, subshells `(…)`, and process substitution `<(…)` are NOT supported. Wrap a literal `&` arg in quotes; for input use a `<` file or the binary's own --input flag.\\n• Env-var expansion `$VAR` is NOT performed — `$VAR` is passed as a literal string. Use the binary's own --env flag or substitute the value yourself.\\n• `cd` DOES NOT PERSIST between calls — each call spawns a fresh process rooted at the project. `cd` also does not persist within parsed chains like `cd dir && command`. Use a command-native cwd flag instead: `npm --prefix <dir> run <script>`, `npm --prefix <dir> exec -- <bin>`, `git -C <dir> ...`, `cargo -C <dir> ...`, `pytest <dir>/tests`.\\n• Glob patterns (`*.ts`) are passed through as literal arguments — no shell expansion. Use `grep -r`, `rg`, `find -name`, etc.\\n• Avoid commands with unbounded output (`netstat -ano`, `find /`, etc.) — they waste tokens. Filter at source: `netstat -ano -p TCP`, `find src -name '*.ts'`, `grep -c`, `wc -l`.\\n\\nCommon read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually — after edits, run the project's tests to verify.\",\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 if (!isAllowAll() && !isCommandAllowed(cmd, getExtraAllowed())) {\n const gate = ctx?.confirmationGate ?? pauseGate;\n const choice = await gate.ask({ kind: \"run_command\", payload: { command: cmd } });\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 effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));\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 (dev server, watcher, any command that doesn't naturally exit) and detach. Waits up to `waitSec` seconds for startup (or until the output matches a readiness signal like 'Local:', 'listening on', 'compiled successfully'), then returns the job id + startup preview. The process keeps running; call `job_output` to tail its logs, `stop_job` to kill it, `list_jobs` to see all running jobs.\\n\\nSame shell constraints as run_command: NO `&&` / `||` / `|` / `;` / `>` / `<` / `2>&1`, `cd` doesn't persist. Dev servers that need a subdirectory: use the tool's own --prefix / --cwd flag. For Vite specifically, `--prefix` on npm only tells npm where package.json is; vite's server root still defaults to process cwd, so pass `vite <project-dir>` or configure via `vite.config.ts` root.\\n\\nUSE THIS — not `run_command` — for: npm/yarn/pnpm run dev, uvicorn / flask run, go run, cargo watch, tsc --watch, webpack serve, anything with 'dev' / 'serve' / 'watch' in the name.\",\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({ kind: \"run_background\", payload: { command: cmd } });\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 until a background job exits or produces new output, bounded by `timeoutMs`. Use this instead of polling `job_output` with identical args when you're intentionally waiting for state to change. Returns JSON with `exited`, `exitCode`, and `latestOutput`.\",\n readOnly: 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 nothing changes. Clamped to 0..30000. Default 5000.\",\n },\n },\n required: [\"jobId\"],\n },\n fn: async (args: { jobId: number; timeoutMs?: number }) => {\n const out = await jobs.waitForJob(args.jobId, { timeoutMs: args.timeoutMs });\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 child.on(\"close\", (code) => {\n job.running = false;\n job.exitCode = code;\n job.signalReady();\n job.signalClosed();\n });\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(id: number, opts: { timeoutMs?: number } = {}): 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(30_000, opts.timeoutMs ?? 5_000));\n const startOutput = job.output;\n let wakeOutput: (() => void) | null = null;\n const outputPromise = new Promise<void>((resolve) => {\n wakeOutput = resolve;\n job.outputWaiters.add(resolve);\n });\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n await Promise.race([\n job.closedPromise,\n outputPromise,\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n }),\n ]);\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 }\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 }\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\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: { ...process.env, 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);\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 = (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 = (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\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 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/** 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(`web_search ${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(\"web_search: Mojeek anti-bot page — rate-limited or blocked\");\n }\n throw new Error(\n `web_search: 0 results but response doesn't look like a real empty page (${html.length} chars, first 120: ${html.slice(0, 120).replace(/\\s+/g, \" \")})`,\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(`web_search: invalid SearXNG endpoint \"${raw}\"`);\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(`web_search: SearXNG endpoint must be http(s), got ${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 `web_search: Cannot reach SearXNG server at ${opts.endpoint ?? \"http://localhost:8080\"}. Please install SearXNG (https://github.com/searxng/searxng) and start it (e.g. \\`docker run -d -p 8080:8080 searxng/searxng\\`), or switch to the default engine with /search-engine mojeek.`,\n );\n }\n throw err;\n }\n if (!resp.ok) throw new Error(`web_search ${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(\n `web_search: 0 results but SearXNG response doesn't look like an empty results page (${html.length} chars)`,\n );\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 const timer = setTimeout(() => ctl.abort(), 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 } finally {\n clearTimeout(timer);\n opts.signal?.removeEventListener(\"abort\", cancel);\n }\n if (!resp.ok) throw new Error(`web_fetch ${resp.status} for ${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(\n `web_fetch refused: content-length ${declaredLen} bytes exceeds ${FETCH_MAX_BYTES}-byte cap (${url})`,\n );\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(\n `web_fetch refused: response body exceeded ${maxBytes}-byte cap (${total} bytes seen)`,\n );\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 `&lt;` into `<` because `&` ran before `<`. */\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(\"web_fetch: url must start with http:// or https://\");\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. \"reasonix chat\", \"bench/baseline\", \"bench/reasonix\"\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/reasonix/latest\";\n\n/** TTL for the on-disk cache entry. 24h keeps noise low; users who\n * want a fresh check can run `reasonix 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 === \"reasonix\"` 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 === \"reasonix\" && 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(), \".reasonix\", \"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 `reasonix 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\n/** False negatives are safe — `npm i -g` works for npx users too. */\nexport function isNpxInstall(): boolean {\n const bin = process.argv[1] ?? \"\";\n if (/[/\\\\]_npx[/\\\\]/.test(bin)) return true;\n if (/[/\\\\]\\.pnpm[/\\\\]/.test(bin) && /dlx/i.test(bin)) return true;\n const ua = process.env.npm_config_user_agent ?? \"\";\n if (ua.includes(\"npx/\")) return true;\n return false;\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 Reasonix'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`. Reasonix'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. Reasonix 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 Reasonix 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: \"reasonix\", 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 try {\n await Promise.race([this.transport.send(frame), promise.then(() => undefined)]);\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 }\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 { ESCALATION_CONTRACT, TUI_FORMATTING_RULES } from \"../prompt-fragments.js\";\n\nexport const CODE_SYSTEM_PROMPT = `You are Reasonix 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# Cite or shut up — non-negotiable\n\nEvery factual claim you make about THIS codebase must be backed by evidence. Reasonix 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 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) 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\nWhen to delegate (call \\`run_skill\\` with a subagent skill):\n- The task would otherwise need >5 file reads or searches.\n- You only need the conclusion, not the exploration trail.\n- The work is self-contained (you can describe it in one paragraph).\n\nWhen NOT to delegate:\n- Direct, narrow questions answerable in 1-2 tool calls — just do them.\n- Anything where you need to track intermediate results yourself (planning, multi-step edits).\n- Anything that requires user interaction (subagents can't submit plans or ask you for clarification).\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\nReasonix 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 are sandbox-relative. \\`/\\` means the project root, \\`/src/foo.ts\\` means \\`<project>/src/foo.ts\\`. Both relative (\\`src/foo.ts\\`) and POSIX-absolute (\\`/src/foo.ts\\`) forms work.\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 && reasonix 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: **dev servers, watchers, any command 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\n**Never use run_command for a dev server.** It will block for 60s, time out, and the user will see a frozen tool call while the server was actually running fine. Always \\`run_background\\`, then \\`job_output\\` to peek at the logs when you need to verify something.\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?)\\` — block until the job exits or emits new output. Prefer this over repeating identical \\`job_output\\` calls while you're intentionally waiting.\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/** Stack order (stable for cache prefix): base → REASONIX.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}\n\nexport function codeSystemPrompt(rootDir: string, opts: CodeSystemPromptOptions = {}): string {\n const base = opts.hasSemanticSearch\n ? `${CODE_SYSTEM_PROMPT}${SEMANTIC_SEARCH_ROUTING}`\n : CODE_SYSTEM_PROMPT;\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(), \".reasonix\", \"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 `reasonix 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":"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","names":["resolve","resolve","readFileSync","homedir","join","path","path","path","path","path","join","homedir","path","readFileSync","settings","resolve","existsSync","readFileSync","dirname","join","t","process","path","t","chmodSync","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","join","homedir","path","existsSync","readFileSync","mkdirSync","dirname","chmodSync","join","stat","join","existsSync","readFileSync","path","mkdirSync","dirname","writeFileSync","chmodSync","readFileSync","t","t","t","decision","existsSync","readFileSync","readdirSync","statSync","join","readFileSync","walk","readdirSync","join","statSync","fs","existsSync","readFileSync","existsSync","readFileSync","join","path","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","parseFrontmatter","homedir","readFileSync","readdirSync","writeFileSync","unlinkSync","path","fs","pathMod","picomatch","i","j","fs","pathMod","picomatch","displayRel","walk","fs","pathMod","displayRel","walk","sep","displayRel","picomatch","fs","stat","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"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/retry.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/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/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/skills.ts","../src/prompt-fragments.ts","../src/tools/filesystem.ts","../src/tools/fs/edit.ts","../src/tools/fs/glob.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 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\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\nexport interface DeepSeekClientOptions {\n apiKey?: string;\n baseUrl?: string;\n timeoutMs?: number;\n fetch?: typeof fetch;\n /** Retry configuration. Pass `{ maxAttempts: 1 }` to disable retries. */\n retry?: RetryOptions;\n}\n\nexport class DeepSeekClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeoutMs: number;\n readonly retry: RetryOptions;\n private readonly _fetch: typeof fetch;\n\n constructor(opts: DeepSeekClientOptions = {}) {\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 this.apiKey = apiKey;\n let url = opts.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? \"https://api.deepseek.com\";\n // Manual trim — `/\\/+$/` is O(n²) on slash-heavy non-matches per CodeQL js/polynomial-redos.\n while (url.endsWith(\"/\")) url = url.slice(0, -1);\n this.baseUrl = url;\n // 11 min. DeepSeek's load-balancer may keep a connection open for\n // up to 10 minutes while the request waits in queue (non-streaming\n // sends empty lines, streaming sends `:` SSE keep-alive comments —\n // both are invisible to our parsers, so neither surfaces until the\n // real response starts). Timing out at the legacy 2-min default\n // killed queued requests prematurely, burned the queue slot on\n // retry, and could loop through the whole queue repeatedly.\n // Setting 11 min lets the server's own 10-min cap close the\n // connection first (clean EOF → natural retry), and our timer\n // is a safety net for genuinely hung sockets.\n this.timeoutMs = opts.timeoutMs ?? 660_000;\n this._fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry ?? {};\n }\n\n private buildPayload(opts: ChatRequestOptions, stream: boolean) {\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 // V4 thinking-mode toggle: lives under `extra_body.thinking.type` per\n // DeepSeek's docs. Docs also note that in thinking mode `temperature`,\n // `top_p`, `presence_penalty`, `frequency_penalty` are silently\n // ignored — we don't strip them here because the server's explicit\n // \"setting won't report an error\" contract means leaving them in is\n // safe and keeps the request payload diffable against OpenAI tooling.\n if (opts.thinking) {\n payload.extra_body = { thinking: { type: opts.thinking } };\n }\n if (opts.reasoningEffort) {\n payload.reasoning_effort = opts.reasoningEffort;\n }\n return payload;\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 try {\n const resp = await this._fetch(`${this.baseUrl}/user/balance`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\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 /** Returns null on failure — callers fall back to a hardcoded model hint. */\n async listModels(opts: { signal?: AbortSignal } = {}): Promise<ModelList | null> {\n try {\n const resp = await this._fetch(`${this.baseUrl}/models`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\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 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 Authorization: `Bearer ${this.apiKey}`,\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(`DeepSeek ${resp.status}: ${await resp.text()}`);\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 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 // Only the initial fetch is retried. Once the server has started sending\n // the stream body we do NOT retry — a mid-stream retry would re-bill and\n // desync the session context.\n resp = await fetchWithRetry(\n this._fetch,\n `${this.baseUrl}/chat/completions`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\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(`DeepSeek ${resp.status}: ${await resp.text().catch(() => \"\")}`);\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 (typeof delta.reasoning_content === \"string\" && delta.reasoning_content.length > 0) {\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\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","/** 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 = { type: \"approve\" } | { type: \"refine\" } | { type: \"cancel\" };\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 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 };\n run_background: { command: string };\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 Reasonix 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 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\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>/.reasonix/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 = \".reasonix\";\n\n/** Where the global settings.json lives. Equivalent to `~/.reasonix/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 ReasonixConfig {\n apiKey?: string;\n baseUrl?: string;\n lang?: LanguageCode;\n preset?: PresetName;\n editMode?: EditMode;\n editModeHintShown?: boolean;\n mouseClipboardHintShown?: boolean;\n reasoningEffort?: ReasoningEffort;\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 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 projects?: {\n [absoluteRootDir: string]: {\n shellAllowed?: string[];\n };\n };\n index?: IndexUserConfig;\n semantic?: SemanticEmbeddingUserConfig;\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(), \".reasonix\", \"config.json\");\n}\n\nexport function readConfig(path: string = defaultConfigPath()): ReasonixConfig {\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") return parsed as ReasonixConfig;\n } catch {\n /* missing or malformed → empty config */\n }\n return {};\n}\n\nexport function writeConfig(cfg: ReasonixConfig, 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\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. */\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\nexport function searchEnabled(path: string = defaultConfigPath()): boolean {\n const env = process.env.REASONIX_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: ReasonixConfig, 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\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 return v === \"auto\" ? \"auto\" : \"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 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.REASONIX_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.REASONIX_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\nexport function isPlausibleKey(key: string): boolean {\n const trimmed = key.trim();\n return /^sk-[A-Za-z0-9_-]{16,}$/.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 \".reasonix\",\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 },\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 Reasonix version.\",\n update: \"Check for a newer Reasonix and install it.\",\n index: \"Build (or incrementally refresh) a local semantic search index.\",\n },\n ui: {\n welcome: \"Run `reasonix` 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 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 · /forget to start over · /sessions to list',\n newSession:\n '▸ session \"{name}\" (new) — auto-saved as you chat · /forget to delete · /sessions to list',\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: \"wheel\", text: \"scrolls the chat history above\" },\n {\n key: \"right-click\",\n text: \"captured by the app — use Ctrl+V (Win/Linux) or Cmd+V (macOS) to paste\",\n },\n {\n key: \"Shift + drag\",\n text: \"selects text natively (Option on iTerm2, Shift on Win Term / Alacritty / WezTerm)\",\n },\n ],\n },\n ],\n footer: \"Run /keys for the full keyboard + mouse reference\",\n },\n keysReference: {\n topic: \"Reasonix 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: \"Ctrl+P / Ctrl+N\", text: \"recall previous / next prompt from history\" },\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 {\n key: \"↑ / ↓\",\n text: \"scroll chat history (PromptInput cursor when buffer non-empty)\",\n },\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 the chat history\" },\n { key: \"right-click\", text: \"captured by the app — use Ctrl+V / Cmd+V to paste\" },\n { key: \"left-click\", text: \"captured (no action yet — reserved for future use)\" },\n ],\n },\n {\n title: \"copy / paste\",\n rows: [\n { key: \"select text\", text: \"hold Shift while dragging (Option on iTerm2)\" },\n {\n key: \"copy\",\n text: \"Ctrl+Shift+C (Win/Linux) · Cmd+C (macOS) — terminal-native after selection\",\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 \"Mouse tracking is on so the wheel scrolls chat instead of moving the cursor — that's why right-click no longer does the terminal's native paste.\",\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 ~/.reasonix/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 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 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 (REASONIX.md + ~/.reasonix/memory)\",\n argsHint: \"[list|show <name>|forget <name>|clear <scope> confirm]\",\n },\n skill: {\n description: \"list / run user skills (<project>/.reasonix/skills + ~/.reasonix/skills)\",\n argsHint: \"[list|show <name>|<name> [args]]\",\n },\n hooks: {\n description: \"list active hooks (settings.json under .reasonix/) · reload re-reads from disk\",\n argsHint: \"[reload]\",\n },\n permissions: {\n description:\n \"show / edit shell allowlist (builtin read-only · per-project: ~/.reasonix/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 \"shrink oversized tool results AND tool-call args (edit_file search/replace) in the log; cap in tokens, default 4000\",\n argsHint: \"[tokens]\",\n },\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 `reasonix 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 REASONIX.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 Reasonix.\",\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: \"Doesn't look like a DeepSeek key. They start with 'sk-' and are 30+ chars.\",\n apiKeyPreview: \"preview: {redacted}\",\n presetTitle: \"Pick a preset\",\n mcpTitle: \"Which MCP servers should Reasonix 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 },\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 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 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. Reasonix 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 /forget (delete the session) or /clear (drop the displayed history) to start fresh.\",\n contextOverflowTooMany: \"too many tokens\",\n auth401:\n \"Authentication failed (DeepSeek 401): {inner}. Your API key is rejected. Fix with `reasonix 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 Reasonix. 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 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>/REASONIX.md (committable).\",\n helpMemoryPinEx:\n \" Example: #findByEmail must be case-insensitive\",\n helpMemoryGlobal:\n \" #g <note> append <note> to ~/.reasonix/REASONIX.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: \" reasonix chat --session <name> use a different named session\",\n helpSessionNone: \" reasonix 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 },\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>/.reasonix/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: reasonix {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 `reasonix update` in another terminal to force it synchronously.\",\n updateLatest: \"latest: reasonix {version}\",\n updateUpToDate: \"you're on the latest. nothing to do.\",\n updateNpxHint: \"you're running via npx — the next `npx reasonix ...` 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 \" reasonix update (interactive, dry-run supported via --dry-run)\",\n updateUpgradeCmd2: \" npm install -g reasonix@latest (direct)\",\n updateInSessionDisabled: \"in-session install is deliberately disabled — the npm 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 `reasonix code` — chat mode doesn't apply edits.\",\n historyCodeOnly: \"/history is only available inside `reasonix code`.\",\n showCodeOnly: \"/show is only available inside `reasonix code`.\",\n applyCodeOnly: \"/apply is only available inside `reasonix code` (nothing to apply here).\",\n discardCodeOnly: \"/discard is only available inside `reasonix code`.\",\n planCodeOnly:\n \"/plan is only available inside `reasonix 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 `reasonix 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 `reasonix 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 `reasonix code`.\",\n checkpointCodeOnly:\n \"/checkpoint is only available inside `reasonix 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 `reasonix 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 `reasonix 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 — Reasonix 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 `reasonix code` — they edit the project-scoped allowlist (`~/.reasonix/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 `reasonix 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 `reasonix 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 },\n jobs: {\n codeOnly: \"/jobs is only available inside `reasonix code`.\",\n killCodeOnly: \"/kill is only available inside `reasonix code`.\",\n logsCodeOnly: \"/logs is only available inside `reasonix 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 (REASONIX_MEMORY=off in env). Unset the var to re-enable — no REASONIX.md or ~/.reasonix/memory content will be pinned in the meantime.\",\n noRoot:\n \"no working directory on this session — `/memory` needs a root to resolve REASONIX.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 ~/.reasonix/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. ~/.reasonix/memory/global/ — your cross-project private memory.\",\n layerProjectHash: \" 3. ~/.reasonix/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 `reasonix setup` to pick some, or launch with --mcp \"<spec>\". `reasonix 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: `reasonix mcp list` · deeper diagnosis: `reasonix mcp inspect <spec>`.\",\n fallbackServers: \"MCP servers ({count}):\",\n fallbackTools: \"Tools in registry ({count}):\",\n fallbackChange: \"To change this set, exit and run `reasonix 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 },\n init: {\n codeOnly:\n \"/init only works in code mode (it needs filesystem tools).\\nRun `reasonix code [path]` to start a session rooted at the\\nproject you want to initialize, then run /init.\",\n exists: \"▸ REASONIX.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 REASONIX.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. Reasonix reads skills from:\",\n listProjectScope:\n \" · <project>/.reasonix/skills/<name>/SKILL.md (or <name>.md) — project scope\",\n listGlobalScope: \" · ~/.reasonix/skills/<name>/SKILL.md (or <name>.md) — global scope\",\n listProjectOnly: \" (project scope is only active in `reasonix 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};\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 },\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: \"打印 Reasonix 版本。\",\n update: \"检查较新版本的 Reasonix 并安装。\",\n index: \"构建(或增量刷新)本地语义搜索索引。\",\n },\n ui: {\n welcome: \"随时运行 `reasonix` 开始聊天 — 您的设置将被记住。\",\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 dashboardAutoStartFailed:\n \"▲ 仪表板自动启动失败 ({reason}) — 尝试 /dashboard,或传递 --no-dashboard 以静默\",\n systemAppendHint: \"追加指令到代码系统提示词。不替换默认提示词 — 在其后添加。\",\n systemAppendFileHint:\n \"追加文件内容到代码系统提示词。不替换默认提示词。UTF-8,相对于 cwd 或绝对路径。\",\n resumedSession:\n '▸ 已恢复会话 \"{name}\",包含 {count} 条历史消息 · /forget 重新开始 · /sessions 列出',\n newSession: '▸ 会话 \"{name}\" (新) — 随聊随存 · /forget 删除 · /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: \"滚动上方的聊天记录\" },\n {\n key: \"右键\",\n text: \"已被应用接管 — 用 Ctrl+V(Win/Linux)或 Cmd+V(macOS)粘贴\",\n },\n {\n key: \"Shift + 拖动\",\n text: \"原生选中文本(iTerm2 用 Option,Win Term / Alacritty / WezTerm 用 Shift)\",\n },\n ],\n },\n ],\n footer: \"运行 /keys 查看完整键盘 + 鼠标参考\",\n },\n keysReference: {\n topic: \"Reasonix 键盘 + 鼠标参考\",\n sections: [\n {\n title: \"键盘\",\n rows: [\n { key: \"Enter\", text: \"提交输入\" },\n { key: \"Shift+Enter\", text: \"在输入框中插入换行\" },\n { key: \"Ctrl+P / Ctrl+N\", text: \"调出上一条 / 下一条历史提示\" },\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: \"↑ / ↓\", text: \"滚动聊天记录(缓冲区非空时为光标移动)\" },\n { key: \"PgUp / PgDn\", text: \"整页滚动聊天记录\" },\n { key: \"End\", text: \"跳到聊天的最新一行\" },\n ],\n },\n {\n title: \"鼠标\",\n rows: [\n { key: \"滚轮\", text: \"滚动聊天记录\" },\n { key: \"右键\", text: \"被应用接管 — 用 Ctrl+V / Cmd+V 粘贴\" },\n { key: \"左键\", text: \"被接管(暂无动作 — 预留给后续功能)\" },\n ],\n },\n {\n title: \"复制 / 粘贴\",\n rows: [\n { key: \"选中文字\", text: \"拖动时按住 Shift(iTerm2 用 Option)\" },\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 \"鼠标追踪已开启,滚轮用来滚动聊天而不是移动光标 — 这也是右键不再触发终端原生粘贴的原因。\",\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: \"本次运行忽略 ~/.reasonix/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 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 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: \"显示 / 管理固定记忆(REASONIX.md + ~/.reasonix/memory)\",\n argsHint: \"[list|show <name>|forget <name>|clear <scope> confirm]\",\n },\n skill: {\n description: \"列出 / 运行用户技能(<project>/.reasonix/skills + ~/.reasonix/skills)\",\n argsHint: \"[list|show <name>|<name> [args]]\",\n },\n hooks: {\n description: \"列出活跃的 hooks(.reasonix/ 下的 settings.json)· reload 从磁盘重新读取\",\n argsHint: \"[reload]\",\n },\n permissions: {\n description: \"显示 / 编辑 shell 允许列表(内置只读 · 项目级:~/.reasonix/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 plans: { description: \"列出此会话的活跃 + 归档计划(最新在前)\" },\n replay: {\n description: \"加载归档计划为只读的时间旅行快照(默认:最新)\",\n argsHint: \"[N]\",\n },\n sessions: { description: \"列出已保存的会话(当前标记为 ▸)\" },\n setup: { description: \"提醒您退出并运行 `reasonix 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 \"扫描项目并合成基线 REASONIX.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: \"欢迎使用 Reasonix。\",\n apiKeyPrompt: \"粘贴你的 DeepSeek API key 开始使用。\",\n apiKeyGetOne: \"在此获取:https://platform.deepseek.com/api_keys\",\n apiKeySavedLocally: \"保存在本地:{path}\",\n apiKeyInputLabel: \"key › \",\n apiKeyInvalid: \"这看起来不像 DeepSeek 的 key。它们以 'sk-' 开头,30 字符以上。\",\n apiKeyPreview: \"预览:{redacted}\",\n presetTitle: \"选择预设\",\n mcpTitle: \"Reasonix 要为你接入哪些 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 },\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 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 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)。通常是单个工具结果太大。Reasonix 默认将新工具结果限制在 8k tokens,并在会话加载时自动修复超大历史 — 重启常能清掉。如果仍然溢出,运行 /forget(删除会话)或 /clear(丢弃显示中的历史)从头开始。\",\n contextOverflowTooMany: \"tokens 数量过多\",\n auth401:\n \"认证失败(DeepSeek 401):{inner}。你的 API key 被拒绝。运行 `reasonix 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 服务端问题,不是 Reasonix 故障。已按指数退避重试 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 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>/REASONIX.md(可提交)。\",\n helpMemoryPinEx: \" 示例:#findByEmail 必须区分大小写\",\n helpMemoryGlobal:\n \" #g <note> 将 <note> 追加到 ~/.reasonix/REASONIX.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: \" reasonix chat --session <name> 使用不同的命名会话\",\n helpSessionNone: \" reasonix 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 },\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>/.reasonix/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: \"当前:reasonix {version}\",\n updateLatestPending: \"最新:(尚未解析 — 后台检查进行中或离线)\",\n updateRetryHint: \"已触发新的注册表获取 — 几秒后重试 `/update`,\",\n updateRetryHint2: \"或在另一个终端运行 `reasonix update` 强制同步执行。\",\n updateLatest: \"最新:reasonix {version}\",\n updateUpToDate: \"您已是最新版本。无需操作。\",\n updateNpxHint: \"您正在通过 npx 运行 — 下次 `npx reasonix ...` 启动时将自动获取。\",\n updateNpxForce: \"要强制刷新:`npm cache clean --force`。\",\n updateUpgradeHint: \"要升级,请退出此会话并运行:\",\n updateUpgradeCmd1: \" reasonix update (交互式,支持 --dry-run 预览)\",\n updateUpgradeCmd2: \" npm install -g reasonix@latest (直接安装)\",\n updateInSessionDisabled: \"会话内安装被刻意禁用 — npm spawn 会\",\n updateInSessionDisabled2: \"破坏此 TUI 的渲染,且 Windows 可能锁定运行中的二进制文件。\",\n statsNoData: \"尚无使用数据。\",\n statsEveryTurn: \"您在此运行的每一轮都会追加一条记录 — 此会话的轮次\",\n statsWillAppear: \"将在您发送消息后显示在仪表板中。\",\n },\n edits: {\n undoCodeOnly: \"/undo 仅在 `reasonix code` 中可用 — 聊天模式不应用编辑。\",\n historyCodeOnly: \"/history 仅在 `reasonix code` 中可用。\",\n showCodeOnly: \"/show 仅在 `reasonix code` 中可用。\",\n applyCodeOnly: \"/apply 仅在 `reasonix code` 中可用(此处无内容可应用)。\",\n discardCodeOnly: \"/discard 仅在 `reasonix code` 中可用。\",\n planCodeOnly: \"/plan 仅在 `reasonix code` 中可用 — 聊天模式不限制工具写入。\",\n planOn:\n \"▸ 计划模式开启 — 写入工具被限制;模型必须先调用 `submit_plan` 才能执行任何操作。(模型也可以在计划模式关闭时自主调用 submit_plan 处理大型任务 — 此开关是更强的显式约束。)输入 /plan off 退出。\",\n planOff: \"▸ 计划模式关闭 — 写入工具再次可用。模型仍可为大型任务自主提出计划。\",\n modeCodeOnly: \"/mode 仅在 `reasonix 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 仅在 `reasonix code` 中可用(需要有根的 git 仓库)。\",\n commitUsage: '用法:/commit \"提交消息\" — 在 {root} 中运行 `git add -A && git commit -m \"…\"`',\n walkCodeOnly: \"/walk 仅在 `reasonix code` 中可用。\",\n checkpointCodeOnly: \"/checkpoint 仅在 `reasonix 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 仅在 `reasonix 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 仅在 `reasonix 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 \"未设置会话预算 — Reasonix 将持续运行直到您停止。使用以下方式设置:/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 仅在 `reasonix code` 中可用 — 它们编辑项目范围的允许列表(`~/.reasonix/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` 是按会话的。在项目中运行 `reasonix code` 以获取会话。\",\n activePlan: \"▸ 活跃计划{label} — {done}/{total} 步骤已完成 · 最后触及 {when}\",\n activeNone: \"▸ 活跃计划:(无)\",\n noArchives: \"此会话尚无归档计划 — 当每个步骤完成时自动归档\",\n archivedHeader: \"已归档({count}):\",\n replayNoSession:\n \"未附加会话 — `/replay` 是按会话的。在项目中运行 `reasonix code` 以获取会话。\",\n replayNoArchives:\n \"此会话尚无归档计划 — `/replay` 在计划完成后启用(每个步骤完成时自动归档)。\",\n replayInvalidIndex:\n \"无效索引 — `/replay` 接受 1..{max}(最新 = 1)。使用 `/plans` 查看列表。\",\n archivedRow: \" ✓ {when} {total}步 · {completion} {label}\",\n completionComplete: \"已完成\",\n stopAborted: \"▸ 计划已停止 — 模型已中止;输入后续内容继续,或开始新任务。\",\n },\n jobs: {\n codeOnly: \"/jobs 仅在 `reasonix code` 中可用。\",\n killCodeOnly: \"/kill 仅在 `reasonix code` 中可用。\",\n logsCodeOnly: \"/logs 仅在 `reasonix 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 \"记忆已禁用(环境变量 REASONIX_MEMORY=off)。取消设置该变量以重新启用 — 此期间不会固定任何 REASONIX.md 或 ~/.reasonix/memory 内容。\",\n noRoot:\n \"此会话无工作目录 — `/memory` 需要一个根目录来解析 REASONIX.md。(在测试环境中运行?)\",\n listEmpty:\n \"尚无用户记忆。模型可以调用 `remember` 保存一个,或您可以在 ~/.reasonix/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. ~/.reasonix/memory/global/ — 您的跨项目私有记忆。\",\n layerProjectHash: \" 3. ~/.reasonix/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 服务器。运行 `reasonix setup` 选择一些,或使用 --mcp \"<spec>\" 启动。`reasonix mcp list` 显示目录。',\n toolsLabel: \" 工具 {count}\",\n resourcesHint: \"`/resource` 浏览+读取\",\n promptsHint: \"`/prompt` 浏览+获取\",\n awarenessOnly: \"聊天模式目前消耗工具;资源+提示在此展示供了解。\",\n catalogHint: \"完整目录:`reasonix mcp list` · 深度诊断:`reasonix mcp inspect <spec>`。\",\n fallbackServers: \"MCP 服务器({count}):\",\n fallbackTools: \"注册表中的工具({count}):\",\n fallbackChange: \"要更改此设置,请退出并运行 `reasonix 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 },\n init: {\n codeOnly:\n \"/init 仅在代码模式下工作(需要文件系统工具)。\\n运行 `reasonix code [path]` 启动一个以您要初始化的项目为根的会话,\\n然后运行 /init。\",\n exists: \"▸ REASONIX.md 已存在于 {path}\",\n existsForce: \" /init force 从头重新生成(覆盖)\",\n existsEdit: \" 或手动编辑 — 它只是 markdown。当前文件已\",\n existsPinned: \" 固定到每次启动的系统提示词中。\",\n info: \"▸ /init — 模型将扫描项目并合成 REASONIX.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: \"未找到技能。Reasonix 从以下位置读取技能:\",\n listProjectScope:\n \" · <project>/.reasonix/skills/<name>/SKILL.md (或 <name>.md) — 项目范围\",\n listGlobalScope: \" · ~/.reasonix/skills/<name>/SKILL.md (或 <name>.md) — 全局范围\",\n listProjectOnly: \" (项目范围仅在 `reasonix 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};\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.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_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(\"reasonix/package.json\")), \"data\", \"deepseek-tokenizer.json.gz\"),\n );\n } catch {\n /* Not installed as `reasonix/` — 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\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\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 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 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 JSON.stringify({\n error: `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 // 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 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 return 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 return JSON.stringify(e.toToolResult());\n } catch {\n /* fall through to the default shape */\n }\n }\n return JSON.stringify({\n error: `${e.name}: ${e.message}`,\n });\n }\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 {\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","/** 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}\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\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}\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 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\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 };\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 `~/.reasonix/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(), \".reasonix\", \"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/** 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 — concurrent crash here = `/forget`. */\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\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\";\n\n/** USD per 1M tokens; CNY sheet converted at fixed 7.2 — revisit if FX moves >±5%. */\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 // Compat aliases — priced as v4-flash per the deprecation notice.\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/** Prompt-side window only; completion caps live server-side and don't affect this gauge. */\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 the table — safe lower bound. */\nexport const DEFAULT_CONTEXT_TOKENS = 131_072;\n\nexport function costUsd(model: string, usage: Usage): number {\n const p = DEEPSEEK_PRICING[model];\n if (!p) 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 = DEEPSEEK_PRICING[model];\n if (!p) 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 = DEEPSEEK_PRICING[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 = DEEPSEEK_PRICING[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","import type { DeepSeekClient } from \"../client.js\";\nimport { t } from \"../i18n/index.js\";\n\nexport interface DeepSeekProbeResult {\n reachable: boolean;\n}\n\nexport function formatLoopError(err: Error, probe?: DeepSeekProbeResult): 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 const m = /^DeepSeek (\\d{3}):\\s*([\\s\\S]*)$/.exec(msg);\n if (!m) return msg;\n const status = m[1] ?? \"\";\n const body = m[2] ?? \"\";\n const inner = extractDeepSeekErrorMessage(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 formatDeepSeek5xx(status, probe);\n return msg;\n}\n\nexport function is5xxError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const m = /^DeepSeek (5\\d{2}):/.exec(err.message ?? \"\");\n return m !== null;\n}\n\nexport async function probeDeepSeekReachable(\n client: DeepSeekClient,\n timeoutMs = 1500,\n): Promise<DeepSeekProbeResult> {\n const balance = await client.getBalance({ signal: AbortSignal.timeout(timeoutMs) });\n return { reachable: balance !== null };\n}\n\nfunction is5xxStatus(status: string): boolean {\n return status === \"500\" || status === \"502\" || status === \"503\" || status === \"504\";\n}\n\nfunction formatDeepSeek5xx(status: string, probe?: DeepSeekProbeResult): 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 extractDeepSeekErrorMessage(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","/** True when the model emits reasoning_content and requires it round-tripped on follow-ups. */\nexport function isThinkingModeModel(model: string): boolean {\n if (model.includes(\"reasoner\")) return true;\n if (model === \"deepseek-v4-flash\" || model === \"deepseek-v4-pro\") return true;\n return false;\n}\n\n/** Pins extra_body.thinking.type; `undefined` lets third-party endpoints skip the field. */\nexport function thinkingModeForModel(model: string): \"enabled\" | \"disabled\" | undefined {\n if (model === \"deepseek-chat\") return \"disabled\";\n if (model.includes(\"reasoner\")) return \"enabled\";\n if (model === \"deepseek-v4-flash\" || model === \"deepseek-v4-pro\") return \"enabled\";\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 } from \"../types.js\";\nimport { shrinkOversizedToolResults, shrinkOversizedToolResultsByTokens } from \"./shrink.js\";\nimport { isThinkingModeModel } from \"./thinking.js\";\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 const needed = new Set<string>();\n for (const call of msg.tool_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);\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","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\n reset(): void {\n this.count = 0;\n this.types = {};\n }\n\n /** True ONLY on the call where the count crosses FAILURE_ESCALATION_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 < FAILURE_ESCALATION_THRESHOLD && this.count >= FAILURE_ESCALATION_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 readonly 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 only via `addTool`; bypassing it 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 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 DeepSeekClient, 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 { t } from \"./i18n/index.js\";\nimport { formatLoopError, is5xxError, probeDeepSeekReachable } 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 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 { 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 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\nconst ESCALATION_MODEL = \"deepseek-v4-pro\";\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: DeepSeekClient;\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 session?: string;\n /** PreToolUse + PostToolUse only — UserPromptSubmit / Stop live at the App boundary. */\n hooks?: ResolvedHook[];\n /** `cwd` reported to hooks; `reasonix 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}\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: DeepSeekClient;\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 _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\n private _proArmedForNextTurn = false;\n private _escalateThisTurn = false;\n private readonly _turnFailures = new TurnFailureTracker();\n private _turnSelfCorrected = false;\n private _foldedThisTurn = false;\n private context!: ContextManager;\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 // 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\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 isMutating = (call: ToolCall): boolean => {\n const name = call.function?.name;\n if (!name) return false;\n const def = registry.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 {\n /* ignore — fall through */\n }\n }\n return def.readOnly !== true;\n };\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,\n isStormExempt,\n stormThreshold: parsePositiveIntEnv(process.env.REASONIX_STORM_THRESHOLD),\n stormWindow: parsePositiveIntEnv(process.env.REASONIX_STORM_WINDOW),\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 // 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 messages but keeps session + immutable prefix (cache-first invariant). */\n clearLog(): { dropped: number } {\n const dropped = this.log.length;\n this.log.compactInPlace([]);\n if (this.sessionName) {\n try {\n rewriteSession(this.sessionName, []);\n } catch {\n /* disk issue shouldn't block the in-memory clear */\n }\n }\n this.scratch.reset();\n return { dropped };\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 private modelForCurrentCall(): string {\n return this._escalateThisTurn ? ESCALATION_MODEL : 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 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\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 = (blocking?.stderr || blocking?.stdout || \"blocked by PreToolUse hook\").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 }\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._turnSelfCorrected = false;\n this._escalateThisTurn = false;\n this._foldedThisTurn = false;\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 !== ESCALATION_MODEL;\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 probeDeepSeekReachable(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() !== ESCALATION_MODEL &&\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: ESCALATION_MODEL, 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: ESCALATION_MODEL,\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.REASONIX_TOOL_DISPATCH ?? \"auto\").toLowerCase() === \"serial\";\n const parallelMaxParsed = Number.parseInt(process.env.REASONIX_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.\n for (const call of chunk) {\n yield {\n turn: this._turn,\n role: \"tool_start\",\n content: \"\",\n toolName: call.function?.name ?? \"\",\n toolArgs: call.function?.arguments ?? \"{}\",\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: ESCALATION_MODEL,\n breakdown: this._turnFailures.formatBreakdown(),\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 };\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","/** 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. */\nexport const AT_PICKER_PREFIX = /(?:^|\\s)@([a-zA-Z0-9_./\\\\-]*)$/;\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)@([a-zA-Z0-9_./\\\\-]+)/g;\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","/** REASONIX.md pinned into ImmutablePrefix.system; edits invalidate the prefix-cache fingerprint. */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport const PROJECT_MEMORY_FILE = \"REASONIX.md\";\nexport const PROJECT_MEMORY_MAX_CHARS = 8000;\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 = join(rootDir, PROJECT_MEMORY_FILE);\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 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.REASONIX_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 return `${basePrompt}\n\n# Project memory (REASONIX.md)\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 REASONIX.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 { 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 type MemoryType = \"user\" | \"feedback\" | \"project\" | \"reference\";\nexport type MemoryScope = \"global\" | \"project\";\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}\n\nexport interface MemoryStoreOptions {\n /** Override `~/.reasonix` — 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}\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 parseFrontmatter(raw: string): { data: Record<string, string>; body: string } {\n const lines = raw.split(/\\r?\\n/);\n if (lines[0] !== \"---\") return { data: {}, body: raw };\n const end = lines.indexOf(\"---\", 1);\n if (end < 0) return { data: {}, body: raw };\n const data: Record<string, string> = {};\n for (let i = 1; i < end; i++) {\n const line = lines[i];\n if (!line) continue;\n const m = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\\s*(.*)$/);\n if (m?.[1]) data[m[1]] = (m[2] ?? \"\").trim();\n }\n return {\n data,\n body: lines\n .slice(end + 1)\n .join(\"\\n\")\n .replace(/^\\n+/, \"\"),\n };\n}\n\nfunction formatFrontmatter(e: WriteInput & { createdAt: string }): string {\n return [\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 \"\",\n ].join(\"\\n\");\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(), \".reasonix\");\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 return {\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 }\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 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 readGlobalReasonixMemory(\n homeDir: string = join(homedir(), \".reasonix\"),\n): { path: string; content: string; originalChars: number; truncated: boolean } | null {\n const path = join(homeDir, \"REASONIX.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 applyGlobalReasonixMemory(basePrompt: string, homeDir?: string): string {\n if (!memoryEnabled()) return basePrompt;\n const dir = homeDir ?? join(homedir(), \".reasonix\");\n const mem = readGlobalReasonixMemory(dir);\n if (!mem) return basePrompt;\n return [\n basePrompt,\n \"\",\n \"# Global memory (~/.reasonix/REASONIX.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/** 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 } = {},\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 if (!global && !project) return basePrompt;\n const parts: string[] = [basePrompt];\n if (global) {\n parts.push(\n \"\",\n \"# User memory — global (~/.reasonix/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 = applyGlobalReasonixMemory(withProject);\n const withMemory = applyUserMemory(withGlobal, { projectRoot: rootDir });\n return applySkillsIndex(withMemory, { projectRoot: rootDir });\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 { 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}\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\nfunction parseFrontmatter(raw: string): { data: Record<string, string>; body: string } {\n const lines = raw.split(/\\r?\\n/);\n if (lines[0] !== \"---\") return { data: {}, body: raw };\n const end = lines.indexOf(\"---\", 1);\n if (end < 0) return { data: {}, body: raw };\n const data: Record<string, string> = {};\n for (let i = 1; i < end; i++) {\n const line = lines[i];\n if (!line) continue;\n const m = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\\s*(.*)$/);\n if (m?.[1]) data[m[1]] = (m[2] ?? \"\").trim();\n }\n return {\n data,\n body: lines\n .slice(end + 1)\n .join(\"\\n\")\n .replace(/^\\n+/, \"\"),\n };\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\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, \".reasonix\", SKILLS_DIRNAME),\n scope: \"project\",\n });\n }\n out.push({ dir: join(this.homeDir, \".reasonix\", 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 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 `reasonix code`\" };\n }\n const root =\n scope === \"project\"\n ? join(this.projectRoot ?? \"\", \".reasonix\", SKILLS_DIRNAME)\n : join(this.homeDir, \".reasonix\", 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, skillStubBody(name), { 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 };\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`;\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\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().filter((s) => s.description);\n if (skills.length === 0) return basePrompt;\n const lines = skills.map(skillIndexLine);\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\nexport const ESCALATION_CONTRACT = `Cost-aware escalation (when you're running on deepseek-v4-flash):\n\nIf a task CLEARLY exceeds what flash 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 flash. Request escalation ONLY when you would otherwise produce a guess or a visibly-mediocre answer. If in doubt, attempt the task on flash first; the system also escalates automatically if you hit 3+ repair / SEARCH-mismatch errors in a single turn (the user sees a typed breakdown).`;\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 { DEFAULT_INDEX_EXCLUDES } from \"../index/config.js\";\nimport type { ToolRegistry } from \"../tools.js\";\nimport { applyEdit, applyMultiEdit } from \"./fs/edit.js\";\nimport { globFiles } from \"./fs/glob.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;\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 /** Resolve path, enforce it's under rootDir, return absolute. */\n const safePath = (raw: unknown): string => {\n if (typeof raw !== \"string\" || raw.length === 0) {\n throw new Error(\"path must be a non-empty string\");\n }\n // Sandbox-root semantics: a leading POSIX-style `/` (or `\\` on\n // Windows) means \"from the project root\", not \"from the filesystem\n // root\". Models routinely write `path: \"/\"` or `path: \"/src/foo.ts\"`\n // intending the sandbox root — without this normalization,\n // path.resolve interprets `/` as the actual drive root (`F:\\` on\n // Windows, `/` on POSIX) and the escape check rightly rejects it,\n // confusing the model. Strip leading separators so the rest of the\n // resolution treats the input as relative to rootDir. Drive-letter\n // absolutes (`C:\\foo`) and Unix absolutes outside rootDir still\n // get caught by the relative-escape check below.\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 const normRoot = pathMod.resolve(rootDir);\n // Use relative() to catch any `..` segments that escape.\n const rel = pathMod.relative(normRoot, resolved);\n if (rel.startsWith(\"..\") || pathMod.isAbsolute(rel)) {\n throw new Error(\n `path escapes sandbox root (${normRoot}): ${raw} — workspace is pinned at launch; quit and relaunch with \\`reasonix code --dir <path>\\` to work in a different folder`,\n );\n }\n return resolved;\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 rather than dumping everything. If you need the middle, re-call with a range. Prefer search_content to locate a symbol first, then read_file with a range around the hit — 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 (args: { path: string; head?: number; tail?: number; range?: string }) => {\n const abs = safePath(args.path);\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 return [\n `[auto-preview: head ${AUTO_PREVIEW_HEAD_LINES} + tail ${AUTO_PREVIEW_TAIL_LINES} of ${totalLines} lines]`,\n head,\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 ].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 }) => {\n const abs = safePath(args.path ?? \".\");\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 (args: { path?: string; maxDepth?: number; include_deps?: boolean }) => {\n const startAbs = safePath(args.path ?? \".\");\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 safePath(args.path ?? \".\"),\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. 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 },\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 },\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 safePath(args.path ?? \".\"),\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({ rootDir, skipDirNames: SKIP_DIR_NAMES }, safePath(args.path ?? \".\"), {\n ...args,\n 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 }) => {\n const abs = safePath(args.path);\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 }) => {\n const abs = safePath(args.path);\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 }) =>\n applyEdit(rootDir, safePath(args.path), 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 (args: { edits: Array<{ path: string; search: string; replace: string }> }) => {\n const resolved = (args.edits ?? []).map((e) => ({\n abs: safePath(e?.path),\n search: e?.search,\n replace: e?.replace,\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 }) => {\n const abs = safePath(args.path);\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 }) => {\n const src = safePath(args.source);\n const dst = safePath(args.destination);\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 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","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\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 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 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\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 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 if (ctxLines === 0) {\n for (const li of hits) {\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 continue;\n }\n const hitSet = new Set(hits);\n let prevWindowEnd = -2;\n for (const li of hits) {\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 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 {\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 `~/.reasonix` (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 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 enum: [\"user\", \"feedback\", \"project\", \"reference\"],\n description:\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 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 `reasonix 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 },\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 }) => {\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 `reasonix 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 });\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. Call this after finishing each step, then immediately continue with the NEXT step — do not stop or wait for the user. The TUI updates the plan card's progress in place. 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.\";\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 if (verdict.type === \"approve\") return \"plan approved\";\n if (verdict.type === \"refine\") throw new Error(\"user requested refinement\");\n throw new Error(\"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 ESCALATION_CONTRACT,\n NEGATIVE_CLAIM_RULE,\n TUI_FORMATTING_RULES,\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\nconst DEFAULT_SUBAGENT_SYSTEM = `You are a Reasonix 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${ESCALATION_CONTRACT}\n\n${TUI_FORMATTING_RULES}`;\n\nconst DEFAULT_MAX_RESULT_CHARS = 8000;\nconst DEFAULT_MAX_ITERS = 16;\nconst MIN_MAX_ITERS = 1;\nconst MAX_MAX_ITERS = 32;\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/** 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 const childPrefix = new ImmutablePrefix({\n system: opts.system,\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 — `reasonix 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 ?? DEFAULT_SUBAGENT_SYSTEM;\n // Bake project memory into the default once — re-reading on every\n // spawn would (a) make the child prefix unstable when REASONIX.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.\n const defaultSystem = 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\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. Use for: deep codebase exploration that would flood the main context, multi-step research where you only need the conclusion, or any focused subtask whose intermediate reasoning the user does not need to see. The subagent inherits all your tools (filesystem, shell, web, MCP, etc.) but runs in its own isolated message log — its tool calls and reasoning never enter your context. Only the final assistant message comes back as this tool's result. 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 system =\n typeof args.system === \"string\" && args.system.trim().length > 0\n ? args.system.trim()\n : (typeSpec?.system ?? defaultSystem);\n const model =\n typeof args.model === \"string\" && args.model.startsWith(\"deepseek-\")\n ? args.model\n : defaultModel;\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 return formatSubagentResult(result);\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 and return its combined stdout+stderr.\\n\\nConstraints (read these before the first call):\\n• Chain operators `|`, `||`, `&&`, `;` ARE supported — parsed natively, no shell invoked, so semantics are identical on Windows / macOS / Linux. Each chain segment is allowlist-checked individually: `git status | grep main` runs if both halves are allowed.\\n• File redirects ARE supported: `>` truncate, `>>` append, `<` stdin from file, `2>` / `2>>` stderr to file, `2>&1` merge stderr→stdout, `&>` both to file. Targets resolve relative to the project root. At most one redirect per fd per segment.\\n• Background `&`, heredoc `<<`, command substitution `$(…)`, subshells `(…)`, and process substitution `<(…)` are NOT supported. Wrap a literal `&` arg in quotes; for input use a `<` file or the binary's own --input flag.\\n• Env-var expansion `$VAR` is NOT performed — `$VAR` is passed as a literal string. Use the binary's own --env flag or substitute the value yourself.\\n• `cd` DOES NOT PERSIST between calls — each call spawns a fresh process rooted at the project. `cd` also does not persist within parsed chains like `cd dir && command`. Use a command-native cwd flag instead: `npm --prefix <dir> run <script>`, `npm --prefix <dir> exec -- <bin>`, `git -C <dir> ...`, `cargo -C <dir> ...`, `pytest <dir>/tests`.\\n• Glob patterns (`*.ts`) are passed through as literal arguments — no shell expansion. Use `grep -r`, `rg`, `find -name`, etc.\\n• Avoid commands with unbounded output (`netstat -ano`, `find /`, etc.) — they waste tokens. Filter at source: `netstat -ano -p TCP`, `find src -name '*.ts'`, `grep -c`, `wc -l`.\\n\\nCommon read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually — after edits, run the project's tests to verify.\",\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 if (!isAllowAll() && !isCommandAllowed(cmd, getExtraAllowed())) {\n const gate = ctx?.confirmationGate ?? pauseGate;\n const choice = await gate.ask({ kind: \"run_command\", payload: { command: cmd } });\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 effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));\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 (dev server, watcher, any command that doesn't naturally exit) and detach. Waits up to `waitSec` seconds for startup (or until the output matches a readiness signal like 'Local:', 'listening on', 'compiled successfully'), then returns the job id + startup preview. The process keeps running; call `job_output` to tail its logs, `stop_job` to kill it, `list_jobs` to see all running jobs.\\n\\nSame shell constraints as run_command: NO `&&` / `||` / `|` / `;` / `>` / `<` / `2>&1`, `cd` doesn't persist. Dev servers that need a subdirectory: use the tool's own --prefix / --cwd flag. For Vite specifically, `--prefix` on npm only tells npm where package.json is; vite's server root still defaults to process cwd, so pass `vite <project-dir>` or configure via `vite.config.ts` root.\\n\\nUSE THIS — not `run_command` — for: npm/yarn/pnpm run dev, uvicorn / flask run, go run, cargo watch, tsc --watch, webpack serve, anything with 'dev' / 'serve' / 'watch' in the name.\",\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({ kind: \"run_background\", payload: { command: cmd } });\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 until a background job exits or produces new output, bounded by `timeoutMs`. Use this instead of polling `job_output` with identical args when you're intentionally waiting for state to change. Returns JSON with `exited`, `exitCode`, and `latestOutput`.\",\n readOnly: 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 nothing changes. Clamped to 0..30000. Default 5000.\",\n },\n },\n required: [\"jobId\"],\n },\n fn: async (args: { jobId: number; timeoutMs?: number }) => {\n const out = await jobs.waitForJob(args.jobId, { timeoutMs: args.timeoutMs });\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 child.on(\"close\", (code) => {\n job.running = false;\n job.exitCode = code;\n job.signalReady();\n job.signalClosed();\n });\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(id: number, opts: { timeoutMs?: number } = {}): 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(30_000, opts.timeoutMs ?? 5_000));\n const startOutput = job.output;\n let wakeOutput: (() => void) | null = null;\n const outputPromise = new Promise<void>((resolve) => {\n wakeOutput = resolve;\n job.outputWaiters.add(resolve);\n });\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n await Promise.race([\n job.closedPromise,\n outputPromise,\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n }),\n ]);\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 }\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 }\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\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: { ...process.env, 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);\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 = (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 = (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\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 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/** 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(`web_search ${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(\"web_search: Mojeek anti-bot page — rate-limited or blocked\");\n }\n throw new Error(\n `web_search: 0 results but response doesn't look like a real empty page (${html.length} chars, first 120: ${html.slice(0, 120).replace(/\\s+/g, \" \")})`,\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(`web_search: invalid SearXNG endpoint \"${raw}\"`);\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(`web_search: SearXNG endpoint must be http(s), got ${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 `web_search: Cannot reach SearXNG server at ${opts.endpoint ?? \"http://localhost:8080\"}. Please install SearXNG (https://github.com/searxng/searxng) and start it (e.g. \\`docker run -d -p 8080:8080 searxng/searxng\\`), or switch to the default engine with /search-engine mojeek.`,\n );\n }\n throw err;\n }\n if (!resp.ok) throw new Error(`web_search ${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(\n `web_search: 0 results but SearXNG response doesn't look like an empty results page (${html.length} chars)`,\n );\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 const timer = setTimeout(() => ctl.abort(), 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 } finally {\n clearTimeout(timer);\n opts.signal?.removeEventListener(\"abort\", cancel);\n }\n if (!resp.ok) throw new Error(`web_fetch ${resp.status} for ${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(\n `web_fetch refused: content-length ${declaredLen} bytes exceeds ${FETCH_MAX_BYTES}-byte cap (${url})`,\n );\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(\n `web_fetch refused: response body exceeded ${maxBytes}-byte cap (${total} bytes seen)`,\n );\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 `&lt;` into `<` because `&` ran before `<`. */\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(\"web_fetch: url must start with http:// or https://\");\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. \"reasonix chat\", \"bench/baseline\", \"bench/reasonix\"\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/reasonix/latest\";\n\n/** TTL for the on-disk cache entry. 24h keeps noise low; users who\n * want a fresh check can run `reasonix 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 === \"reasonix\"` 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 === \"reasonix\" && 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(), \".reasonix\", \"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 `reasonix 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\n/** False negatives are safe — `npm i -g` works for npx users too. */\nexport function isNpxInstall(): boolean {\n const bin = process.argv[1] ?? \"\";\n if (/[/\\\\]_npx[/\\\\]/.test(bin)) return true;\n if (/[/\\\\]\\.pnpm[/\\\\]/.test(bin) && /dlx/i.test(bin)) return true;\n const ua = process.env.npm_config_user_agent ?? \"\";\n if (ua.includes(\"npx/\")) return true;\n return false;\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 Reasonix'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`. Reasonix'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. Reasonix 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 Reasonix 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: \"reasonix\", 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 try {\n await Promise.race([this.transport.send(frame), promise.then(() => undefined)]);\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 }\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 { ESCALATION_CONTRACT, TUI_FORMATTING_RULES } from \"../prompt-fragments.js\";\n\nexport const CODE_SYSTEM_PROMPT = `You are Reasonix 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# Cite or shut up — non-negotiable\n\nEvery factual claim you make about THIS codebase must be backed by evidence. Reasonix 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 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) 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\nWhen to delegate (call \\`run_skill\\` with a subagent skill):\n- The task would otherwise need >5 file reads or searches.\n- You only need the conclusion, not the exploration trail.\n- The work is self-contained (you can describe it in one paragraph).\n\nWhen NOT to delegate:\n- Direct, narrow questions answerable in 1-2 tool calls — just do them.\n- Anything where you need to track intermediate results yourself (planning, multi-step edits).\n- Anything that requires user interaction (subagents can't submit plans or ask you for clarification).\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\nReasonix 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 are sandbox-relative. \\`/\\` means the project root, \\`/src/foo.ts\\` means \\`<project>/src/foo.ts\\`. Both relative (\\`src/foo.ts\\`) and POSIX-absolute (\\`/src/foo.ts\\`) forms work.\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 && reasonix 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: **dev servers, watchers, any command 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\n**Never use run_command for a dev server.** It will block for 60s, time out, and the user will see a frozen tool call while the server was actually running fine. Always \\`run_background\\`, then \\`job_output\\` to peek at the logs when you need to verify something.\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?)\\` — block until the job exits or emits new output. Prefer this over repeating identical \\`job_output\\` calls while you're intentionally waiting.\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/** Stack order (stable for cache prefix): base → REASONIX.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}\n\nexport function codeSystemPrompt(rootDir: string, opts: CodeSystemPromptOptions = {}): string {\n const base = opts.hasSemanticSearch\n ? `${CODE_SYSTEM_PROMPT}${SEMANTIC_SEARCH_ROUTING}`\n : CODE_SYSTEM_PROMPT;\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(), \".reasonix\", \"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 `reasonix 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":"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","names":["resolve","resolve","readFileSync","homedir","join","path","path","path","path","path","join","homedir","path","readFileSync","settings","resolve","existsSync","readFileSync","dirname","join","t","process","path","t","chmodSync","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","join","homedir","path","existsSync","readFileSync","mkdirSync","dirname","chmodSync","join","stat","join","existsSync","readFileSync","path","mkdirSync","dirname","writeFileSync","chmodSync","readFileSync","t","t","t","decision","existsSync","readFileSync","readdirSync","statSync","join","readFileSync","walk","readdirSync","join","statSync","fs","existsSync","readFileSync","existsSync","readFileSync","join","path","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","parseFrontmatter","homedir","readFileSync","readdirSync","writeFileSync","unlinkSync","path","fs","pathMod","picomatch","i","j","fs","pathMod","picomatch","displayRel","walk","fs","pathMod","displayRel","walk","sep","displayRel","picomatch","fs","stat","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"]}
|