@wrongstack/core 0.292.0 → 0.292.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/utils/assert-never.ts", "../../src/utils/atomic-write.ts", "../../src/types/errors.ts", "../../src/utils/child-env.ts", "../../src/utils/term.ts", "../../src/utils/color.ts", "../../src/utils/config-backup.ts", "../../src/utils/cache-key.ts", "../../src/utils/config-json.ts", "../../src/utils/context-evidence.ts", "../../src/utils/error.ts", "../../src/utils/expect-defined.ts", "../../src/utils/message-invariants.ts", "../../src/core/agent-response.ts", "../../src/core/system-prompt-builder.ts", "../../src/utils/tool-wire-compact.ts", "../../src/utils/token-estimate.ts", "../../src/utils/context-breakdown.ts", "../../src/utils/deep-merge.ts", "../../src/utils/diff.ts", "../../src/utils/glob-expand.ts", "../../src/utils/glob-match.ts", "../../src/utils/incoming-images.ts", "../../src/utils/ip-guard.ts", "../../src/utils/json-repair.ts", "../../src/utils/json-schema-validate.ts", "../../src/utils/merge-custom-models.ts", "../../src/utils/merge-models-payload.ts", "../../src/utils/newline-normalize.ts", "../../src/utils/regex-guard.ts", "../../src/utils/safe-json.ts", "../../src/utils/session-scoped-path.ts", "../../src/utils/sleep.ts", "../../src/utils/slug.ts", "../../src/utils/string.ts", "../../src/utils/task-format.ts", "../../src/utils/todos-format.ts", "../../src/utils/tool-description-mode.ts", "../../src/utils/tool-output-serializer.ts", "../../src/utils/tool-result-render-mode.ts", "../../src/utils/tool-subject.ts", "../../src/utils/ulid.ts", "../../src/utils/wstack-paths.ts"],
4
- "sourcesContent": ["/**\n * Exhaustiveness check for discriminated union switches.\n * Place in the `default` branch of a switch over a union type\n * to get a compile-time error when a new variant is added.\n *\n * @example\n * switch (block.type) {\n * case 'text': return renderText(block);\n * case 'tool_use': return renderToolUse(block);\n * default: return assertNever(block);\n * }\n */\nexport function assertNever(x: never, message?: string): never {\n const err = new Error(\n message ?? `Unhandled case: ${JSON.stringify(x)}`,\n );\n err.name = 'AssertNeverError';\n throw err;\n}\n", "import { randomBytes } from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport { watch as watchDir } from 'node:fs';\nimport type { FSWatcher } from 'node:fs';\nimport * as path from 'node:path';\nimport { FsError } from '../types/errors.js';\n\nexport interface AtomicWriteOptions {\n mode?: number | undefined;\n encoding?: BufferEncoding | undefined;\n}\n\nexport interface FileLockOptions {\n timeoutMs?: number | undefined;\n staleMs?: number | undefined;\n}\n\nexport async function atomicWrite(\n targetPath: string,\n content: string | Uint8Array,\n opts: AtomicWriteOptions = {},\n): Promise<void> {\n const dir = path.dirname(targetPath);\n await fs.mkdir(dir, { recursive: true });\n const tmp = path.join(dir, `.${path.basename(targetPath)}.${randomBytes(6).toString('hex')}.tmp`);\n\n // Write content to tmp first; 'wx' ensures exclusive creation (fails if\n // tmp already exists \u2014 extremely unlikely with 6-byte random suffix).\n try {\n if (typeof content === 'string') {\n await fs.writeFile(tmp, content, { flag: 'wx', encoding: opts.encoding ?? 'utf8' });\n } else {\n await fs.writeFile(tmp, content, { flag: 'wx' });\n }\n try {\n const fh = await fs.open(tmp, 'r+');\n try {\n await fh.sync();\n } finally {\n await fh.close();\n }\n } catch {\n // fsync best-effort\n }\n // Now safely read mode from target (if it exists) and apply to tmp before rename.\n // Prefer opts.mode for new files; for existing files preserve their mode.\n let mode: number | undefined;\n try {\n const stat = await fs.stat(targetPath);\n mode = stat.mode & 0o777;\n } catch {\n mode = opts.mode;\n }\n if (mode !== undefined) {\n await fs.chmod(tmp, mode);\n }\n await renameWithRetry(tmp, targetPath);\n // P3 #20 (before-release.md): on Windows, fs.rename (MoveFileExW) does\n // not preserve Unix permission bits \u2014 the chmod above applies to the tmp\n // file, but the rename may reset the destination's mode to the Windows\n // default. Re-apply the mode after rename on win32 so an edited file\n // keeps its executable bit (or any non-default permission). On POSIX,\n // rename preserves metadata so this is a no-op (chmod is idempotent and\n // cheap), but we gate it on win32 to avoid the extra stat+chmod on the\n // common path.\n if (mode !== undefined && process.platform === 'win32') {\n try {\n await fs.chmod(targetPath, mode);\n } catch {\n // Best-effort: a transient EPERM (antivirus lock) should not fail\n // the write \u2014 the content is already on disk.\n }\n }\n } catch (err) {\n try {\n await fs.unlink(tmp);\n } catch {\n // ignore cleanup error\n }\n throw err;\n }\n}\n\nexport async function ensureDir(dir: string): Promise<void> {\n await fs.mkdir(dir, { recursive: true });\n}\n\nexport async function withFileLock<T>(\n targetPath: string,\n fn: () => Promise<T>,\n opts: FileLockOptions = {},\n): Promise<T> {\n const dir = path.dirname(targetPath);\n await fs.mkdir(dir, { recursive: true });\n const lockPath = path.join(dir, `.${path.basename(targetPath)}.lock`);\n // A lock holder can be scheduled out for several seconds when the full test\n // suite (or a busy workstation) is spawning many child processes. Five\n // seconds was short enough to turn ordinary contention into a dropped\n // best-effort index write. Keep the wait bounded, but leave enough headroom\n // for the holder to resume and release before stale-lock recovery applies.\n const timeoutMs = opts.timeoutMs ?? 15_000;\n const staleMs = opts.staleMs ?? 30_000;\n const started = Date.now();\n let handle: fs.FileHandle | undefined;\n\n for (;;) {\n try {\n handle = await fs.open(lockPath, 'wx');\n await handle.writeFile(`${process.pid}:${Date.now()}`);\n break;\n } catch (err) {\n // If fs.open succeeded but handle.writeFile threw (e.g. ENOSPC, EIO),\n // `handle` owns an open exclusive lock file. Close the handle and remove\n // the orphan lock so the next iteration (or a peer) can acquire it\n // without timing out on the stale-lock window or dead-looping on EEXIST.\n if (handle) {\n await handle.close().catch(() => {});\n await fs.unlink(lockPath).catch(() => {});\n handle = undefined;\n }\n const code = (err as NodeJS.ErrnoException).code;\n // ENOENT means the directory was deleted (e.g. by concurrent cleanup).\n // Recreate it and retry acquiring the lock.\n if (code === 'ENOENT') {\n await fs.mkdir(dir, { recursive: true });\n continue;\n }\n if (code !== 'EEXIST' && code !== 'EPERM') throw err;\n try {\n const stat = await fs.stat(lockPath);\n if (Date.now() - stat.mtimeMs > staleMs) {\n await fs.unlink(lockPath);\n continue;\n }\n } catch {\n continue;\n }\n const elapsed = Date.now() - started;\n if (elapsed >= timeoutMs) {\n throw new FsError({\n message: `Timed out waiting for file lock: ${targetPath}`,\n code: 'FS_ATOMIC_WRITE_FAILED',\n path: targetPath,\n context: { timeoutMs },\n });\n }\n // Wait for the lock to be released, using a filesystem watcher for\n // nearly-instant wake-up instead of polling. The watcher is best-effort:\n // a safety timeout fires at most every 100ms so we don't busy-wait.\n await waitForLockRelease(lockPath, timeoutMs - elapsed);\n }\n }\n\n try {\n return await fn();\n } finally {\n try {\n await handle?.close();\n } catch {\n // ignore\n }\n try {\n await fs.unlink(lockPath);\n } catch {\n // ignore\n }\n }\n}\n\n/**\n * Watch a lock file's parent directory for the file being removed (unlinked),\n * which signals that the lock holder has released it. A safety timeout caps\n * the wait so the overall `withFileLock` timeout is always respected.\n *\n * Uses a bounded safety interval (up to 100ms) so even if `fs.watch` is\n * unavailable or misses the event, we never busy-wait at 25ms fixed polling.\n */\nasync function waitForLockRelease(lockPath: string, remainingMs: number): Promise<void> {\n const parentDir = path.dirname(lockPath);\n const lockName = path.basename(lockPath);\n const intervalMs = Math.min(remainingMs, 100);\n\n return new Promise<void>((resolve) => {\n let settled = false;\n let watcher: FSWatcher | null = null;\n\n // Safety timer \u2014 always fires, even if fs.watch is unavailable.\n const timer = setTimeout(() => {\n settled = true;\n watcher?.close();\n resolve();\n }, intervalMs);\n\n try {\n watcher = watchDir(parentDir, (eventType, filename) => {\n if (settled) return;\n // 'rename' fires on unlink on most platforms; 'change' is a\n // conservative fallback for environments that only emit 'change'.\n if (filename === lockName && (eventType === 'rename' || eventType === 'change')) {\n settled = true;\n clearTimeout(timer);\n watcher?.close();\n resolve();\n }\n });\n } catch {\n // fs.watch not supported (e.g. some container environments, network\n // filesystems). Clear the safety timer and fall back to a single\n // short delay \u2014 the caller's loop will retry on the next iteration.\n clearTimeout(timer);\n if (!settled) {\n settled = true;\n setTimeout(resolve, Math.min(remainingMs, 25));\n }\n return;\n }\n\n // Re-check lock existence after setting up the watch to close the race\n // where the lock was released between our last EEXIST check and now.\n fs.access(lockPath).then(\n () => {\n // Lock still exists \u2014 the watch (or safety timer) will resolve.\n },\n () => {\n // Lock was already released \u2014 respond immediately.\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n watcher?.close();\n resolve();\n }\n },\n );\n });\n}\n\n// On Windows, fs.rename over an existing file can fail with EPERM/EBUSY/EACCES\n// when antivirus, file indexers, editor file watchers, or a concurrent writer\n// briefly hold a handle on the destination. These are transient \u2014 retry with a\n// short backoff before giving up. POSIX renames are atomic and won't hit this.\nconst TRANSIENT_RENAME_CODES = new Set(['EPERM', 'EBUSY', 'EACCES', 'ENOTEMPTY']);\n\nasync function renameWithRetry(from: string, to: string): Promise<void> {\n if (process.platform !== 'win32') {\n await fs.rename(from, to);\n return;\n }\n const delays = [10, 25, 60, 120, 250];\n let lastErr: unknown;\n for (let i = 0; i <= delays.length; i++) {\n try {\n await fs.rename(from, to);\n return;\n } catch (err) {\n lastErr = err;\n const code = (err as NodeJS.ErrnoException)?.code;\n if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {\n throw err;\n }\n await new Promise((resolve) => setTimeout(resolve, delays[i]));\n }\n }\n throw lastErr;\n}\n", "import { toErrorMessage } from '../utils/index.js';\n\n/**\n * WrongStack error hierarchy.\n *\n * Every error thrown by the framework is a `WrongStackError` with a\n * machine-readable `code`, a `subsystem` tag, and a `severity` level.\n * This lets consumers (CLI, TUI, plugins, tests) branch on structured\n * data instead of parsing error messages.\n */\n\n// \u2500\u2500 Error codes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Machine-readable error codes as frozen constants.\n *\n * Use `ERROR_CODES.X` instead of raw string literals for:\n * - IDE autocomplete and compile-time validation\n * - Safe refactoring (rename updates all usages)\n * - Plugin extensibility (extend the object to add custom codes)\n *\n * The `ErrorCode` type is derived from this object, so adding a new\n * code here automatically updates the type without extra changes.\n */\nexport const ERROR_CODES = {\n // Provider\n PROVIDER_RATE_LIMITED: 'PROVIDER_RATE_LIMITED',\n PROVIDER_AUTH_FAILED: 'PROVIDER_AUTH_FAILED',\n PROVIDER_OVERLOADED: 'PROVIDER_OVERLOADED',\n PROVIDER_INVALID_REQUEST: 'PROVIDER_INVALID_REQUEST',\n PROVIDER_SERVER_ERROR: 'PROVIDER_SERVER_ERROR',\n PROVIDER_NETWORK_ERROR: 'PROVIDER_NETWORK_ERROR',\n PROVIDER_CONTEXT_OVERFLOW: 'PROVIDER_CONTEXT_OVERFLOW',\n // Tool\n TOOL_NOT_FOUND: 'TOOL_NOT_FOUND',\n TOOL_PERMISSION_DENIED: 'TOOL_PERMISSION_DENIED',\n TOOL_EXECUTION_FAILED: 'TOOL_EXECUTION_FAILED',\n TOOL_TIMEOUT: 'TOOL_TIMEOUT',\n TOOL_INPUT_INVALID: 'TOOL_INPUT_INVALID',\n // Config\n CONFIG_INVALID: 'CONFIG_INVALID',\n CONFIG_NOT_FOUND: 'CONFIG_NOT_FOUND',\n CONFIG_PARSE_FAILED: 'CONFIG_PARSE_FAILED',\n CONFIG_MIGRATION_NEEDED: 'CONFIG_MIGRATION_NEEDED',\n // Plugin\n PLUGIN_LOAD_FAILED: 'PLUGIN_LOAD_FAILED',\n PLUGIN_API_MISMATCH: 'PLUGIN_API_MISMATCH',\n PLUGIN_MISSING_DEPENDENCY: 'PLUGIN_MISSING_DEPENDENCY',\n // Agent\n AGENT_ITERATION_LIMIT: 'AGENT_ITERATION_LIMIT',\n AGENT_CONTEXT_OVERFLOW: 'AGENT_CONTEXT_OVERFLOW',\n AGENT_ABORTED: 'AGENT_ABORTED',\n AGENT_RUN_FAILED: 'AGENT_RUN_FAILED',\n // Session\n SESSION_NOT_FOUND: 'SESSION_NOT_FOUND',\n SESSION_CORRUPTED: 'SESSION_CORRUPTED',\n SESSION_WRITE_FAILED: 'SESSION_WRITE_FAILED',\n // Container / Registry\n CONTAINER_TOKEN_ALREADY_BOUND: 'CONTAINER_TOKEN_ALREADY_BOUND',\n CONTAINER_TOKEN_NOT_BOUND: 'CONTAINER_TOKEN_NOT_BOUND',\n CONTAINER_CIRCULAR_DEPENDENCY: 'CONTAINER_CIRCULAR_DEPENDENCY',\n REGISTRY_DUPLICATE: 'REGISTRY_DUPLICATE',\n REGISTRY_NOT_FOUND: 'REGISTRY_NOT_FOUND',\n REGISTRY_INVALID: 'REGISTRY_INVALID',\n // File system\n FS_READ_FAILED: 'FS_READ_FAILED',\n FS_WRITE_FAILED: 'FS_WRITE_FAILED',\n FS_MKDIR_FAILED: 'FS_MKDIR_FAILED',\n FS_DELETE_FAILED: 'FS_DELETE_FAILED',\n FS_ATOMIC_WRITE_FAILED: 'FS_ATOMIC_WRITE_FAILED',\n // SDD (Spec-Driven Development)\n SDD_VALIDATION_FAILED: 'SDD_VALIDATION_FAILED',\n SDD_PARSE_FAILED: 'SDD_PARSE_FAILED',\n SDD_INVALID_STATE: 'SDD_INVALID_STATE',\n SDD_NOT_READY: 'SDD_NOT_READY',\n // General\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n PARSE_FAILED: 'PARSE_FAILED',\n UNKNOWN: 'UNKNOWN',\n} as const;\n\n/**\n * Union type derived from `ERROR_CODES`. Using `typeof ERROR_CODES[keyof typeof ERROR_CODES]`\n * instead of a string literal union means TypeScript auto-updates the type whenever\n * a new code is added to `ERROR_CODES` \u2014 no need to keep two lists in sync.\n */\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\nexport type ErrorSubsystem =\n | 'provider'\n | 'tool'\n | 'config'\n | 'plugin'\n | 'agent'\n | 'session'\n | 'sdd'\n | 'container'\n | 'fs'\n | 'general';\nexport type ErrorSeverity = 'fatal' | 'error' | 'warning';\n\n// \u2500\u2500 Base error class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class WrongStackError extends Error {\n readonly code: ErrorCode;\n readonly subsystem: ErrorSubsystem;\n readonly severity: ErrorSeverity;\n readonly recoverable: boolean;\n readonly context?: Record<string, unknown> | undefined;\n\n constructor(opts: {\n message: string;\n code: ErrorCode;\n subsystem: ErrorSubsystem;\n severity?: ErrorSeverity | undefined;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super(opts.message, { cause: opts.cause });\n this.name = 'WrongStackError';\n this.code = opts.code;\n this.subsystem = opts.subsystem;\n this.severity = opts.severity ?? 'error';\n this.recoverable = opts.recoverable ?? false;\n this.context = opts.context;\n }\n\n /**\n * Render a one-line user-facing description.\n * Subclasses should override for domain-specific formatting.\n */\n describe(): string {\n const ctx = this.context ? ` ${formatContext(this.context)}` : '';\n return `${this.code}: ${this.message}${ctx}`;\n }\n}\n\nfunction formatContext(ctx: Record<string, unknown>): string {\n const parts = Object.entries(ctx)\n .filter(([, v]) => v !== undefined)\n .slice(0, 3)\n .map(([k, v]) => `${k}=${String(v)}`);\n return parts.length > 0 ? `[${parts.join(' ')}]` : '';\n}\n\n// \u2500\u2500 Specific error classes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Tool execution errors \u2014 thrown by ToolExecutor and individual tools.\n */\nexport class ToolError extends WrongStackError {\n readonly toolName: string;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n | 'TOOL_NOT_FOUND'\n | 'TOOL_PERMISSION_DENIED'\n | 'TOOL_EXECUTION_FAILED'\n | 'TOOL_TIMEOUT'\n | 'TOOL_INPUT_INVALID'\n >;\n toolName: string;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'tool',\n recoverable: opts.recoverable,\n context: { tool: opts.toolName, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ToolError';\n this.toolName = opts.toolName;\n }\n}\n\n/**\n * Config loading / validation errors.\n */\nexport class ConfigError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'CONFIG_INVALID' | 'CONFIG_NOT_FOUND' | 'CONFIG_PARSE_FAILED' | 'CONFIG_MIGRATION_NEEDED'\n >;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'config',\n severity: 'fatal',\n recoverable: false,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'ConfigError';\n }\n}\n\n/**\n * Plugin loading / lifecycle errors.\n */\nexport class PluginError extends WrongStackError {\n readonly pluginName: string;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'PLUGIN_LOAD_FAILED' | 'PLUGIN_API_MISMATCH' | 'PLUGIN_MISSING_DEPENDENCY'\n >;\n pluginName: string;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'plugin',\n severity: 'error',\n recoverable: opts.code === ERROR_CODES.PLUGIN_MISSING_DEPENDENCY,\n context: { plugin: opts.pluginName, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'PluginError';\n this.pluginName = opts.pluginName;\n }\n}\n\n/**\n * Agent runtime errors \u2014 thrown by Agent.run when a non-WrongStackError\n * escapes the inner loop, so callers always see a structured error.\n */\nexport class AgentError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'AGENT_ITERATION_LIMIT' | 'AGENT_CONTEXT_OVERFLOW' | 'AGENT_ABORTED' | 'AGENT_RUN_FAILED'\n >;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'agent',\n severity: opts.code === ERROR_CODES.AGENT_ABORTED ? 'warning' : 'error',\n recoverable: opts.recoverable ?? opts.code === ERROR_CODES.AGENT_ITERATION_LIMIT,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'AgentError';\n }\n}\n\n/**\n * Wrap an arbitrary thrown value into a `WrongStackError` so the caller\n * always gets a structured error. Pass-throughs WrongStackError instances\n * unchanged; raw `Error`s and primitives get an `AGENT_RUN_FAILED` wrapper\n * with the original preserved as `cause`.\n */\nexport function toWrongStackError(\n err: unknown,\n code: Extract<ErrorCode, 'AGENT_RUN_FAILED' | 'AGENT_ABORTED' | 'UNKNOWN'> = ERROR_CODES.AGENT_RUN_FAILED,\n): WrongStackError {\n if (err instanceof WrongStackError) return err;\n const message = toErrorMessage(err);\n return new AgentError({\n message,\n code: code === 'UNKNOWN' ? ERROR_CODES.AGENT_RUN_FAILED : code,\n cause: err,\n });\n}\n\n/**\n * Session storage errors.\n */\nexport class SessionError extends WrongStackError {\n readonly sessionId?: string | undefined;\n\n constructor(opts: {\n message: string;\n code: Extract<ErrorCode, 'SESSION_NOT_FOUND' | 'SESSION_CORRUPTED' | 'SESSION_WRITE_FAILED'>;\n sessionId?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'session',\n severity: opts.code === ERROR_CODES.SESSION_WRITE_FAILED ? 'error' : 'warning',\n recoverable: opts.code !== ERROR_CODES.SESSION_CORRUPTED,\n context: { sessionId: opts.sessionId, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'SessionError';\n this.sessionId = opts.sessionId;\n }\n}\n\n/**\n * SDD (Spec-Driven Development) errors \u2014 spec validation, parsing, and\n * state machine violations in the AISpecBuilder, TaskFlow, and TaskTracker.\n */\nexport class SddError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'SDD_VALIDATION_FAILED' | 'SDD_PARSE_FAILED' | 'SDD_INVALID_STATE' | 'SDD_NOT_READY'\n >;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'sdd',\n severity: opts.code === ERROR_CODES.SDD_PARSE_FAILED ? 'warning' : 'error',\n recoverable: opts.code === ERROR_CODES.SDD_NOT_READY,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'SddError';\n }\n}\n\n/**\n * File system operation errors.\n */\nexport class FsError extends WrongStackError {\n readonly path?: string | undefined;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'FS_READ_FAILED' | 'FS_WRITE_FAILED' | 'FS_MKDIR_FAILED' | 'FS_DELETE_FAILED' | 'FS_ATOMIC_WRITE_FAILED'\n >;\n path?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'fs',\n severity: 'error',\n recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,\n context: { path: opts.path, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'FsError';\n this.path = opts.path;\n }\n}\n\n/**\n * HTTP fetch error \u2014 thrown when a network request returns a non-OK status.\n * Carries the response status so {@link classifyToolError} can branch on it\n * (429 \u2192 transient, 404 \u2192 not_found, 401 \u2192 permission) without duck-typing\n * the error via `'response' in err`.\n *\n * P3 #18 (before-release.md): the previous `'response' in err` check caught\n * any Error with a `response` property, including custom errors, proxy\n * objects, or mocked errors in tests. `instanceof FetchError` is reliable.\n *\n * Tools and providers that make HTTP requests and need the executor to\n * classify their failures should throw `new FetchError({ status, message })`\n * instead of a bare `Error` with an ad-hoc `response` field.\n */\nexport class FetchError extends WrongStackError {\n readonly status: number;\n\n constructor(opts: {\n message: string;\n status: number;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n severity: 'error',\n recoverable: opts.status === 429 || opts.status >= 500,\n context: { status: opts.status, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'FetchError';\n this.status = opts.status;\n }\n}\n\n/**\n * Tool input validation error \u2014 thrown when a tool's input fails a validation\n * check that the JSON Schema cannot express (e.g. `old_string === new_string`\n * in edit, or a cross-field invariant). Use this instead of a bare\n * `throw new Error('...validation...')` so {@link classifyToolError} can\n * match on `instanceof` rather than a locale-dependent message substring.\n *\n * P2 #6 (before-release.md): the previous `err.message.includes('validation')`\n * check misclassified any error whose message happened to contain \"validation\"\n * (e.g. a third-party \"input validation timeout\") as a VALIDATION error.\n *\n * Named `ToolValidationError` (not `ValidationError`) to avoid colliding with\n * the existing `ValidationError` interface exported by json-schema-validate.ts\n * (a validation-result shape, not an Error subclass).\n */\nexport class ToolValidationError extends WrongStackError {\n constructor(opts: {\n message: string;\n /** Field path or tool name that failed validation, for diagnostics. */\n field?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n severity: 'error',\n recoverable: false,\n context: { field: opts.field, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ToolValidationError';\n }\n}\n\n/**\n * Response / payload parse error \u2014 thrown when an upstream HTTP response,\n * file, or data structure is well-formed at the transport layer (HTTP 200,\n * valid JSON) but is missing required fields or has an unexpected shape.\n *\n * Distinct from `ConfigError(CONFIG_PARSE_FAILED)` (which is specifically\n * for config-file parsing) and `FetchError` (which covers HTTP non-OK\n * responses). `ParseError` fills the gap: the request succeeded but the\n * response body couldn't be interpreted.\n *\n * Common sites: OAuth token responses missing `access_token`, device-code\n * responses missing `device_code`, registry responses with unexpected\n * schemas.\n */\nexport class ParseError extends WrongStackError {\n readonly source?: string | undefined;\n\n constructor(opts: {\n message: string;\n /**\n * What was being parsed \u2014 e.g. `'oauth-token-response'`,\n * `'device-code-response'`. Lets consumers distinguish parse failures\n * from different upstream APIs without parsing the message.\n */\n source?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.PARSE_FAILED,\n subsystem: 'general',\n severity: 'error',\n recoverable: false,\n context: { source: opts.source, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ParseError';\n this.source = opts.source;\n }\n}\n\n// \u2500\u2500 Type guards \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function isWrongStackError(err: unknown): err is WrongStackError {\n return err instanceof WrongStackError;\n}\n\nexport function isToolError(err: unknown): err is ToolError {\n return err instanceof ToolError;\n}\n\nexport function isConfigError(err: unknown): err is ConfigError {\n return err instanceof ConfigError;\n}\n\nexport function isPluginError(err: unknown): err is PluginError {\n return err instanceof PluginError;\n}\n\nexport function isSessionError(err: unknown): err is SessionError {\n return err instanceof SessionError;\n}\n\nexport function isAgentError(err: unknown): err is AgentError {\n return err instanceof AgentError;\n}\n\nexport function isFsError(err: unknown): err is FsError {\n return err instanceof FsError;\n}\n\nexport function isToolValidationError(err: unknown): err is ToolValidationError {\n return err instanceof ToolValidationError;\n}\n\nexport function isFetchError(err: unknown): err is FetchError {\n return err instanceof FetchError;\n}\n\nexport function isParseError(err: unknown): err is ParseError {\n return err instanceof ParseError;\n}\n\nexport function isSddError(err: unknown): err is SddError {\n return err instanceof SddError;\n}\n", "/**\n * Build a sanitized child-process environment.\n *\n * The bash/exec tools and MCP stdio transports execute LLM-generated or\n * configured commands. The parent process carries provider API keys\n * (ANTHROPIC_API_KEY, OPENAI_API_KEY, ...), VCS tokens (GITHUB_TOKEN),\n * and cloud credentials. Forwarding those to a child is an exfiltration\n * vector even with `permission: 'confirm'` \u2014 a compromised MCP server\n * or a cleverly composed shell pipeline can leak secrets.\n *\n * Strategy: copy a small, explicit allowlist of variables that real builds\n * need, then copy anything else that does NOT look secret-bearing. This\n * preserves user-friendly behavior (locale, terminal, npm config) while\n * blocking the obvious leak channels. Two value-side guards back up the\n * name-based filter:\n * - any value carrying an embedded URI credential (`scheme://user:pass@host`,\n * e.g. `DATABASE_URL`/`REDIS_URL`/`*_DSN`) is dropped (WS-01);\n * - `NODE_OPTIONS` is forwarded but with module-preload directives\n * (`--require`/`--import`/`--loader`) stripped so a parent-set value can't\n * inject code into node children (WS-02).\n *\n * Override with `WRONGSTACK_CHILD_ENV_PASSTHROUGH=1` to forward the full\n * parent environment unchanged (opt-in for advanced users who understand\n * the risk).\n */\n\nconst ALLOWED_KEYS = new Set<string>([\n 'PATH',\n 'HOME',\n 'USER',\n 'USERNAME',\n 'LOGNAME',\n 'SHELL',\n 'LANG',\n 'LC_ALL',\n 'LC_CTYPE',\n 'TERM',\n 'TZ',\n 'TMPDIR',\n 'TEMP',\n 'TMP',\n 'PWD',\n 'OLDPWD',\n 'COMSPEC',\n 'SYSTEMROOT',\n 'SYSTEMDRIVE',\n 'WINDIR',\n 'PROGRAMFILES',\n 'PROGRAMFILES(X86)',\n 'PROGRAMDATA',\n 'APPDATA',\n 'LOCALAPPDATA',\n 'USERPROFILE',\n 'PUBLIC',\n 'PATHEXT',\n]);\n\n// Substring match against env-var names (case-insensitive). Bias toward\n// false-positives \u2014 a missing var is recoverable, an exfiltrated key is not.\n// Only consulted for vars NOT on the curated allowlist; PWD/PASSWD-style\n// false positives there are avoided by checking allowlist first.\nconst SECRET_NAME_PARTS = [\n 'TOKEN',\n 'SECRET',\n 'PASSWORD',\n 'PASSWD',\n 'AUTH',\n 'CRED',\n 'BEARER',\n 'COOKIE',\n 'PRIVATE',\n];\n\nfunction looksSecret(name: string): boolean {\n const upper = name.toUpperCase();\n for (const p of SECRET_NAME_PARTS) {\n if (upper.includes(p)) return true;\n }\n // KEY is tricky \u2014 PUBLIC_KEY is fine to forward but most _KEY vars are\n // secrets. Require word boundary so KEYBOARD_LAYOUT etc. are not flagged.\n if (/(?:^|_)KEY(?:$|_|S$)/i.test(upper)) return true;\n if (/API[_-]?KEY/i.test(upper)) return true;\n if (/ACCESS[_-]?KEY/i.test(upper)) return true;\n if (/SESSION[_-]?ID/i.test(upper) === false && /SESSION/i.test(upper)) {\n // SESSION_ID is metadata (we set our own); other SESSION_* often holds\n // session cookies. Be conservative.\n return true;\n }\n return false;\n}\n\n/**\n * Value-side secret detection (WS-01). The name-based `looksSecret` filter\n * misses connection-string variables whose NAME is innocuous but whose VALUE\n * embeds a password \u2014 e.g. `DATABASE_URL=postgres://user:pass@host`,\n * `REDIS_URL=redis://:pass@host`, `MONGO_URI`, `AMQP_URL`, `*_DSN`. Forwarding\n * these to a child (bash/exec/MCP server) leaks the embedded credential.\n *\n * Matches a URI userinfo component that contains a password, i.e.\n * `scheme://[user]:<password>@host`. Deliberately precise: a credential-free\n * URL (`https://api.example.com`, `https://user@host` with no password) is NOT\n * matched, so non-secret `*_URL` knobs (registries, endpoints) still forward.\n */\nfunction valueHasEmbeddedCredential(value: string): boolean {\n // scheme:// then optional user, a ':' , a non-empty password, then '@'.\n // Userinfo chars stop at '/', whitespace, ':' (separator) and '@'.\n return /\\b[a-z][a-z0-9+.-]*:\\/\\/[^/\\s:@]*:[^/\\s@]+@/i.test(value);\n}\n\n/**\n * Code-injection directives that turn `NODE_OPTIONS` into an RCE channel by\n * preloading an arbitrary module into every node child process (WS-02).\n */\nconst NODE_OPTIONS_INJECTION_FLAG =\n /^(?:--require|-r|--import|--loader|--experimental-loader)$/;\nconst NODE_OPTIONS_INJECTION_FLAG_EQ =\n /^(?:--require|-r|--import|--loader|--experimental-loader)=/;\n\n/**\n * Strip module-preload directives from a `NODE_OPTIONS` value while preserving\n * benign flags (`--no-warnings`, `--max-old-space-size=\u2026`, etc.). Handles both\n * the `--require=./x.js` and space-separated `--require ./x.js` forms. Returns\n * the sanitized string (possibly empty).\n */\nexport function sanitizeNodeOptions(value: string): string {\n const tokens = value.split(/\\s+/).filter(Boolean);\n const kept: string[] = [];\n for (let i = 0; i < tokens.length; i++) {\n const tok = tokens[i] as string;\n if (NODE_OPTIONS_INJECTION_FLAG_EQ.test(tok)) continue; // --require=./x\n if (NODE_OPTIONS_INJECTION_FLAG.test(tok)) {\n i++; // also drop the following path token (--require ./x)\n continue;\n }\n kept.push(tok);\n }\n return kept.join(' ');\n}\n\nexport interface BuildChildEnvOptions {\n /** Session ID to inject as WRONGSTACK_SESSION_ID. */\n sessionId?: string | undefined;\n /** Additional env vars to merge (takes priority over filtered parent env). */\n extra?: NodeJS.ProcessEnv | undefined;\n}\n\n/**\n * Commit identity applied to every git-touching child process via the\n * `GIT_AUTHOR_*` / `GIT_COMMITTER_*` env vars. Env-var identity outranks\n * every `git config` layer (repo/global/system), so commits made by any\n * tool (git tool, bash/exec, worktree manager, plugins) carry this\n * name/email without touching the user's git config \u2014 hand-made commits\n * in a normal terminal are unaffected.\n */\nexport interface GitIdentity {\n name?: string | undefined;\n email?: string | undefined;\n}\n\nlet gitIdentity: GitIdentity | null = null;\n\n/**\n * Set (or clear with `null`) the git commit identity injected by\n * `buildChildEnv()`. Wired at boot from the user-level `config.git.identity`\n * (the config loader strips `git` from repo-committed in-project configs \u2014\n * identity spoofing must not be repo-controllable) and re-applied at runtime\n * by the `/gitid` slash command.\n */\nexport function configureChildEnvGitIdentity(identity: GitIdentity | null | undefined): void {\n const name = identity?.name?.trim();\n const email = identity?.email?.trim();\n gitIdentity = name || email ? { name: name || undefined, email: email || undefined } : null;\n}\n\n/** Current configured git identity, or null when none is set. */\nexport function getChildEnvGitIdentity(): Readonly<GitIdentity> | null {\n return gitIdentity;\n}\n\n/**\n * Build a filtered child-process environment suitable for bash, exec, and\n * MCP server subprocesses. Strips API keys, tokens, and other credentials\n * while preserving system/tooling variables.\n */\nexport function buildChildEnv(optsOrSessionId?: BuildChildEnvOptions | string): NodeJS.ProcessEnv {\n const opts: BuildChildEnvOptions =\n typeof optsOrSessionId === 'string'\n ? { sessionId: optsOrSessionId }\n : (optsOrSessionId ?? {});\n\n // WRONGSTACK_CHILD_ENV_PASSTHROUGH may NOT be set via config file.\n // It is a privileged override that opt-outs the entire credential filter\n // and must only be set by the operator's shell environment (real env var,\n // not something a config file injects into process.env). Config-file\n // sources do NOT go through process.env \u2014 only the actual shell environment\n // does \u2014 so checking Object.prototype.hasOwnProperty.call(process.env, ...)\n // is sufficient to exclude config-driven values.\n const hasOwn = Object.hasOwn(process.env, 'WRONGSTACK_CHILD_ENV_PASSTHROUGH');\n const legacyHasOwn = Object.hasOwn(process.env, 'WRONGSTACK_BASH_ENV_PASSTHROUGH');\n const passthrough = (hasOwn && process.env['WRONGSTACK_CHILD_ENV_PASSTHROUGH'] === '1')\n || (legacyHasOwn && process.env['WRONGSTACK_BASH_ENV_PASSTHROUGH'] === '1');\n if (passthrough && !process.env['CI']) {\n console.warn(\n '[agent] WARNING: WRONGSTACK_*_ENV_PASSTHROUGH=1 is active \u2014\\n' +\n ' all parent env vars (including API keys) forwarded to child processes.\\n' +\n ' Do not use on shared or multi-tenant systems.'\n );\n }\n const out: NodeJS.ProcessEnv = {};\n\n // The CLI entry defaults NODE_ENV=production (so React/Ink resolve their\n // production builds \u2014 see cli-main) and marks the injection with this\n // flag. The injected value must NOT reach children: NODE_ENV=production\n // makes `pnpm install` skip devDependencies and flips test-runner\n // behavior. Strip both vars whenever the flag says wrongstack set them \u2014\n // a NODE_ENV genuinely exported by the operator's shell (flag absent)\n // is forwarded unchanged. Applies in passthrough mode too: passthrough\n // means \"the operator's real environment\", which this value is not.\n const nodeEnvDefaulted = process.env['WRONGSTACK_NODE_ENV_DEFAULTED'] === '1';\n\n for (const [k, v] of Object.entries(process.env)) {\n if (v === undefined) continue;\n if (nodeEnvDefaulted && (k === 'NODE_ENV' || k === 'WRONGSTACK_NODE_ENV_DEFAULTED')) continue;\n if (passthrough) {\n out[k] = v;\n continue;\n }\n const upper = k.toUpperCase();\n // 0. Strip any value with an embedded URI credential (user:pass@host),\n // regardless of the variable name (WS-01). Applied before the allowlist\n // so even a \"system\" name carrying a connection string is caught.\n if (valueHasEmbeddedCredential(v)) continue;\n // 1. Forward names on the explicit allowlist \u2014 these are well-known\n // non-secret system variables (PATH, HOME, LANG, ...).\n if (ALLOWED_KEYS.has(upper)) {\n out[k] = v;\n continue;\n }\n // 2. Strip anything that looks like a secret.\n if (looksSecret(upper)) continue;\n // NODE_OPTIONS is forwarded (builds rely on flags like --no-warnings) but\n // module-preload directives (--require/--import/--loader) are stripped \u2014\n // they would let a parent-set NODE_OPTIONS inject code into every node\n // child (WS-02 defense-in-depth).\n if (upper === 'NODE_OPTIONS') {\n const sanitized = sanitizeNodeOptions(v);\n if (sanitized) out[k] = sanitized;\n continue;\n }\n // 3. Forward tooling-prefixed vars that builds commonly need, unless\n // they already failed the secret check above.\n if (\n upper.startsWith('NODE_') ||\n upper.startsWith('NPM_') ||\n upper.startsWith('PNPM_') ||\n upper.startsWith('YARN_') ||\n upper.startsWith('GIT_') ||\n upper.startsWith('CI') ||\n upper.startsWith('XDG_') ||\n // Our own non-secret knobs (WRONGSTACK_HOME, WRONGSTACK_SESSION_ID, \u2026).\n // Secrets never live in WRONGSTACK_* env vars (they're in the encrypted\n // vault). Forwarding keeps child wstack processes \u2014 e.g. ones spawned\n // by the test suite \u2014 inside the same redirected global root.\n upper.startsWith('WRONGSTACK_') ||\n upper === 'EDITOR' ||\n upper === 'VISUAL' ||\n upper === 'PAGER'\n ) {\n out[k] = v;\n }\n }\n\n // Configured commit identity. Applied in passthrough mode too \u2014 it is the\n // operator's explicit intent, not a parent-env leak. Placed BEFORE the\n // extras merge so a caller-provided GIT_* override still wins.\n if (gitIdentity) {\n if (gitIdentity.name) {\n out['GIT_AUTHOR_NAME'] = gitIdentity.name;\n out['GIT_COMMITTER_NAME'] = gitIdentity.name;\n }\n if (gitIdentity.email) {\n out['GIT_AUTHOR_EMAIL'] = gitIdentity.email;\n out['GIT_COMMITTER_EMAIL'] = gitIdentity.email;\n }\n }\n\n // Merge explicit extras AFTER filtering. Callers MUST treat `opts.extra`\n // as a small, user-authored allowlist (e.g. MCP server tokens, LSP env\n // overrides from config). Do NOT pass `process.env` or any object derived\n // from it \u2014 that would defeat the parent-env scrub above. The secret\n // filter is intentionally skipped here so legitimate secret-bearing\n // tokens the user explicitly configured can still reach the child.\n if (opts.extra) {\n Object.assign(out, opts.extra);\n }\n\n if (opts.sessionId) out['WRONGSTACK_SESSION_ID'] = opts.sessionId;\n return out;\n}\n", "/**\n * TTY detection helpers \u2014 the single source of truth for \"is this process\n * running against a real terminal?\". Replaces ad-hoc `process.stdin.isTTY`\n * / `process.stdout.isTTY` checks scattered across the codebase so that:\n *\n * 1. test code can mock a single module instead of stubbing `isTTY` on\n * every ReadStream/WriteStream the test happens to touch;\n * 2. a future TTY-detection source (an env var override, a Windows\n * ConPTY workaround, \u2026) lands in one place;\n * 3. `isInteractive()` encodes the rule the project already used inline\n * (\"both streams are TTYs AND we're not running under CI\") in one\n * testable helper instead of the same 3-condition check in two\n * different files.\n *\n * Scope: detection only. Raw-mode control (`setRawMode`), resize\n * subscriptions, and write-injection belong to a future, larger TTY\n * abstraction; this module is the smallest pull that gives us a\n * testable seam and dedups 20+ call sites.\n */\n\nconst hasStdout = (): boolean => typeof process !== 'undefined' && !!process.stdout;\nconst hasStdin = (): boolean => typeof process !== 'undefined' && !!process.stdin;\n\n/** True when `process.stdout` is attached to a terminal (not a pipe/file). */\nexport function isStdoutTTY(): boolean {\n return hasStdout() && Boolean(process.stdout.isTTY);\n}\n\n/** True when `process.stdin` is attached to a terminal (not a pipe/file). */\nexport function isStdinTTY(): boolean {\n return hasStdin() && Boolean(process.stdin.isTTY);\n}\n\n/**\n * True when the current process is an interactive session: both stdin and\n * stdout are TTYs. Callers that also need a \"not a single-shot invocation\"\n * or \"not under CI\" check should layer that on top \u2014 keeping this helper\n * minimal preserves the original inline checks it replaces.\n */\nexport function isInteractive(): boolean {\n return isStdinTTY() && isStdoutTTY();\n}\n\n/** Current terminal size in characters, with a 24\u00D780 fallback for non-TTYs. */\nexport function getTermSize(): { rows: number; cols: number } {\n if (!hasStdout()) return { rows: 24, cols: 80 };\n return {\n rows: process.stdout.rows ?? 24,\n cols: process.stdout.columns ?? 80,\n };\n}\n\n/**\n * Subscribe to terminal resize events. `cb` is called with the new size each\n * time the underlying stream emits `resize`. Returns a cleanup function the\n * caller MUST call on dispose to remove the listener \u2014 leaving a stale\n * `resize` listener on a disposed component leaks the closure (and the\n * component itself, transitively) until the process exits.\n *\n * The stream argument defaults to `process.stdout`. Pass an explicit\n * `NodeJS.WriteStream` when the caller already owns one (e.g. a status line\n * that targets an injected `out` for testability). For non-TTY streams no\n * listener is registered and the returned cleanup is a no-op.\n */\nexport function onResize(\n cb: (size: { rows: number; cols: number }) => void,\n stream: NodeJS.WriteStream = process.stdout,\n): () => void {\n if (!stream || typeof stream.on !== 'function') return () => {};\n const handler = (): void => {\n cb({\n rows: stream.rows ?? 24,\n cols: stream.columns ?? 80,\n });\n };\n stream.on('resize', handler);\n return () => {\n stream.off('resize', handler);\n };\n}\n\n/**\n * Toggle raw mode on a TTY stdin stream. Returns `true` when the toggle was\n * applied, `false` when the stream is null, not a TTY, or doesn't expose\n * `setRawMode` (pipes, file descriptors, Windows ConPTY edge cases). Callers\n * that need to restore the previous mode should snapshot `input.isRaw`\n * BEFORE the call and pass the value to a second call to flip back.\n *\n * Use this helper to drop the now-redundant\n * `if (input.isTTY) input.setRawMode(...)` ceremony at every call site.\n */\nexport function setRawMode(input: NodeJS.ReadStream, mode: boolean): boolean {\n if (input?.isTTY !== true) return false;\n if (typeof input.setRawMode !== 'function') return false;\n input.setRawMode(mode);\n return true;\n}\n\n/**\n * Bracket installed by the interactive input reader while a `readline`\n * prompt is on screen. Out-of-band terminal writes \u2014 logger WARN/INFO\n * lines, async activity from the Telegram bridge, etc. \u2014 go to the same\n * physical terminal as the half-typed prompt but readline has no idea they\n * happened, so it never repaints. The result is the classic corruption the\n * user sees: every async line strands the in-progress draft as a fresh\n * scrollback row (sometimes with its cursor underline).\n *\n * The guard closes that gap. `suspend()` wipes the draft row so the message\n * prints clean; `resume()` repaints the prompt + draft (cursor preserved).\n * When no prompt is active the guard is `null` and writes pass straight\n * through \u2014 so agent-turn output (spinner, renderer) is untouched.\n */\nexport interface OutputLineGuard {\n /** Clear the current input row right before an out-of-band write. */\n suspend(): void;\n /** Repaint the prompt + in-progress draft right after the write. */\n resume(): void;\n}\n\nlet activeOutputGuard: OutputLineGuard | null = null;\n\n/**\n * Register (or clear, with `null`) the guard that brackets out-of-band\n * writes. Installed by {@link writeOut}/{@link writeErr} consumers \u2014 in\n * practice the CLI's readline input reader \u2014 only while a prompt is live.\n * Idempotent; the most recent caller wins.\n */\nexport function setOutputLineGuard(guard: OutputLineGuard | null): void {\n activeOutputGuard = guard;\n}\n\n/**\n * Stream-agnostic write primitive. Returns `false` when the stream is\n * missing or doesn't expose `write` so callers can degrade silently under\n * hostile host environments (closed pipe, mock injects `null`, test\n * replaces the stream with a stub).\n *\n * When an {@link OutputLineGuard} is installed (a readline prompt is on\n * screen) the write is bracketed by `suspend()`/`resume()` so the user's\n * half-typed input survives the interruption instead of being stranded in\n * scrollback. The guard's own redraw uses raw stream writes \u2014 never\n * `writeOut`/`writeErr` \u2014 so there is no re-entrancy here.\n *\n * **Not exported in the public API.** Exposed only inside `term.ts` for\n * `writeOut` / `writeErr` to share a single implementation. If a caller\n * needs to write to an arbitrary stream, they should call `writeOut` (or\n * `writeErr`) with an explicit `stream` argument \u2014 the named functions\n * are the public surface so the \"this is the standard error stream\"\n * intent stays visible at every call site.\n */\nfunction writeTo(\n s: string,\n stream: NodeJS.WriteStream | undefined,\n): boolean {\n if (!stream || typeof stream.write !== 'function') return false;\n const guard = activeOutputGuard;\n if (!guard) {\n stream.write(s);\n return true;\n }\n // A prompt is live \u2014 wipe the draft row, emit the message, repaint.\n guard.suspend();\n stream.write(s);\n guard.resume();\n return true;\n}\n\n/**\n * Write `s` to `stream` (defaults to `process.stdout`). Returns `false`\n * when the stream is missing or doesn't expose `write` so callers can\n * degrade silently under hostile host environments (closed pipe, mock\n * injects `null`, test replaces the stream with a stub).\n *\n * Why a helper:\n * 1. **Single seam for output capture in tests** \u2014 stub `writeOut` once\n * and assert on what the rest of the codebase intended to print,\n * without spying on `process.stdout.write` (which is brittle and\n * leaks across parallel test files).\n * 2. **Stream swap without grep** \u2014 routing the CLI's output to a\n * logger or `out.log` becomes a one-line change at process boot.\n * 3. **Defensive default** \u2014 closes the \"what if `process.stdout` is\n * `null`\" gap that currently exists at ~50 call sites that just\n * call `process.stdout.write(s)` and crash on certain Windows\n * redirect invocations.\n *\n * Call-site migration is staged: this commit introduces the helper, a\n * follow-up commit replaces the 50+ `process.stdout.write(...)` sites\n * with `writeOut(...)`. Until that migration lands, both forms coexist\n * and `writeOut` is the preferred form for new code.\n */\nexport function writeOut(\n s: string,\n stream: NodeJS.WriteStream = process.stdout,\n): boolean {\n return writeTo(s, stream);\n}\n\n/**\n * Symmetric partner of `writeOut` for the standard error stream. Same shape,\n * same defensive contract, same single-seam-for-tests story \u2014 just defaults to\n * `process.stderr` instead of `process.stdout`.\n *\n * Use this in code paths that emit error/diagnostic/warning text. Keeping\n * these two helpers split (rather than a single `writeTo(s, stream)`) means\n * the call site reads as a clear intent signal: \"I am writing an error\" vs.\n * \"I am writing a result\" \u2014 which matters for callers that decide between\n * stdout/stderr routing (e.g. `--quiet` flags, log-level filtering,\n * structured-log rewriters that fork on stream).\n *\n * Stderr writes from the core logger (see `infrastructure/logger.ts`) and from\n * the TUI guard (see `tui/run-tui.ts`) used to call `process.stderr.write`\n * directly. Routing them through this helper lets tests stub the stream at\n * one boundary and lets future logging middleware (e.g. a JSON-line rewriter)\n * swap the destination for the entire process in one place.\n */\nexport function writeErr(\n s: string,\n stream: NodeJS.WriteStream = process.stderr,\n): boolean {\n return writeTo(s, stream);\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// TerminalCapability \u2014 startup capability profile\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Color depth the terminal can render.\n *\n * 0 = no color (dumb, redirected, `TERM=dumb`)\n * 1 = 16 colors (basic ANSI)\n * 2 = 256 colors (ANSI 256 + bright variants)\n * 3 = 16.7M / 24-bit truecolor\n */\nexport type ColorDepth = 0 | 1 | 2 | 3;\n\n/**\n * Mouse tracking protocol the terminal speaks.\n *\n * 'none' \u2014 no mouse reporting\n * 'x10' \u2014 basic button press/release, capped at 223 columns\n * 'urxvt'\u2014 URXVT extension, supports >223 cols\n * 'sgr' \u2014 SGR extended mode, modern standard, no column cap\n */\nexport type MouseProtocol = 'none' | 'x10' | 'urxvt' | 'sgr';\n\n/**\n * A snapshot of the terminal's capabilities and identity, computed once at\n * startup. Call {@link detectTerminal} once and pass the result through your\n * app \u2014 never re-detect mid-session.\n *\n * Query once \u2192 adapt once. Re-querying on every render is wrong because\n * `$TERM` is static for the process lifetime and stdout.isTTY can only go\n * from true\u2192false (never the reverse), so a mid-session change means the\n * process was started in a terminal and then something went wrong.\n */\nexport interface TerminalCapability {\n /** True when both stdin and stdout are attached to a terminal. */\n isRealTTY: boolean;\n /**\n * Whether the terminal speaks color.\n *\n * Uses the industry-standard precedence:\n * `FORCE_COLOR=0` \u2192 0 (disabled)\n * `FORCE_COLOR` \u2192 3 (force truecolor)\n * `NO_COLOR=\u2026` \u2192 0 (opted out)\n * `COLORTERM=truecolor|24bit` \u2192 3\n * `TERM=\u2026truecolor|24bit` \u2192 3 (some emulators advertise it)\n * `TERM=\u2026256color` \u2192 2\n * fallback \u2192 1\n *\n * Note: `TERM=dumb` always produces 0 even if `COLORTERM=truecolor` is set \u2014\n * `dumb` terminals are non-interactive by definition.\n */\n colorDepth: ColorDepth;\n /**\n * Whether `stdout` can be written to. Determined once at startup from\n * `stdout?.isTTY ?? false`. Even when `isRealTTY` is true, `stdout` may be\n * writable=false (e.g. the terminal was closed mid-session).\n */\n stdoutWritable: boolean;\n /**\n * Best mouse protocol the terminal speaks. Progressive enhancement:\n * attempt SGR first, fall back to URXVT, then X10, then 'none'.\n *\n * The caller (typically the App component) decides whether to enable\n * tracking at all \u2014 this only reports what the terminal CAN understand.\n */\n mouseProtocol: MouseProtocol;\n /**\n * Whether the terminal title can be set via OSC 0 / OSC 2.\n * True when stdout is a TTY and `$TERM` is not `dumb`.\n */\n canSetTitle: boolean;\n /**\n * Whether the terminal understands the tmux DCS passthrough prefix.\n * Detected by seeing `$TERM=tmux*`. When true, escape sequences should be\n * wrapped: `\\x1bPtmux;\\x1b${seq}\\x1b\\\\`.\n */\n isTmux: boolean;\n /**\n * Whether the terminal is on Windows using the legacy conhost backend\n * (cmd.exe, PowerShell non-ConPTY) rather than ConPTY (Windows Terminal,\n * VS Code Integrated Terminal). Used to apply Windows-specific raw-mode\n * handoff logic.\n *\n * Detected by: platform === 'win32' AND `!process.stdout.getColorDepth?.()`\n * (ConPTY exposes color depth; conhost does not). Also false on non-Windows.\n */\n isWindowsConhost: boolean;\n}\n\n/**\n * Parse `process.env.TERM` into a color-depth guess.\n *\n * We intentionally do NOT use the `supports-color` npm package here because\n * (a) it adds a dependency, (b) it resolves at module-import time, and (c) we\n * need to factor in `FORCE_COLOR` / `NO_COLOR` which may be set after import.\n * This pure function is trivial to test and predictable regardless of import\n * order.\n */\nfunction parseColorDepth(env: { FORCE_COLOR?: string; NO_COLOR?: string; COLORTERM?: string; TERM?: string; }): ColorDepth {\n // `FORCE_COLOR=0` is an explicit opt-out, even when `COLORTERM=truecolor`.\n if (env.FORCE_COLOR === '0') return 0;\n // `FORCE_COLOR` with no value or any truthy value forces truecolor.\n if (env.FORCE_COLOR !== undefined) return 3;\n // Explicit user opt-out.\n if (typeof env.NO_COLOR === 'string' && env.NO_COLOR !== '') return 0;\n // Explicit terminal advertisement.\n const colorterm = (env.COLORTERM ?? '').toLowerCase();\n if (colorterm === 'truecolor' || colorterm === '24bit') return 3;\n // TERM strings that advertise rich color.\n const term = (env.TERM ?? '').toLowerCase();\n if (term.includes('truecolor') || term.includes('24bit')) return 3;\n if (term.includes('256color')) return 2;\n // TERM=dumb = no interactive capability at all.\n if (term === 'dumb') return 0;\n // Default to 16-color \u2014 the safest floor. Modern terminals all override this.\n return 1;\n}\n\n/**\n * Detect the best mouse protocol the terminal speaks.\n *\n * Uses the `$TERM` string as a proxy since Node.js has no runtime query for\n * mouse capability (DECSET 1000/1002/1003 responses are not standardized for\n * programmatic use). The approximation is:\n * - `tmux` + `screen` = SGR (they proxy it)\n * - `xterm*`, `rxvt*`, `konsole`, `gnome*` = SGR (post-2013)\n * - `linux`, `vt100`, `dumb` = none\n * - Everything else = URXVT / X10 (try SGR, fall back gracefully in mouse.ts)\n *\n * This is advisory only. The App component gates mouse tracking behind an\n * explicit user/setting opt-in; false positives just mean the tracking enable\n * sequence is ignored harmlessly.\n */\nfunction parseMouseProtocol(term: string): MouseProtocol {\n const t = term.toLowerCase();\n // Terminals that reliably support SGR (1006) mode.\n if (\n t.startsWith('xterm') ||\n t.startsWith('tmux') ||\n t.startsWith('screen') ||\n t.includes('rxvt-unicode') ||\n t.includes('urxvt') ||\n t.includes('konsole') ||\n t.includes('gnome') ||\n t.includes('foot') ||\n t.includes('alacritty') ||\n t.includes('wezterm') ||\n t.includes('kitty') ||\n t.includes('vscode') ||\n t.includes('Apple_Terminal')\n ) {\n return 'sgr';\n }\n // Known mouse-capable but older terminals.\n if (t.startsWith('rxvt') || t.startsWith('linux') || t.includes('Eterm')) {\n return 'urxvt';\n }\n // vt100 / dumb \u2014 no mouse.\n if (t === 'vt100' || t === 'dumb') return 'none';\n // Default: assume SGR is safe to try; mouse.ts falls back silently.\n return 'sgr';\n}\n\n/**\n * Detect the terminal's capabilities and identity at startup.\n *\n * Call once, early in process boot, before any event-loop async has had a\n * chance to corrupt the read of `process.stdout.isTTY`. Store the result and\n * pass it through the app \u2014 do NOT call this on every render.\n *\n * All `env` lookups default gracefully to safe values when the env var is\n * absent or empty, so the return value is deterministic regardless of what\n * the host's environment looks like.\n */\nexport function detectTerminal(\n opts: {\n stdin?: NodeJS.ReadStream | null;\n stdout?: NodeJS.WriteStream | null;\n env?: typeof process.env;\n } = {},\n): TerminalCapability {\n const stdin = opts.stdin ?? process.stdin;\n const stdout = opts.stdout ?? process.stdout;\n const env = opts.env ?? process.env;\n\n const isRealTTY = (stdin?.isTTY ?? false) && (stdout?.isTTY ?? false);\n const stdoutWritable = isRealTTY && typeof stdout?.write === 'function';\n const term = env.TERM ?? '';\n const isTmux = term.toLowerCase().startsWith('tmux');\n const isWindowsConhost = isRealTTY\n && process.platform === 'win32'\n && typeof (stdout as NodeJS.WriteStream & { getColorDepth?: unknown }).getColorDepth !== 'function';\n\n return {\n isRealTTY,\n colorDepth: isRealTTY ? parseColorDepth(env) : 0,\n stdoutWritable,\n mouseProtocol: parseMouseProtocol(term),\n canSetTitle: isRealTTY && term !== 'dumb',\n isTmux,\n isWindowsConhost,\n };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// TerminalLifecycle \u2014 raw-mode lifecycle manager\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Lifecycle owner for the TTY stdin raw-mode state machine.\n *\n * Raw mode is the most dangerous primitive in TUI code. Getting it wrong leaves\n * the user's terminal in a broken state (line-buffered, no echo) that requires\n * closing and reopening the terminal to recover. This class enforces a strict\n * acquire \u2192 hold \u2192 release lifecycle:\n *\n * - `acquire()` may be called only once (idempotent \u2014 subsequent calls no-op).\n * - `release()` restores the previous mode exactly once.\n * - `release()` is called automatically on process `exit` and `beforeExit`.\n * - Any signal handler (SIGINT, SIGTERM, SIGHUP) that calls `release()` clears\n * its own registration so double-signals don't double-restore.\n *\n * Use this instead of calling `setRawMode()` directly at every call site. The\n * single instance lives at module scope in `run-tui.ts`; components that need\n * to signal \"TUI is shutting down\" call `terminalLifecycle.requestExit()` rather\n * than calling `release()` directly.\n */\nexport class TerminalLifecycle {\n private _active = false;\n\n /**\n * The stdin stream we took raw mode on. Set by `acquire()`; undefined before\n * first call. Used by `release()` and by the ConPTY race-closer in\n * `acquire()`.\n */\n private _stdin: NodeJS.ReadStream | undefined;\n\n /**\n * The `isRaw` snapshot captured before the first `acquire()` call, so\n * `release()` restores to the pre-TUI state rather than blindly disabling\n * raw mode. Null when `acquire()` has never been called.\n */\n private _wasRaw: boolean | null = null;\n\n /**\n * Whether stdin was paused before the TUI took ownership. Readline closes\n * its interface by pausing the shared process stream, so raw mode alone is\n * not enough to make Ink receive bytes during the boot-prompt -> TUI handoff.\n */\n private _wasPaused: boolean | null = null;\n\n /**\n * Request the process to exit. Set the flag, trigger any registered\n * `onRequestExit` callback (typically Ink's `unmount()`), and arm a\n * deadline timer. When the timer fires the process is hard-exited.\n *\n * Safe to call multiple times: subsequent calls no-op after the first.\n */\n requestExit: (exitCode?: number) => void;\n\n /**\n * Callback invoked when `requestExit` is called. Registered by the Ink\n * mount point so unmount() runs before the deadline timer fires.\n */\n onRequestExit: (() => void | Promise<void>) | null = null;\n\n constructor() {\n // Build the requestExit closure here so subclasses / tests can override\n // `requestExit` before calling `acquire()`.\n let exitCode = 0;\n let requested = false;\n this.requestExit = (code = 0) => {\n exitCode = code;\n if (requested) return;\n requested = true;\n // Let the Ink unmount handler run (if registered).\n try {\n this.onRequestExit?.();\n } catch {\n // unmount handler threw \u2014 proceed to hard exit.\n }\n // Hard-exit deadline. `unref()` so the timer doesn't keep the event\n // loop alive if everything else has settled.\n setTimeout(() => process.exit(exitCode), 5_000).unref();\n };\n }\n\n /** True while raw mode is held. */\n get active(): boolean {\n return this._active;\n }\n\n /**\n * Acquire raw mode on `stdin`. Idempotent \u2014 calling twice is safe; only the\n * first call has any effect.\n *\n * On Windows ConPTY, calls `setRawMode` twice: once immediately, then again\n * after the next event-loop tick. This closes the ConPTY race where\n * `readline`'s cleanup (registered before this class is constructed) can\n * restore the original cooked mode after we set raw mode but before Ink's\n * render loop takes over the stdin fd. The double-call ensures the last\n * writer wins.\n *\n * @param stdin - The stdin stream (defaults to `process.stdin`).\n * @returns `true` if raw mode was acquired; `false` if stdin is not a TTY\n * or already shut down.\n */\n acquire(stdin: NodeJS.ReadStream = process.stdin): boolean {\n if (this._active) return false;\n\n // Guard: must be a real TTY, must have setRawMode.\n if (stdin?.isTTY !== true) return false;\n if (typeof stdin.setRawMode !== 'function') return false;\n\n // Snapshot the pre-TUI raw state so release() restores correctly.\n this._wasRaw = stdin.isRaw ?? false;\n this._wasPaused = stdin.isPaused();\n this._stdin = stdin;\n\n stdin.setRawMode(true);\n // A preceding readline prompt normally leaves process.stdin paused.\n // Explicitly start the stream before Ink installs its input listener;\n // otherwise the UI can render perfectly while every key appears dead.\n stdin.resume();\n this._active = true;\n\n // Windows ConPTY double-acquire: schedule a second setRawMode on the next\n // tick so we win the race against readline's \"restore original mode\".\n // Safe on non-Windows (no-op: process.platform !== 'win32').\n if (process.platform === 'win32') {\n setImmediate(() => {\n if (this._active && this._stdin?.isTTY) {\n this._stdin.setRawMode?.(true);\n }\n });\n }\n\n return true;\n }\n\n /**\n * Release raw mode and restore the terminal to the state it was in before\n * `acquire()` was called. Idempotent \u2014 subsequent calls are no-ops.\n *\n * Call this on: SIGINT, SIGTERM, SIGHUP, SIGBREAK, and `process.exit`.\n *\n * \u26A0 **Windows caveat**: `setRawMode(false)` on ConPTY restores the original\n * console mode captured when the process started \u2014 not the mode captured by\n * `acquire()`. On ConPTY, the original mode was already raw (ConPTY starts\n * raw), so this call is typically a no-op on Windows Terminal. On conhost.exe\n * it may restore cooked mode, which is correct for that backend.\n */\n release(): void {\n if (!this._active) return;\n this._active = false;\n\n const stdin = this._stdin;\n if (stdin?.isTTY === true) {\n // Restore to pre-TUI state rather than blindly disabling raw mode.\n stdin.setRawMode?.(this._wasRaw ?? false);\n if (this._wasPaused) stdin.pause();\n }\n this._stdin = undefined;\n this._wasRaw = null;\n this._wasPaused = null;\n }\n\n /**\n * Synchronously reset all terminal state written by the TUI layer:\n * raw mode, SGR attributes, cursor visibility, and mouse tracking.\n *\n * Intended for the final `process.on('exit')` handler where async is\n * impossible. Uses `\\x1b[0m` (SGR reset), `\\x1b[?25h` (cursor show),\n * and `\\x1b[?9l` (mouse off) \u2014 all safe to emit even if the terminal does\n * not understand them (unknown CSI sequences are silently ignored).\n *\n * Safe to call multiple times from multiple exit paths because each write\n * is a constant-time synchronous stream write.\n *\n * @param stdout - Stream to reset (defaults to `process.stdout`).\n */\n reset(stdout: NodeJS.WriteStream = process.stdout): void {\n if (typeof stdout?.write !== 'function') return;\n // SGR reset \u2014 clears bold, color, underline, etc.\n stdout.write('\\x1b[0m');\n // Cursor show (in case Ink's unmount left it hidden).\n stdout.write('\\x1b[?25h');\n // Mouse tracking off (all three DECRST sequences \u2014 safe no-ops if never set).\n stdout.write('\\x1b[?1003l\\x1b[?1002l\\x1b[?1000l');\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// safeEscape \u2014 guarded escape-sequence emitter\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Result of emitting an escape sequence. Distinguishes the three distinct\n * failure modes so callers can degrade gracefully.\n */\nexport interface EscapeEmitResult {\n /** `true` when the sequence was written to the stream. */\n ok: boolean;\n /**\n * Human-readable reason when `ok` is `false`. One of:\n * `'not_a_tty'` \u2014 stdout is not a terminal (pipe / redirect / CI)\n * `'unwritable'` \u2014 stdout.write is missing or threw\n * `'empty'` \u2014 `sequence` was the empty string\n */\n reason: 'not_a_tty' | 'unwritable' | 'empty';\n}\n\n/**\n * A terminal escape sequence with its optional string terminator.\n *\n * OSC (title, color palette) sequences MUST be terminated with `BEL (\\x07)`\n * or `ST (\\x1b\\\\)` or the terminal stays in command mode and corrupts\n * subsequent output. CSI sequences (colors, cursor movement) are\n * self-terminating and do not need a terminator byte.\n */\nexport interface EscapeSequence {\n /** The raw sequence, e.g. `\\x1b[38;2;255;0;0m`. */\n raw: string;\n /**\n * Optional terminator byte required by OSC/DCS families.\n * `undefined` for CSI sequences; `\\x07` (BEL) for OSC; `\\x1b\\\\` (ST) for DCS.\n */\n terminator?: string;\n}\n\n/**\n * Shared OSC/DCS terminator bytes.\n *\n * BEL (`\\x07`) is understood by iTerm2, Konsole, mintty, foot, and\n * Windows Terminal. ST (`\\x1b\\\\`) is the strict DEC standard equivalent.\n * Prefer BEL for compatibility; fall back to ST if the terminal's response\n * to BEL is observed to be garbled.\n */\nexport const ESCAPE_TERMINATOR = Object.freeze({\n BEL: '\\x07', // OSC 0 / OSC 2 (title)\n ST: '\\x1b\\\\', // DCS / strict OSC\n});\n\n/**\n * Emit a terminal escape sequence safely.\n *\n * Rules enforced:\n * 1. Empty string \u2192 returns `ok: false, reason: 'empty'`, no write.\n * 2. Not a TTY \u2192 returns `ok: false, reason: 'not_a_tty'`, no write.\n * 3. Stream write throws \u2192 returns `ok: false, reason: 'unwritable'`.\n * 4. OSC/DCS sequences MUST have a terminator byte (guarded assertion).\n * A missing terminator on an OSC sequence corrupts output on terminals\n * that don't understand the sequence \u2014 this function refuses to emit it.\n *\n * @param sequence - The sequence to emit. CSI sequences need no terminator.\n * OSC/DCS sequences must carry `terminator: '\\x07'` or `'\\x1b\\\\'`.\n * @param stdout - Stream to write to (defaults to `process.stdout`).\n * @returns Result indicating success or the specific failure mode.\n */\nexport function safeEmit(\n sequence: EscapeSequence | string,\n stdout: NodeJS.WriteStream = process.stdout,\n): EscapeEmitResult {\n const raw = typeof sequence === 'string' ? sequence : sequence.raw;\n const term = typeof sequence === 'string' ? undefined : sequence.terminator;\n\n if (!raw) {\n return { ok: false, reason: 'empty' };\n }\n if (stdout?.isTTY !== true) {\n return { ok: false, reason: 'not_a_tty' };\n }\n\n // For OSC/DCS sequences (which include `[` or `]` in the body), verify a\n // terminator is present. CSI sequences start with `\\x1b[` and are\n // self-terminating; we treat any sequence that is not a known OSC/DCS\n // as CSI and allow through. OSC starts with `\\x1b]`; DCS with `\\x1bP`.\n if (raw.startsWith('\\x1b]') || raw.startsWith('\\x1bP')) {\n // Assertion: terminator must be provided. Belt-and-suspenders against\n // callers that forget to append `\\x07` \u2014 this prevents corrupted output.\n if (!term) {\n // Defensive: append BEL rather than crashing. This is the only case\n // where we mutate the sequence. The assert-then-fallback pattern is\n // intentional: we catch programmer error in development but degrade\n // safely in production rather than throwing.\n void term; // suppress unused-variable warning\n const safe = `${raw}\\x07`;\n try {\n stdout.write(safe);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' }; // reason field is ignored when ok=true\n }\n try {\n stdout.write(`${raw}${term}`);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' };\n }\n\n // CSI sequence \u2014 self-terminating.\n try {\n stdout.write(raw);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' };\n}\n\n/**\n * Build an OSC 0 / OSC 2 (window/tab title) sequence with a safe terminator.\n *\n * The terminator defaults to `BEL (\\x07)` for broad terminal compatibility.\n * Use `terminator: ESCAPE_TERMINATOR.ST` for stricter terminals that interpret\n * BEL as a beep.\n *\n * @param title - The title string. Embedded BEL bytes are stripped.\n * @param terminator - Defaults to `ESCAPE_TERMINATOR.BEL`.\n */\nexport function buildTitleSequence(\n title: string,\n terminator: string = ESCAPE_TERMINATOR.BEL,\n): EscapeSequence {\n return {\n raw: `\\x1b]0;${title.replace(/\\x07/g, '')}`,\n terminator,\n };\n}\n\n/**\n * Build an SGR (Select Graphic Rendition) color/style sequence.\n *\n * @param codes - SGR parameter list, e.g. `[31]` (red foreground),\n * `[1;32]` (bold green), `[38;2;255;128;0]` (truecolor orange).\n */\nexport function buildSgrSequence(...codes: number[]): EscapeSequence {\n return { raw: `\\x1b[${codes.join(';')}m` };\n}\n\n/**\n * Emit a title sequence. Convenience wrapper around `safeEmit` + `buildTitleSequence`.\n *\n * @param title - The title string.\n * @param stdout - Target stream.\n */\nexport function setTitle(\n title: string,\n stdout: NodeJS.WriteStream = process.stdout,\n): boolean {\n return safeEmit(buildTitleSequence(title), stdout).ok;\n}\n", "import { isStdoutTTY } from './term.js';\n\nconst isColorTty = (): boolean => {\n if (envFlag(process.env.NO_COLOR)) return false;\n if (envFlag(process.env.FORCE_COLOR)) return true;\n return isStdoutTTY();\n};\n\nfunction envFlag(value: string | undefined): boolean {\n if (value === undefined) return false;\n if (value.trim() === '') return false;\n return !/^(0|false|no|off)$/i.test(value.trim());\n}\n\nconst COLOR = isColorTty();\n\nconst wrap =\n (open: string, close: string) =>\n (s: string): string =>\n COLOR ? `\\x1b[${open}m${s}\\x1b[${close}m` : s;\n\nexport const color = {\n reset: wrap('0', '0'),\n bold: wrap('1', '22'),\n dim: wrap('2', '22'),\n italic: wrap('3', '23'),\n underline: wrap('4', '24'),\n red: wrap('31', '39'),\n green: wrap('32', '39'),\n yellow: wrap('33', '39'),\n blue: wrap('34', '39'),\n magenta: wrap('35', '39'),\n cyan: wrap('36', '39'),\n gray: wrap('90', '39'),\n amber: wrap('38;5;214', '39'),\n pink: wrap('38;5;205', '39'),\n bgRed: wrap('41', '49'),\n bgGreen: wrap('42', '49'),\n};\n\nexport function stripAnsi(s: string): string {\n return s.replace(/\\x1b\\[[0-9;]*[A-Za-z]/g, '');\n}\n", "import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * Minimal paths object needed for config backup \u2014 just the global root\n * from which we derive the backup history directory.\n */\nexport interface ConfigBackupPaths {\n globalRoot: string;\n}\n\n/**\n * Backup directory for config file history.\n * Every config write creates a timestamped snapshot here so the user can\n * recover from accidental changes. Backups are never cleaned up automatically.\n */\nexport function configHistoryDir(globalRoot: string): string {\n return path.join(globalRoot, 'config-history');\n}\n\n/**\n * Derive a human-readable slug for a config file path relative to the\n * ~/.wrongstack root. Examples:\n * config.json \u2192 config\n * profiles/default/config.json \u2192 profiles-default-config\n */\nfunction configSlug(absolutePath: string, globalRoot: string): string {\n const rel = path.relative(globalRoot, absolutePath);\n const normalized = rel.replace(/\\\\/g, '/').replace(/\\.json$/i, '');\n return normalized.replace(/\\//g, '-');\n}\n\n/**\n * Before overwriting a config file, save its current content to the\n * config-history directory with a timestamp. Best-effort: failures are\n * silently ignored so they never block the config write itself.\n */\nexport async function backupConfigFile(\n filePath: string,\n paths: ConfigBackupPaths,\n): Promise<void> {\n let currentContent: string;\n try {\n currentContent = await fs.readFile(filePath, 'utf8');\n if (!currentContent.trim()) return;\n } catch {\n return; // ENOENT or other error \u2014 no current content to back up\n }\n\n const now = new Date();\n const ts = now.toISOString()\n .replace(/[:.]/g, '-')\n .replace(/Z$/, '');\n const slug = configSlug(filePath, paths.globalRoot);\n const backupDir = configHistoryDir(paths.globalRoot);\n const backupFile = path.join(backupDir, `${slug}-${ts}.json`);\n\n try {\n await fs.mkdir(backupDir, { recursive: true });\n await fs.writeFile(backupFile, currentContent, { mode: 0o600, encoding: 'utf8' });\n } catch {\n // best-effort \u2014 never block the config write for a backup failure\n }\n}\n", "import { createHash } from 'node:crypto';\nimport type { TextBlock } from '../types/blocks.js';\n\nconst keyCache = new WeakMap<readonly TextBlock[], string>();\n\n/**\n * Derive a stable, provider-agnostic cache-partition key from a frozen\n * system-prompt epoch. Requests that share the same stable prefix produce the\n * same key, so provider backends route them to the same automatic-cache\n * partition \u2014 this is what OpenAI's `prompt_cache_key` (and Gemini implicit\n * routing) needs to actually hit the cache on load-balanced deployments.\n *\n * Keyed off the volatile-free `ctx.systemPrompt` epoch array (the per-turn\n * ledger/next-steps blocks are appended AFTER this array, so they never enter\n * the key). Cached by array identity in a WeakMap \u2014 the sha-256 runs once per\n * epoch (a new array = a new epoch, e.g. on mode switch), not per request.\n *\n * Anthropic ignores this field (it uses `ttl` + `cache_control` markers), so\n * setting it is harmless there; only the wires that read `req.cache.key` act on\n * it, gated by their own capability flags.\n */\nexport function deriveCachePrefixKey(systemPrompt: readonly TextBlock[]): string {\n const cached = keyCache.get(systemPrompt);\n if (cached !== undefined) return cached;\n const h = createHash('sha256');\n for (const block of systemPrompt) h.update(block.text).update('\u0000');\n // 128 bits of hex is ample collision resistance for a routing key and keeps\n // the value short enough for provider length limits (OpenAI caps at 128 chars).\n const key = `ws-${h.digest('hex').slice(0, 32)}`;\n keyCache.set(systemPrompt, key);\n return key;\n}\n", "import * as fs from 'node:fs/promises';\nimport { atomicWrite } from './atomic-write.js';\n\nexport type JsonObject = Record<string, unknown>;\nexport type JsonPathSegment = string | number;\nexport type JsonPath = readonly JsonPathSegment[];\n\nexport async function readJsonObjectFile(filePath: string): Promise<JsonObject> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return isJsonObject(parsed) ? parsed : {};\n } catch {\n return {};\n }\n}\n\nexport async function jsonObjectFileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function writeJsonObjectFile(filePath: string, value: JsonObject): Promise<void> {\n await atomicWrite(filePath, JSON.stringify(value, null, 2), { mode: 0o600 });\n}\n\nexport async function updateJsonObjectFile(\n filePath: string,\n mutator: (config: JsonObject) => void | JsonObject | Promise<void | JsonObject>,\n): Promise<JsonObject> {\n const config = await readJsonObjectFile(filePath);\n const maybeNext = await mutator(config);\n const next = maybeNext && isJsonObject(maybeNext) ? maybeNext : config;\n await writeJsonObjectFile(filePath, next);\n return next;\n}\n\nexport function getJsonPath(root: unknown, path: JsonPath): unknown {\n let current = root;\n for (const segment of path) {\n if (typeof segment === 'number') {\n if (!Array.isArray(current)) return undefined;\n current = current[segment];\n continue;\n }\n if (!isJsonObject(current)) return undefined;\n current = current[segment];\n }\n return current;\n}\n\nexport function setJsonPath(root: JsonObject, path: JsonPath, value: unknown): JsonObject {\n if (path.length === 0) {\n if (!isJsonObject(value)) throw new Error('Root config value must be an object');\n return value;\n }\n const parent = ensureJsonParent(root, path);\n const leaf = lastPathSegment(path);\n if (typeof leaf === 'number') {\n if (!Array.isArray(parent)) throw new Error(`Cannot set numeric segment ${leaf} on non-array parent`);\n parent[leaf] = value;\n } else {\n if (!isJsonObject(parent)) throw new Error(`Cannot set property ${leaf} on non-object parent`);\n parent[leaf] = value;\n }\n return root;\n}\n\nexport function removeJsonPath(root: JsonObject, path: JsonPath): boolean {\n if (path.length === 0) return false;\n const parent = getJsonPath(root, path.slice(0, -1));\n const leaf = lastPathSegment(path);\n if (typeof leaf === 'number') {\n if (!Array.isArray(parent) || leaf < 0 || leaf >= parent.length) return false;\n parent.splice(leaf, 1);\n return true;\n }\n if (!isJsonObject(parent) || !(leaf in parent)) return false;\n delete parent[leaf];\n return true;\n}\n\nexport async function setJsonPathInFile(filePath: string, path: JsonPath, value: unknown): Promise<JsonObject> {\n return updateJsonObjectFile(filePath, (config) => setJsonPath(config, path, value));\n}\n\nexport async function removeJsonPathInFile(filePath: string, path: JsonPath): Promise<JsonObject> {\n return updateJsonObjectFile(filePath, (config) => {\n removeJsonPath(config, path);\n });\n}\n\nexport function isJsonObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction lastPathSegment(path: JsonPath): JsonPathSegment {\n const segment = path[path.length - 1];\n /* v8 ignore next -- defensive: callers guard path.length === 0 before here */\n if (segment === undefined) throw new Error('Invalid empty JSON path');\n return segment;\n}\n\nfunction ensureJsonParent(root: JsonObject, path: JsonPath): JsonObject | unknown[] {\n let current: JsonObject | unknown[] = root;\n for (let i = 0; i < path.length - 1; i += 1) {\n const segment = path[i];\n const nextSegment = path[i + 1];\n /* v8 ignore next -- defensive: sparse-array paths are never produced by callers */\n if (segment === undefined) throw new Error('Invalid empty JSON path segment');\n const nextContainer = typeof nextSegment === 'number' ? [] : {};\n\n if (typeof segment === 'number') {\n if (!Array.isArray(current)) throw new Error(`Cannot traverse numeric segment ${segment} on non-array parent`);\n if (!isJsonObject(current[segment]) && !Array.isArray(current[segment])) current[segment] = nextContainer;\n current = current[segment] as JsonObject | unknown[];\n } else {\n if (!isJsonObject(current)) throw new Error(`Cannot traverse property ${segment} on non-object parent`);\n if (!isJsonObject(current[segment]) && !Array.isArray(current[segment])) current[segment] = nextContainer;\n current = current[segment] as JsonObject | unknown[];\n }\n }\n return current;\n}\n", "import * as path from 'node:path';\nimport type { Context } from '../core/context.js';\nimport type { TextBlock } from '../types/blocks.js';\nimport type { CompactReport } from '../types/compactor.js';\nimport type {\n CompletedWorkEvidence,\n CompletedWorkSource,\n ContextEvidenceState,\n ToolOutputMetadata,\n} from '../types/context-evidence.js';\nimport type { Message } from '../types/messages.js';\n\nconst MAX_TOOL_CALLS = 80;\nconst MAX_FACTS = 40;\nconst MAX_ERRORS = 20;\nconst MAX_DIGEST_CHARS = 4_000;\n/** Cap for the per-iteration reference scan \u2014 see markAssistantReferencedEvidence. */\nconst RECENT_TOOL_CALL_SCAN_LIMIT = 20;\n/** Cap content fed to file/symbol regex extractors (first N chars). */\nconst EXTRACT_CONTENT_CAP_CHARS = 10_000;\n/** Only scan the last N lines for error patterns \u2014 errors surface at the bottom. */\nconst EXTRACT_ERROR_TAIL_LINES = 200;\n\nconst WRITE_TOOLS = new Set(['edit', 'write', 'replace', 'patch']);\nconst READ_TOOLS = new Set(['read', 'grep', 'glob', 'ls', 'tree']);\n\nexport function createContextEvidenceState(): ContextEvidenceState {\n return {\n sessionGoals: [],\n implicitFacts: [],\n activeErrors: [],\n toolCalls: [],\n fileGraph: {},\n repeatedReads: [],\n completedWork: [],\n updatedAt: Date.now(),\n };\n}\n\nexport interface RecordToolOutputEvidenceInput {\n toolUseId: string;\n toolName: string;\n input: unknown;\n content: string;\n ok: boolean;\n outputBytes?: number | undefined;\n outputTokens?: number | undefined;\n outputLines?: number | undefined;\n}\n\nexport function recordUserIntentEvidence(ctx: Context, text: string): void {\n const intent = normalizeWhitespace(text).slice(0, 700);\n if (!intent) return;\n const state = ensureEvidence(ctx);\n state.currentIntent = { text: intent, updatedAt: Date.now() };\n if (state.sessionGoals.length === 0 || isGoalish(intent)) {\n pushUniqueBounded(state.sessionGoals, intent, 8);\n }\n state.updatedAt = Date.now();\n}\n\nexport function recordToolOutputEvidence(\n ctx: Context,\n input: RecordToolOutputEvidenceInput,\n): ToolOutputMetadata {\n const state = ensureEvidence(ctx);\n // Cap content for regex extraction. File paths and symbol declarations\n // appear near the top of tool output (import blocks, function definitions),\n // so the first 10KB captures them. Without this cap, matchAll() runs over\n // the full output \u2014 e.g. a 50KB file read triggers ~100KB of regex scanning\n // across two patterns in extractSymbols plus the extractFiles pass.\n const scanContent = input.content.length > EXTRACT_CONTENT_CAP_CHARS\n ? input.content.slice(0, EXTRACT_CONTENT_CAP_CHARS)\n : input.content;\n const files = extractFiles(ctx, input.toolName, input.input, scanContent);\n const symbols = extractSymbols(scanContent, input.input);\n const commands = extractCommands(input.toolName, input.input);\n const errors = extractErrors(input.content);\n const summary = summarizeToolOutput(input.toolName, input.input, input.content, {\n files,\n symbols,\n errors,\n ok: input.ok,\n });\n\n const metadata: ToolOutputMetadata = {\n toolUseId: input.toolUseId,\n toolName: input.toolName,\n ok: input.ok,\n inputSummary: summarizeInput(input.input),\n summary,\n files,\n symbols,\n commands,\n errors,\n status: 'seen',\n referenceCount: 0,\n seenAt: Date.now(),\n outputBytes: input.outputBytes,\n outputTokens: input.outputTokens,\n outputLines: input.outputLines,\n };\n\n state.toolCalls.push(metadata);\n if (state.toolCalls.length > MAX_TOOL_CALLS) {\n state.toolCalls.splice(0, state.toolCalls.length - MAX_TOOL_CALLS);\n }\n\n updateFileGraph(state, metadata);\n updateRepeatedReadSignals(state, metadata);\n if (errors.length > 0) {\n for (const err of errors) pushUniqueBounded(state.activeErrors, err, MAX_ERRORS);\n }\n const fact = implicitFactFor(metadata);\n if (fact) pushUniqueBounded(state.implicitFacts, fact, MAX_FACTS);\n state.updatedAt = Date.now();\n return metadata;\n}\n\nexport function markAssistantReferencedEvidence(ctx: Context, text: string): void {\n const state = ensureEvidence(ctx);\n const haystack = text.toLowerCase();\n if (!haystack.trim()) return;\n\n // Only scan the most recent tool calls. The assistant almost always\n // references the files/symbols it just worked on \u2014 older entries are\n // rarely re-referenced. Scanning the full list (up to 80 entries) means\n // worst case: 80 \u00D7 (files + symbols) includes() calls per iteration,\n // each O(responseText.length), which degrades as the conversation grows.\n // The last 20 captures the realistic reference window at \u00BC the cost.\n const recent = state.toolCalls.length > RECENT_TOOL_CALL_SCAN_LIMIT\n ? state.toolCalls.slice(-RECENT_TOOL_CALL_SCAN_LIMIT)\n : state.toolCalls;\n for (const tool of recent) {\n if (!metadataReferencedByText(tool, haystack)) continue;\n tool.status = 'referenced';\n tool.referenceCount++;\n tool.referencedAt = Date.now();\n for (const file of tool.files) {\n const node = state.fileGraph[file];\n if (node) node.referenced = true;\n }\n }\n state.updatedAt = Date.now();\n}\n\nexport function buildContextEvidenceDigest(ctx: Context): string {\n const state = ensureEvidence(ctx);\n const lines: string[] = [];\n\n if (state.currentIntent?.text) {\n lines.push(`intent: ${state.currentIntent.text}`);\n }\n\n const goals = state.sessionGoals.slice(-3);\n if (goals.length > 0) {\n lines.push('session_goals:');\n for (const goal of goals) lines.push(`- ${goal}`);\n }\n\n const activeErrors = state.activeErrors.slice(-5);\n if (activeErrors.length > 0) {\n lines.push('active_errors:');\n for (const err of activeErrors) lines.push(`- ${err}`);\n }\n\n const files = Object.values(state.fileGraph)\n .sort((a, b) => (b.writes - a.writes) || (b.reads - a.reads) || a.path.localeCompare(b.path))\n .slice(0, 12);\n if (files.length > 0) {\n lines.push('dependency_graph:');\n for (const file of files) {\n const actions = [\n file.reads > 0 ? `read ${file.reads}x` : '',\n file.writes > 0 ? `write ${file.writes}x` : '',\n ].filter(Boolean).join(', ');\n const refs = file.referenced ? '; referenced by assistant' : '';\n const via = file.lastToolUseId ? `; last via ${file.lastToolUseId}` : '';\n lines.push(`- ${file.path} (${actions || 'seen'}${refs}${via})`);\n }\n }\n\n const referenced = state.toolCalls\n .filter((tool) => tool.status === 'referenced')\n .slice(-10);\n const recentSeen = state.toolCalls\n .filter((tool) => tool.status === 'seen')\n .slice(-5);\n const trail = [...referenced, ...recentSeen];\n if (trail.length > 0) {\n lines.push('tool_trail:');\n for (const tool of trail) {\n const size = tool.outputTokens ? `; ~${tool.outputTokens} tokens` : '';\n const filesText = tool.files.length > 0 ? `; files=${tool.files.slice(0, 4).join(', ')}` : '';\n const symbolsText = tool.symbols.length > 0 ? `; symbols=${tool.symbols.slice(0, 4).join(', ')}` : '';\n lines.push(\n `- ${tool.toolUseId} ${tool.toolName} ${tool.status}: ${tool.summary}${filesText}${symbolsText}${size}`,\n );\n }\n }\n\n const facts = state.implicitFacts.slice(-8);\n if (facts.length > 0) {\n lines.push('implicit_facts:');\n for (const fact of facts) lines.push(`- ${fact}`);\n }\n\n const digest = lines.join('\\n');\n if (digest.length <= MAX_DIGEST_CHARS) return digest;\n return `${digest.slice(0, MAX_DIGEST_CHARS)}... [+${digest.length - MAX_DIGEST_CHARS} chars]`;\n}\n\nexport function repeatedReadPressure(ctx: Context): number {\n return ensureEvidence(ctx).repeatedReads.reduce((max, item) => Math.max(max, item.count), 0);\n}\n\n/** Marker prefixing the forced evidence-floor system message (also its dedupe key). */\nconst CONTEXT_STATE_MARKER = '[context_state]';\n\n/**\n * Stable issue keys emitted by `checkCompactionQuality` and consumed by\n * `injectEvidenceFloor`. Using typed consts instead of raw string literals\n * ensures the producer/consumer contract is typechecker-enforced \u2014 any new\n * issue key must be added here and both sides will be updated together.\n */\nconst QUALITY_ISSUE = {\n missingIntent: 'missing intent anchor',\n missingPathTrail: 'missing tool/path trail',\n} as const;\n\n/**\n * Deterministic post-compaction sanity check. Cheap and local: records whether\n * the compacted context still carries an intent anchor and a tool/path trail.\n * Shared across all compactors so they report quality the same way (previously\n * only HybridCompactor did). Advisory on its own \u2014 pair with\n * `injectEvidenceFloor` to actually repair a flagged loss.\n */\nexport function checkCompactionQuality(\n ctx: Context,\n opts: {\n collapsedDigest?: string | undefined;\n evidenceDigest?: string | undefined;\n reduced: boolean;\n },\n): CompactReport['quality'] {\n const evidence = ctx.contextEvidence;\n const digest = `${opts.collapsedDigest ?? ''}\\n${opts.evidenceDigest ?? ''}`;\n const hasIntent = Boolean(\n evidence?.currentIntent?.text ||\n /\\b(intent|goal|session_goals|hedef|amac|istiyorum|gerekiyor)\\b/i.test(digest),\n );\n const hasPathTrail = Boolean(\n Object.keys(evidence?.fileGraph ?? {}).length > 0 ||\n (evidence?.toolCalls.length ?? 0) > 0 ||\n /\\b(dependency_graph|tool_trail|files=)\\b/i.test(digest),\n );\n const issues: string[] = [];\n if (opts.reduced && !hasIntent) issues.push(QUALITY_ISSUE.missingIntent);\n if (opts.reduced && !hasPathTrail) issues.push(QUALITY_ISSUE.missingPathTrail);\n return { ok: issues.length === 0, hasIntent, hasPathTrail, issues };\n}\n\n/**\n * Enforce an evidence floor: when `checkCompactionQuality` flags that\n * compaction dropped the intent anchor or tool/path trail, prepend a compact\n * `[context_state]` system message rebuilt from the live evidence state so the\n * session goal is never silently lost. Idempotent (won't double-inject) and a\n * no-op when quality is fine or there is no evidence to inject. Returns true\n * when it injected a block (so the caller re-estimates tokens).\n */\nexport function injectEvidenceFloor(\n ctx: Context,\n quality: CompactReport['quality'] | undefined,\n): boolean {\n if (!quality || quality.ok) return false;\n const needsRepair =\n quality.issues.includes(QUALITY_ISSUE.missingIntent) ||\n quality.issues.includes(QUALITY_ISSUE.missingPathTrail);\n if (!needsRepair) return false;\n\n const digest = buildContextEvidenceDigest(ctx);\n if (!digest.trim()) return false;\n\n const already = ctx.messages.some(\n (m) => typeof m.content === 'string' && m.content.startsWith(CONTEXT_STATE_MARKER),\n );\n if (already) return false;\n\n const block: Message = { role: 'system', content: `${CONTEXT_STATE_MARKER}\\n${digest}` };\n ctx.state.replaceMessages([block, ...ctx.messages]);\n return true;\n}\n\nfunction ensureEvidence(ctx: Context): ContextEvidenceState {\n if (!ctx.contextEvidence) {\n (ctx as never as { contextEvidence: ContextEvidenceState }).contextEvidence =\n createContextEvidenceState();\n }\n // States restored from sessions persisted before the completed-work\n // ledger existed lack the array \u2014 heal in place so recorders can push.\n ctx.contextEvidence.completedWork ??= [];\n return ctx.contextEvidence;\n}\n\n// \u2500\u2500 Completed-work ledger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Bound on retained ledger entries \u2014 oldest are dropped first. */\nconst MAX_COMPLETED_WORK = 50;\n/** How many (newest) entries render into the system-prompt block. */\nconst LEDGER_BLOCK_ITEMS = 20;\n\n/** Marker prefixing the ledger's system-prompt block (also used to find/replace it). */\nexport const COMPLETED_WORK_LEDGER_MARKER = '[completed_work_ledger]';\n\nexport interface RecordCompletedWorkInput {\n /** Stable dedupe key, e.g. `task:<id>` \u2014 re-completion updates in place. */\n key: string;\n source: CompletedWorkSource;\n summary: string;\n /** Optional pointer to proof (test run, commit hash, file path). */\n evidence?: string | undefined;\n /** Epoch ms; defaults to now. */\n completedAt?: number | undefined;\n}\n\n/**\n * Append one finished unit of work to the session ledger. The provider request\n * composer renders this state as a volatile tail block; the stable system-prompt\n * prefix is never mutated here.\n */\nexport function recordCompletedWorkEvidence(\n ctx: Context,\n input: RecordCompletedWorkInput,\n): CompletedWorkEvidence {\n const state = ensureEvidence(ctx);\n const entry: CompletedWorkEvidence = {\n key: input.key,\n source: input.source,\n summary: normalizeWhitespace(input.summary).slice(0, 300),\n completedAt: input.completedAt ?? Date.now(),\n ...(input.evidence !== undefined && { evidence: input.evidence }),\n };\n const existing = state.completedWork.findIndex((item) => item.key === entry.key);\n if (existing >= 0) state.completedWork.splice(existing, 1);\n state.completedWork.push(entry);\n if (state.completedWork.length > MAX_COMPLETED_WORK) {\n state.completedWork.splice(0, state.completedWork.length - MAX_COMPLETED_WORK);\n }\n state.updatedAt = Date.now();\n return entry;\n}\n\n/** Render the ledger's system-prompt block text (marker + newest entries). */\nexport function formatCompletedWorkLedger(items: readonly CompletedWorkEvidence[]): string {\n const lines = items\n .slice(-LEDGER_BLOCK_ITEMS)\n .map(\n (item) =>\n `- [${item.source}] ${item.summary}${item.evidence ? ` (evidence: ${item.evidence})` : ''}`,\n );\n return (\n `${COMPLETED_WORK_LEDGER_MARKER}\\n` +\n 'Work already completed this session \u2014 do not redo it; build on it:\\n' +\n lines.join('\\n')\n );\n}\n\n/** Build the current volatile completed-work block without mutating the prompt. */\nexport function buildCompletedWorkLedgerBlock(ctx: Context): TextBlock | undefined {\n const items = ensureEvidence(ctx).completedWork;\n if (items.length === 0) return undefined;\n return {\n type: 'text',\n text: formatCompletedWorkLedger(items),\n cache_control: { type: 'ephemeral' },\n };\n}\n\n/**\n * @deprecated Volatile state must be composed at request time. Kept as a\n * compatibility no-op for embedders importing the old helper.\n */\nexport function syncCompletedWorkLedgerBlock(_ctx: Context): void {\n // Intentionally empty. Mutating ctx.systemPrompt invalidates provider prefix caches.\n}\n\nfunction isGoalish(text: string): boolean {\n return /\\b(goal|objective|task|need|want|implement|fix|improve|refactor|add|remove|hedef|amac|istiyorum|gerekiyor|iyilestir|duzelt|ekle|kaldir)\\b/i.test(text);\n}\n\nfunction normalizeWhitespace(text: string): string {\n return text.replace(/\\s+/g, ' ').trim();\n}\n\nfunction pushUniqueBounded(list: string[], value: string, max: number): void {\n const normalized = normalizeWhitespace(value);\n /* v8 ignore next -- unreachable: every caller passes already-normalized non-empty text */\n if (!normalized) return;\n const existing = list.findIndex((item) => item.toLowerCase() === normalized.toLowerCase());\n if (existing >= 0) list.splice(existing, 1);\n list.push(normalized);\n if (list.length > max) list.splice(0, list.length - max);\n}\n\nfunction extractFiles(\n ctx: Context,\n toolName: string,\n input: unknown,\n content: string,\n): string[] {\n const out = new Set<string>();\n for (const value of inputPathValues(input)) addPath(ctx, out, value);\n\n if (toolName === 'grep' || toolName === 'glob' || toolName === 'bash') {\n const re = /(?:(?:[A-Za-z]:)?[./\\\\]?[\\w@.-]+(?:[\\\\/][\\w@(). -]+)+\\.[A-Za-z0-9]{1,12})/g;\n for (const match of content.matchAll(re)) addPath(ctx, out, match[0]);\n }\n\n return [...out].slice(0, 30);\n}\n\nfunction inputPathValues(input: unknown): string[] {\n const values: string[] = [];\n const visit = (value: unknown, key?: string): void => {\n if (typeof value === 'string') {\n if (key && /^(path|file|files|fromFile|toFile|dir|cwd)$/i.test(key)) values.push(value);\n return;\n }\n if (Array.isArray(value)) {\n for (const item of value) visit(item, key);\n return;\n }\n if (!value || typeof value !== 'object') return;\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) visit(v, k);\n };\n visit(input);\n return values;\n}\n\nfunction addPath(ctx: Context, out: Set<string>, raw: string): void {\n const clean = raw.trim().replace(/^[\"'`]+|[\"'`),;:]+$/g, '');\n if (!clean || clean.length > 260) return;\n let normalized = clean.replace(/\\\\/g, '/');\n try {\n const abs = path.isAbsolute(clean) ? path.resolve(clean) : null;\n if (abs) {\n const rel = path.relative(ctx.projectRoot, abs);\n if (!rel.startsWith('..') && !path.isAbsolute(rel)) {\n normalized = rel.replace(/\\\\/g, '/');\n }\n }\n } catch {\n // Keep the best-effort normalized string.\n }\n if (normalized.length > 0) out.add(normalized);\n}\n\nfunction extractSymbols(content: string, input: unknown): string[] {\n const out = new Set<string>();\n const patterns = [\n /\\b(?:function|class|interface|type|enum|const|let|var|def|fn|struct)\\s+([A-Za-z_$][\\w$]*)/g,\n /\\b(?:export\\s+)?(?:async\\s+)?function\\s+([A-Za-z_$][\\w$]*)/g,\n ];\n for (const re of patterns) {\n for (const match of content.matchAll(re)) {\n if (match[1]) out.add(match[1]);\n if (out.size >= 30) break;\n }\n }\n\n const pattern = input && typeof input === 'object'\n ? (input as Record<string, unknown>)['pattern']\n : undefined;\n if (typeof pattern === 'string' && /^[A-Za-z_$][\\w$]*$/.test(pattern)) {\n out.add(pattern);\n }\n\n return [...out].slice(0, 30);\n}\n\nfunction extractCommands(toolName: string, input: unknown): string[] {\n if (toolName !== 'bash' && toolName !== 'exec' && toolName !== 'shell') return [];\n if (!input || typeof input !== 'object') return [];\n const command = (input as Record<string, unknown>)['command'];\n if (typeof command !== 'string') return [];\n return [command.slice(0, 220)];\n}\n\nfunction extractErrors(content: string): string[] {\n const allLines = content.split(/\\r?\\n/);\n // Only scan the last N lines \u2014 errors and stack traces surface at the\n // bottom of tool output. Scanning all lines means one regex test per line,\n // so a 2000-line file read costs 2000 regex evaluations for no gain since\n // the interesting errors are always at the tail.\n const lines = allLines.length > EXTRACT_ERROR_TAIL_LINES\n ? allLines.slice(-EXTRACT_ERROR_TAIL_LINES)\n : allLines;\n const errors: string[] = [];\n for (const line of lines) {\n if (!/\\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm|typeerror|syntaxerror)\\b/i.test(line)) continue;\n errors.push(normalizeWhitespace(line).slice(0, 260));\n if (errors.length >= 5) break;\n }\n return errors;\n}\n\nfunction summarizeInput(input: unknown): string | undefined {\n if (!input || typeof input !== 'object') return undefined;\n const obj = input as Record<string, unknown>;\n const parts: string[] = [];\n for (const key of ['path', 'file', 'pattern', 'glob', 'command']) {\n const value = obj[key];\n if (typeof value === 'string') parts.push(`${key}=${value.slice(0, 160)}`);\n }\n return parts.length > 0 ? parts.join(', ') : undefined;\n}\n\nfunction summarizeToolOutput(\n toolName: string,\n input: unknown,\n content: string,\n opts: { files: string[]; symbols: string[]; errors: string[]; ok: boolean },\n): string {\n if (!opts.ok && opts.errors.length > 0) return opts.errors[0] ?? `${toolName} failed`;\n if (toolName === 'read' && opts.files[0]) return `read ${opts.files[0]}`;\n if (toolName === 'grep') {\n const pattern = input && typeof input === 'object'\n ? (input as Record<string, unknown>)['pattern']\n : undefined;\n return `searched ${typeof pattern === 'string' ? pattern : 'pattern'} (${opts.files.length} file hint(s))`;\n }\n if ((toolName === 'edit' || toolName === 'write') && opts.files[0]) {\n return `${toolName === 'write' ? 'wrote' : 'edited'} ${opts.files[0]}`;\n }\n const firstLine = normalizeWhitespace(content.split(/\\r?\\n/).find((line) => line.trim()) ?? '');\n return firstLine ? firstLine.slice(0, 220) : `${toolName} returned no text`;\n}\n\nfunction updateFileGraph(state: ContextEvidenceState, metadata: ToolOutputMetadata): void {\n const writes = WRITE_TOOLS.has(metadata.toolName) ? 1 : 0;\n const reads = writes === 0 && (READ_TOOLS.has(metadata.toolName) || metadata.files.length > 0)\n ? 1\n : 0;\n for (const file of metadata.files) {\n const existing = state.fileGraph[file] ?? {\n path: file,\n reads: 0,\n writes: 0,\n tools: [],\n referenced: false,\n };\n existing.reads += reads;\n existing.writes += writes;\n existing.lastToolUseId = metadata.toolUseId;\n pushUniqueBounded(existing.tools, `${metadata.toolName}#${metadata.toolUseId}`, 8);\n state.fileGraph[file] = existing;\n }\n}\n\nfunction updateRepeatedReadSignals(state: ContextEvidenceState, metadata: ToolOutputMetadata): void {\n if (metadata.toolName !== 'read' || metadata.files.length === 0) {\n state.lastReadPath = undefined;\n return;\n }\n const file = metadata.files[0] as string;\n if (state.lastReadPath === file) {\n const existing = state.repeatedReads.find((item) => item.file === file);\n if (existing) {\n existing.count++;\n existing.lastToolUseId = metadata.toolUseId;\n } else {\n state.repeatedReads.push({ file, count: 2, lastToolUseId: metadata.toolUseId });\n }\n if (state.repeatedReads.length > 10) state.repeatedReads.shift();\n }\n state.lastReadPath = file;\n}\n\nfunction implicitFactFor(metadata: ToolOutputMetadata): string | undefined {\n if (metadata.errors.length > 0) return `${metadata.toolName}#${metadata.toolUseId} exposed error: ${metadata.errors[0]}`;\n if (metadata.toolName === 'read' && metadata.files[0]) {\n const size = metadata.outputLines ? ` (${metadata.outputLines} line(s) returned)` : '';\n return `read ${metadata.files[0]}${size}`;\n }\n if ((metadata.toolName === 'edit' || metadata.toolName === 'write') && metadata.files[0]) {\n return `${metadata.toolName} changed ${metadata.files[0]}`;\n }\n if (metadata.status === 'referenced') return `${metadata.toolName}#${metadata.toolUseId} was referenced`;\n return undefined;\n}\n\nfunction metadataReferencedByText(metadata: ToolOutputMetadata, haystack: string): boolean {\n for (const file of metadata.files) {\n const f = file.toLowerCase();\n const base = path.basename(file).toLowerCase();\n if (f && haystack.includes(f)) return true;\n if (base && haystack.includes(base)) return true;\n }\n for (const symbol of metadata.symbols) {\n if (symbol.length >= 3 && haystack.includes(symbol.toLowerCase())) return true;\n }\n for (const err of metadata.errors) {\n const head = err.slice(0, 80).toLowerCase();\n if (head.length >= 12 && haystack.includes(head)) return true;\n }\n return false;\n}\n", "/**\n * Converts an unknown error value to a human-readable string.\n * Used in 40+ files across the codebase to normalize error messaging.\n */\nexport function toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n", "/** Assert a value is neither null nor undefined. Throws if it is.\n * Useful after optional chaining and indexed access when the\n * control flow guarantees the value exists but TypeScript can't\n * prove it (e.g. after a check on a related field). */\nexport function expectDefined<T>(value: T | null | undefined, label?: string): T {\n if (value === null || value === undefined) {\n const err = new Error(label ? `Expected ${label} to be defined` : 'Expected value to be defined');\n err.name = 'ExpectDefinedError';\n throw err;\n }\n return value;\n}\n", "import type { ContentBlock, ToolResultBlock, ToolUseBlock } from '../types/blocks.js';\nimport type { Message } from '../types/messages.js';\nimport { expectDefined } from './expect-defined.js';\nexport interface MessageRepairReport {\n changed: boolean;\n removedToolUses: string[];\n removedToolResults: string[];\n removedMessages: number;\n}\n\nexport interface MessageRepairResult {\n messages: Message[];\n report: MessageRepairReport;\n}\n\n/**\n * Repair provider-level tool-call adjacency invariants.\n *\n * Anthropic requires every assistant `tool_use` block to have a matching\n * `tool_result` block in the immediately following user message. Manual\n * context surgery (summary/prune) can cut through the middle of such an\n * exchange. This function removes only the now-orphaned protocol blocks,\n * preserving surrounding text/images/thinking blocks where possible.\n */\nexport function repairToolUseAdjacency(messages: Message[]): MessageRepairResult {\n const removedToolUses: string[] = [];\n const removedToolResults: string[] = [];\n let removedMessages = 0;\n let changed = false;\n const out: Message[] = [];\n\n for (let i = 0; i < messages.length; i++) {\n const original = expectDefined(messages[i]);\n let msg = original;\n\n if (hasToolUse(msg)) {\n const nextIds = toolResultIds(messages[i + 1]);\n const filtered = mapContent(msg, (blocks) => {\n const next: ContentBlock[] = [];\n for (const block of blocks) {\n if (block.type === 'tool_use' && !nextIds.has(block.id)) {\n removedToolUses.push(block.id);\n changed = true;\n continue;\n }\n next.push(block);\n }\n return next;\n });\n msg = filtered ?? msg;\n }\n\n if (hasToolResult(msg)) {\n const allowed = toolUseIds(out[out.length - 1]);\n const filtered = mapContent(msg, (blocks) => {\n const next: ContentBlock[] = [];\n for (const block of blocks) {\n if (block.type === 'tool_result' && !allowed.has(block.tool_use_id)) {\n removedToolResults.push(block.tool_use_id);\n changed = true;\n continue;\n }\n next.push(block);\n }\n return next;\n });\n msg = filtered ?? msg;\n }\n\n if (isEmptyMessage(msg)) {\n removedMessages++;\n changed = true;\n continue;\n }\n out.push(msg);\n }\n\n return {\n messages: changed ? out : messages,\n report: { changed, removedToolUses, removedToolResults, removedMessages },\n };\n}\n\nfunction hasToolUse(msg: Message | undefined): boolean {\n return contentBlocks(msg).some((b): b is ToolUseBlock => b.type === 'tool_use');\n}\n\nfunction hasToolResult(msg: Message | undefined): boolean {\n return contentBlocks(msg).some((b): b is ToolResultBlock => b.type === 'tool_result');\n}\n\nfunction toolUseIds(msg: Message | undefined): Set<string> {\n const ids = new Set<string>();\n if (msg?.role !== 'assistant') return ids;\n for (const block of contentBlocks(msg)) {\n if (block.type === 'tool_use') ids.add(block.id);\n }\n return ids;\n}\n\nfunction toolResultIds(msg: Message | undefined): Set<string> {\n const ids = new Set<string>();\n if (msg?.role !== 'user') return ids;\n for (const block of contentBlocks(msg)) {\n if (block.type === 'tool_result') ids.add(block.tool_use_id);\n }\n return ids;\n}\n\nfunction contentBlocks(msg: Message | undefined): ContentBlock[] {\n return msg && Array.isArray(msg.content) ? msg.content : [];\n}\n\nfunction mapContent(msg: Message, fn: (blocks: ContentBlock[]) => ContentBlock[]): Message | null {\n if (!Array.isArray(msg.content)) return msg;\n const next = fn(msg.content);\n if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {\n return msg;\n }\n return { ...msg, content: next };\n}\n\n/**\n * True when a message content payload carries meaningful information for\n * the provider: non-whitespace text, a tool call/result, thinking with\n * text or a signature, or any other block type.\n *\n * False for empty strings/arrays and for arrays whose only blocks are\n * empty or whitespace-only text \u2014 the shape persisted when a stream is\n * interrupted before the first meaningful delta (issue #271). Strict\n * providers reject such assistant turns, so repair and replay paths must\n * treat them as empty.\n */\nexport function hasMeaningfulContent(content: Message['content']): boolean {\n if (typeof content === 'string') return content.trim().length > 0;\n for (const block of content) {\n if (block.type === 'text') {\n if (block.text.trim().length > 0) return true;\n continue;\n }\n if (block.type === 'thinking') {\n // Signature-only thinking blocks are valid and required for replay;\n // blocks with neither text nor signature are provider-rejected noise.\n if (block.thinking.trim().length > 0 || block.signature) return true;\n continue;\n }\n // tool_use, tool_result, image, redacted_thinking, \u2026 \u2014 always meaningful.\n return true;\n }\n return false;\n}\n\nfunction isEmptyMessage(msg: Message): boolean {\n return !hasMeaningfulContent(msg.content);\n}\n", "/**\n * Response processing handler \u2014 extracted from Agent class.\n * Handles provider response pipeline, event emission, session\n * persistence, text rendering, and autonomous continuation parsing.\n */\n\nimport { isTextBlock, type TextBlock } from '../types/blocks.js';\nimport type { Request, Response } from '../types/provider.js';\nimport { deriveCachePrefixKey } from '../utils/cache-key.js';\nimport {\n buildCompletedWorkLedgerBlock,\n markAssistantReferencedEvidence,\n} from '../utils/context-evidence.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { hasMeaningfulContent, repairToolUseAdjacency } from '../utils/message-invariants.js';\nimport type { AgentInternals } from './agent-internals.js';\nimport type { Context, RunOptions } from './context.js';\nimport { type ContinueDirective, parseContinueDirective } from './continue-to-next-iteration.js';\n\ninterface ProcessResponseResult {\n finalText: string;\n aborted: boolean;\n done: boolean;\n directive?: ContinueDirective | undefined;\n}\n\nexport interface AgentResponseHandler {\n buildAndRunRequestPipeline(opts: RunOptions): Promise<Request>;\n processResponse(raw: Response, req: Request): Promise<ProcessResponseResult>;\n}\n\nconst MAX_TODO_SNAPSHOT_ITEMS = 10;\nconst MAX_TODO_SNAPSHOT_CONTENT = 180;\n\n/**\n * Build the leader-only, per-request decision gate for `<nextsteps>`.\n *\n * The base system prompt is intentionally frozen for provider caching, while\n * todos change during tool execution. Keeping this block volatile makes the\n * final-response contract follow the live todo state on every iteration.\n */\nexport function buildLiveNextStepsGateBlock(\n ctx: Pick<Context, 'agentId' | 'todos'>,\n): TextBlock | undefined {\n if (ctx.agentId !== 'leader') return undefined;\n\n const openTodos = ctx.todos.filter(\n (todo) => todo.status === 'pending' || todo.status === 'in_progress',\n );\n\n if (openTodos.length === 0) {\n return {\n type: 'text',\n text: [\n '[nextsteps_gate]',\n 'Authoritative live state for this request: open todos = 0.',\n 'On the final response, you MUST take exactly one branch:',\n '1. If at least one genuinely useful follow-on action exists, include a balanced <nextsteps> block containing 1-4 exact prompt messages that can be submitted back to you through the current TUI or WebUI input.',\n 'Every item must ask the agent to perform work. Never put a human-only chore or an instruction addressed to the user inside <nextsteps>; natural-language agent-directed imperatives are valid and need not be shell commands.',\n '2. If no useful follow-on action truly exists, omit <nextsteps> and explicitly tell the user in normal prose that no further steps are needed for this task.',\n 'Silently omitting both is invalid. Do not decide by chance, tone, or response length, and do not invent filler suggestions.',\n '[/nextsteps_gate]',\n ].join('\\n'),\n cache_control: { type: 'ephemeral' },\n };\n }\n\n const todoSnapshot = openTodos.slice(0, MAX_TODO_SNAPSHOT_ITEMS).map((todo) => {\n const normalized = todo.content.replace(/\\s+/g, ' ').trim();\n const content =\n normalized.length > MAX_TODO_SNAPSHOT_CONTENT\n ? `${normalized.slice(0, MAX_TODO_SNAPSHOT_CONTENT - 1)}\u2026`\n : normalized;\n return `- [${todo.status}] ${content}`;\n });\n const omitted = openTodos.length - todoSnapshot.length;\n if (omitted > 0) todoSnapshot.push(`- \u2026and ${omitted} more open todo(s)`);\n\n return {\n type: 'text',\n text: [\n '[nextsteps_gate]',\n `Authoritative live state for this request: open todos = ${openTodos.length}.`,\n 'You MUST omit <nextsteps> entirely while these todos remain open. Continue or finish the tracked work; do not propose unrelated follow-on work.',\n 'Open todo snapshot:',\n ...todoSnapshot,\n '[/nextsteps_gate]',\n ].join('\\n'),\n cache_control: { type: 'ephemeral' },\n };\n}\n\nexport function createAgentResponseHandler(a: AgentInternals): AgentResponseHandler {\n // Each assigned prompt array is one explicit cache epoch. Freeze it at the\n // first request boundary so turn-time code cannot silently invalidate the\n // provider prefix by pushing/replacing blocks in place. Lifecycle actions\n // such as a mode switch may assign a new array, which becomes a new epoch.\n const stabilizedPromptEpochs = new WeakSet<TextBlock[]>();\n\n function stabilizePromptEpoch(): void {\n const prompt = a.ctx.systemPrompt;\n if (stabilizedPromptEpochs.has(prompt)) return;\n for (const block of prompt) {\n if (block.cache_control) Object.freeze(block.cache_control);\n Object.freeze(block);\n }\n Object.freeze(prompt);\n stabilizedPromptEpochs.add(prompt);\n }\n\n async function buildAndRunRequestPipeline(opts: RunOptions): Promise<Request> {\n // Only scan for tool-use adjacency issues when tool content has been\n // added since the last scan. Pure text responses and iterations without\n // tool calls don't introduce new adjacency problems \u2014 skipping the O(n)\n // message-array walk saves ~1-3ms per iteration on large contexts.\n if (a.ctx.toolAdjacencyDirty) {\n const repaired = repairToolUseAdjacency(a.ctx.messages);\n a.ctx.toolAdjacencyDirty = false;\n if (repaired.report.changed) {\n a.ctx.state.replaceMessages(repaired.messages);\n a.events.emit('context.repaired', {\n sessionId: a.ctx.session.id,\n ctx: a.ctx,\n ...repaired.report,\n });\n a.logger.warn(\n `Repaired context tool adjacency: removed ${repaired.report.removedToolUses.length} tool_use block(s), ` +\n `${repaired.report.removedToolResults.length} tool_result block(s), ` +\n `${repaired.report.removedMessages} empty message(s)`,\n );\n }\n }\n stabilizePromptEpoch();\n const volatileLedger = buildCompletedWorkLedgerBlock(a.ctx);\n const liveNextStepsGate = buildLiveNextStepsGateBlock(a.ctx);\n const volatileBlocks = [volatileLedger, liveNextStepsGate].filter(\n (block): block is TextBlock => block !== undefined,\n );\n const system =\n volatileBlocks.length > 0 ? [...a.ctx.systemPrompt, ...volatileBlocks] : a.ctx.systemPrompt;\n const baseReq: Request = {\n model: opts.model ?? a.ctx.model,\n system,\n messages: a.ctx.messages,\n tools: a.tools.list(),\n // Default to the provider's model-native output ceiling so subagents\n // (Chimera, etc.) can run long reports up to the model's actual\n // limit. The provider adapter's `buildBody` substitutes its own\n // fallback (`ctx.capabilities.maxOutput ?? 8192`) when this is\n // absent \u2014 keeping the field optional at the wire layer is what\n // lets the catalog-driven ceiling reach the API untouched.\n maxTokens: a.ctx.provider.capabilities.maxOutput,\n // Provider-agnostic cache-partition key from the stable prompt epoch.\n // Wires that support prompt caching (OpenAI `prompt_cache_key`) read it;\n // the config `ttl` is merged over this by the ModelRuntime middleware.\n cache: { key: deriveCachePrefixKey(a.ctx.systemPrompt) },\n };\n return a.pipelines.request.run(baseReq);\n }\n\n async function processResponse(raw: Response, req: Request): Promise<ProcessResponseResult> {\n let res = raw;\n res = await a.pipelines.response.run(res);\n a.events.emit('provider.response', {\n sessionId: a.ctx.session.id,\n ctx: a.ctx,\n model: req.model,\n content: res.content,\n usage: res.usage,\n stopReason: res.stopReason,\n });\n a.ctx.tokenCounter.account(res.usage, req.model, a.ctx.provider.id);\n\n // Issue #271: never append or persist a semantically empty assistant\n // response (e.g. a stream interrupted before the first meaningful delta,\n // which the response builders represent as a single empty text block).\n // Strict providers reject empty assistant turns on the next request, and\n // once journaled, the malformed turn survived every repair path. Partial\n // text, tool calls, and thinking content remain meaningful and are kept.\n if (hasMeaningfulContent(res.content)) {\n a.ctx.state.appendMessage({ role: 'assistant', content: res.content });\n // If the assistant emitted tool_use blocks, mark the message adjacency\n // as potentially needing repair before the next provider request.\n if (!a.ctx.toolAdjacencyDirty) {\n for (const block of res.content) {\n if (block.type === 'tool_use') {\n a.ctx.toolAdjacencyDirty = true;\n break;\n }\n }\n }\n await a.ctx.session.append({\n type: 'llm_response',\n ts: new Date().toISOString(),\n content: res.content,\n stopReason: res.stopReason,\n usage: res.usage,\n });\n // Tool execution is a side-effect boundary: ensure the response containing\n // its tool_use blocks has reached the session writer before any tool runs.\n // FileSessionWriter keeps failed batches queued for retry; alternate\n // writers may reject, which is logged without masking the provider result.\n try {\n await a.ctx.flushConversationJournal();\n await a.ctx.session.flush();\n } catch (err) {\n (a.logger.debug ?? a.logger.warn)?.(`LLM response flush failed: ${toErrorMessage(err)}`);\n }\n } else {\n a.logger.warn('Empty assistant response \u2014 not appended to context or session', {\n model: req.model,\n stopReason: res.stopReason,\n aborted: a.ctx.signal.aborted,\n });\n }\n\n if (a.ctx.signal.aborted) {\n // M3: collect into an array and join at the end. `finalText += block.text`\n // is O(n\u00B2) on V8 for many concatenations because each `+=` may allocate\n // a new backing string. For a typical 4-block response this is moot,\n // but the streaming-text path concatenates the *full* response in chunks\n // \u2014 and long autonomous loops with verbose reasoning can hit dozens of\n // chunks, making the cost visible. `Array.push` + single `join('')` is\n // amortized O(n).\n const parts: string[] = [];\n for (const block of res.content) {\n if (isTextBlock(block)) parts.push(block.text);\n }\n return { finalText: parts.join(''), aborted: true, done: false };\n }\n\n const parts: string[] = [];\n const streamed = a.ctx.provider.capabilities.streaming;\n for (const block of res.content) {\n if (isTextBlock(block)) {\n const rendered = await a.pipelines.assistantOutput.run(block);\n parts.push(rendered.text);\n if (!streamed) a.renderer?.write(rendered);\n }\n }\n const finalText = parts.join('');\n markAssistantReferencedEvidence(a.ctx, finalText);\n\n let directive: ContinueDirective = 'none';\n if (finalText) {\n directive = parseContinueDirective(finalText);\n }\n\n return { finalText, aborted: false, done: false, directive };\n }\n\n return { buildAndRunRequestPipeline, processResponse };\n}\n", "import { spawn } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { MailboxAgentStatus } from '../coordination/mailbox-types.js';\nimport { SKILL_LIMITS } from '../skills/limits.js';\nimport type { TextBlock } from '../types/blocks.js';\nimport type { ConcreteTokenSavingTier, TokenSavingTier } from '../types/config.js';\nimport { resolveTokenSavingTier } from '../types/config.js';\nimport type { MemoryStore } from '../types/memory.js';\nimport type { ModeStore } from '../types/mode.js';\nimport type { SkillLoader } from '../types/skill.js';\nimport type {\n BuildContext,\n ModelCapabilities,\n SystemPromptBuilder,\n SystemPromptRegions,\n} from '../types/system-prompt.js';\nimport { flattenSystemPromptRegions } from '../types/system-prompt.js';\nimport type { SystemPromptContributor } from '../types/system-prompt-contributor.js';\nimport type { Tool } from '../types/tool.js';\nimport { buildChildEnv } from '../utils/child-env.js';\nimport {\n type InstructionBundle,\n type InstructionBundlePaths,\n loadInstructionBundle,\n mergeInstructionBundle,\n} from './instruction-bundle.js';\nimport { PROMPT as DEFAULT_PROMPT, LEADER_AFTER_TASK_PROMPT } from './modes/default.js';\n\nexport const LAYER_1_IDENTITY = DEFAULT_PROMPT;\n\n/**\n * The section of the system prompt a given TextBlock originated from. Used by\n * `getContextBreakdown()` to attribute real token counts per category in the\n * `/context` display.\n */\nexport type SystemBlockSource =\n | 'identity' // layer1 \u2014 instructions/system.md\n | 'tool-usage' // layer2 \u2014 tool prose summary\n | 'environment' // layer3 \u2014 OS/git/date/skills-in-scope\n | 'skills' // layer4 \u2014 Active Skills bodies (+ memory when injectMemory)\n | 'mode' // layer5 mode prompt + mode-skill hint\n | 'plan' // layer6 \u2014 active plan\n | 'leader-after-task' // leader-only after-task affordances\n | 'contributor' // plugin-contributed volatile blocks\n | 'ledger' // volatile completed-work ledger (request-time)\n | 'nextsteps'; // volatile next-steps gate (request-time)\n\n/**\n * Side-table mapping each system-prompt TextBlock to the section it came from.\n * Kept as a WeakMap rather than a field on TextBlock so the label never reaches\n * the wire: the provider adapters spread blocks verbatim (Anthropic non-ttl\n * `: b`, OpenAI `stripCacheControl` rest-spread), so any extra field would leak.\n * Block identities survive `flattenSystemPromptRegions` into `ctx.systemPrompt`,\n * so a later read of `ctx.systemPrompt` resolves the same entries.\n */\nexport const SYSTEM_BLOCK_SOURCE = new WeakMap<TextBlock, SystemBlockSource>();\n\n/** Tag a freshly-built block with its origin, returning the same reference. */\nfunction tagBlock(block: TextBlock, source: SystemBlockSource): TextBlock {\n SYSTEM_BLOCK_SOURCE.set(block, source);\n return block;\n}\n\nfunction shortSessionId(sessionId: string): string {\n const leaf = sessionId.split('/').pop() ?? sessionId;\n return leaf.length > 12 ? `${leaf.slice(0, 12)}\u2026` : leaf;\n}\n\n/** Canonical shell the `bash` tool targets \u2014 drives the Environment Shell line\n * and the syntax-guidance sub-block. */\ntype EffectiveShell = 'pwsh' | 'powershell' | 'cmd' | 'posix';\n\n/**\n * Derive the shell the `bash` tool will use from `os.platform()` + the pinned\n * `WRONGSTACK_SHELL` value (set at boot by `ensureSessionShell` in\n * @wrongstack/tools). On POSIX this is always `'posix'` and the caller shows the\n * raw `$SHELL`. On Windows with no pinned value (boot didn't run \u2014 tests /\n * embeddings) we report `'cmd'`, matching `bash.ts`'s default for\n * non-PowerShell-looking commands.\n */\nexport function effectiveShell(\n platform: NodeJS.Platform,\n wrongstackShell: string | undefined,\n): EffectiveShell {\n if (platform !== 'win32') return 'posix';\n const v = wrongstackShell?.trim().toLowerCase();\n if (v === 'powershell' || v === 'powershell.exe') return 'powershell';\n if (v === 'pwsh' || v === 'pwsh.exe') return 'pwsh';\n if (v === 'cmd' || v === 'cmd.exe') return 'cmd';\n return 'cmd';\n}\n\nconst SHELL_DISPLAY: Record<Exclude<EffectiveShell, 'posix'>, string> = {\n pwsh: 'pwsh (PowerShell 7+) \u2014 write PowerShell syntax, not bash',\n powershell: 'powershell (Windows PowerShell 5.1) \u2014 write PowerShell syntax, not bash',\n cmd: 'cmd.exe (Command Prompt) \u2014 write cmd syntax, not bash',\n};\n\n/**\n * Shell-specific syntax guidance for the Environment block. Returns `''` for\n * POSIX (the model writes bash natively, so no nudge is needed). `detail:\n * 'short'` is the light-tier one-liner; `'full'` is the complete cheat-sheet.\n * The `&&`/`||` note branches on the PowerShell edition (only pwsh 7 supports\n * them).\n */\nexport function shellGuidanceBlock(shell: EffectiveShell, detail: 'full' | 'short'): string {\n if (shell === 'posix') return '';\n if (shell === 'cmd') {\n if (detail === 'short') {\n return '- Shell syntax: cmd.exe \u2014 use `%VAR%`, `2>nul`, `dir`/`type`/`del`/`where` (NOT bash `$VAR`, `/dev/null`, `ls`/`cat`/`rm`).';\n }\n return [\n '## Shell \u2014 cmd.exe',\n 'The `bash` tool runs **cmd.exe** on this machine. Write cmd syntax, not bash/POSIX:',\n '- Env vars: `%NAME%` (NOT `$NAME`); set with `set NAME=value`.',\n '- Discard output: `2>nul` / `>nul` (NOT `2>/dev/null`).',\n '- No `ls`/`cat`/`rm`/`which`/`head` \u2014 use `dir`/`type`/`del`/`where` and `more`.',\n '- Chain with `&&` / `||` / `&`. Prefer the dedicated read/grep/glob tools over shell file ops.',\n ].join('\\n');\n }\n // pwsh or powershell\n if (detail === 'short') {\n return '- Shell syntax: PowerShell \u2014 use `$env:VAR`, `2>$null`, `Get-Content`/`Select-Object` (NOT bash `$VAR`, `/dev/null`, `cat`/`head`).';\n }\n const chain =\n shell === 'pwsh'\n ? '- Chain with `&&` / `||` (supported in PowerShell 7).'\n : '- `&&` / `||` are NOT available in Windows PowerShell 5.1 \u2014 separate commands with `;` (and check `$LASTEXITCODE`).';\n return [\n `## Shell \u2014 PowerShell${shell === 'pwsh' ? ' 7+ (pwsh)' : ' 5.1 (powershell)'}`,\n 'The `bash` tool runs **PowerShell** on this machine. Write PowerShell syntax, not bash/POSIX:',\n \"- Env vars: read `$env:NAME`, set `$env:NAME = 'value'` (NOT `$NAME`, `%NAME%`, or `export`).\",\n '- Discard output: `... 2>$null` or `$null = ...` (NOT `2>/dev/null`).',\n '- No bash builtins \u2014 use cmdlets: `head -n N`\u2192`Select-Object -First N`, `tail`\u2192`-Last N`, `cat`\u2192`Get-Content`, `which x`\u2192`Get-Command x`, `rm -rf p`\u2192`Remove-Item -Recurse -Force p`, `touch f`\u2192`New-Item -ItemType File f`. Prefer the grep/glob tools over `Select-String`.',\n '- Read a line window of a file: `Get-Content path | Select-Object -Skip N -First M` (the `sed -n` / `head|tail` equivalent).',\n '- Pipes work normally; `rg`/`git`/`node` and other native exes run as-is \u2014 only the *shell builtins* differ. (`rg --files src | rg pattern` is fine.)',\n '- Call exes whose path has spaces via the call operator: `& \"C:\\\\Program Files\\\\app.exe\" args`.',\n \"- Multi-line literals: single-quoted here-string `@'\u2026'@` with the closing `'@` at column 0.\",\n '- Non-interactive only: no `Read-Host`/`Get-Credential`/`pause`; add `-Confirm:$false` to destructive cmdlets.',\n chain,\n ].join('\\n');\n}\n\nexport interface DefaultSystemPromptBuilderOptions {\n memoryStore?: MemoryStore | undefined;\n /**\n * Inject a static \"# Relevant Memory\" section into the built prompt from\n * `memoryStore`. Default: true. Set to false when a dedicated per-turn memory\n * retriever (the Super Memory turn middleware) is the single injection\n * channel \u2014 this avoids double-injecting the same memories and keeps all\n * memory context flowing through one system.\n */\n injectMemory?: boolean | undefined;\n skillLoader?: SkillLoader | undefined;\n /**\n * How skill bodies reach the prompt. `'eager'` (default) injects every\n * discovered skill body; `'progressive'` injects only a name+trigger manifest\n * and relies on the agent calling the `skill` tool to load a body on demand\n * (the agentskills.io progressive-disclosure model).\n */\n skillMode?: 'eager' | 'progressive' | undefined;\n /**\n * In eager mode, cap the total chars of injected skill bodies (highest-priority\n * skills first); the rest become a load-on-demand manifest. Bounds prompt\n * cost when many skills are discovered. Default ~24k chars.\n */\n skillEagerMaxChars?: number | undefined;\n modeStore?: ModeStore | undefined;\n /** Pre-resolved active mode id \u2014 shown in environment block. */\n modeId?: string | undefined;\n /** Pre-resolved mode prompt \u2014 avoids redundant modeStore.getActiveMode() call. */\n modePrompt?: string | undefined;\n /** Model capabilities \u2014 object snapshot or lazy getter for live model switches. */\n modelCapabilities?: ModelCapabilities | (() => ModelCapabilities | undefined) | undefined;\n todayIso?: string | undefined;\n /**\n * Path to the session's plan JSON, or a getter that returns it. When\n * set, the builder reads the file on every `build()` call and injects\n * an \"Active plan\" block listing open items, so the LLM is anchored to\n * the strategic roadmap every turn \u2014 not just at resume. The block is\n * tagged `ephemeral` so a plan edit on turn N doesn't invalidate the\n * provider's prefix cache for earlier turns.\n *\n * The function form lets callers bind the builder before the session\n * id is known (e.g. DI containers that resolve the builder lazily) \u2014\n * the getter is called at build-time, after the session has been\n * created.\n */\n planPath?: string | (() => string | undefined);\n /**\n * System prompt contributors \u2014 called on every `build()` to inject\n * additional TextBlocks. Use `ExtensionRegistry.listSystemPromptContributors()`\n * or pass a plain array. Contributors are called in order; a throwing\n * contributor is caught and logged without aborting the build.\n */\n contributors?: readonly SystemPromptContributor[] | undefined;\n /**\n * Token-saving mode tier. Controls how aggressively the system prompt is\n * compacted: skill bodies are omitted/trimmed, tool hints are shortened,\n * and optional guidance sections (delegation, mailbox, context management)\n * use minimal versions to reduce per-request tokens.\n *\n * - 'off' \u2014 Full guidance (no reduction)\n * - 'minimal' \u2014 TIER1 tools, stripped guidance\n * - 'light' \u2014 TIER1 + memory tools, minimal patterns\n * - 'medium' \u2014 TIER1 + TIER2 tools, some guidance\n * - 'aggressive' \u2014 Maximum reduction before tools become unusable\n *\n * Boolean values are accepted for backward compatibility:\n * - `true` \u2192 'medium'\n * - `false` \u2192 'off'\n */\n tokenSavingMode?: TokenSavingTier | boolean | undefined;\n /**\n * File-backed instruction layers. Builtins are loaded first, then global\n * overrides, then project overrides, then explicit files. This lets durable\n * system instructions live outside TypeScript while keeping the builder API\n * stable for embedded runtimes.\n */\n instructionPaths?: InstructionBundlePaths | undefined;\n /**\n * Last-mile in-memory overrides, applied after instructionPaths. Useful for\n * tests, embedders, and plugin-provided prompt experiments.\n */\n instructionBundle?: InstructionBundle | undefined;\n}\n\nexport class DefaultSystemPromptBuilder implements SystemPromptBuilder {\n /**\n * Cached environment block, keyed by projectRoot. A single builder\n * instance is normally reused across turns of the same agent run, but\n * tests and library consumers may reuse it across runs with different\n * roots; keying the cache prevents leaking the first call's project\n * state into a later call against an unrelated project.\n */\n private envCacheByRoot = new Map<string, string>();\n private skillCache?: string | undefined;\n /** Cached full skill bodies (after frontmatter), built once per session. */\n private skillBodyCache?: string | undefined;\n /** Tools from last build \u2014 used for memory relevance scoring. */\n private _lastBuildTools?: Tool[] | undefined;\n /** Cached rendered online agents string, keyed by content fingerprint. */\n private _lastOnlineAgents?: { hash: string; text: string } | undefined;\n /** Cached full buildToolUsage output \u2014 keyed by tools array ref + agents fingerprint + tier. */\n private _toolsUsageCache?:\n | { toolsRef: readonly Tool[]; agentsHash: string; tier: string; text: string }\n | undefined;\n private _instructionBundle?: Promise<InstructionBundle> | undefined;\n constructor(private readonly opts: DefaultSystemPromptBuilderOptions = {}) {}\n\n /**\n * Normalizes `tokenSavingMode` to a boolean for backward-compatible boolean checks.\n * - `undefined` / `false` / `'off'` \u2192 false\n * - `true` / any tier string other than `'off'` \u2192 true\n *\n * Note: invalid tier strings (e.g. \"MINIMAL\") are coerced to 'off'\n * by `normalizeTokenSavingTier` via the `tier` getter, so isCompact\n * correctly returns false for them \u2014 preventing isCompact/tier\n * disagreement on bad input.\n */\n private get isCompact(): boolean {\n return this.tier !== 'off';\n }\n\n /** Exposes the effective (concrete) `TokenSavingTier` for tier-aware guidance\n * decisions. Invalid strings coerce to 'off'; the `'auto'` sentinel expands\n * from the model's context window (cache-safe \u2014 the window is stable per\n * session, so the resolved tier and therefore the prompt prefix stay stable).\n * See packages/core/src/types/config.ts. */\n private get tier(): ConcreteTokenSavingTier {\n return resolveTokenSavingTier(\n this.opts.tokenSavingMode,\n this.modelCapabilities()?.maxContextTokens,\n );\n }\n\n /**\n * Returns the max tool description length for the current tier.\n * Per the design doc: off=80, minimal=40, light=50, medium=60, aggressive=70.\n */\n private toolDescLimit(): number {\n switch (this.tier) {\n case 'minimal':\n return 30;\n case 'light':\n return 40;\n case 'medium':\n return 50;\n case 'aggressive':\n return 60;\n default:\n return 70;\n }\n }\n\n async build(ctx: BuildContext): Promise<TextBlock[]> {\n return flattenSystemPromptRegions(await this.buildRegions(ctx));\n }\n\n async buildRegions(ctx: BuildContext): Promise<SystemPromptRegions> {\n this._lastBuildTools = ctx.tools;\n // Pre-load skill entries so we can include them in the environment block\n // (which is cached). Skills are static per-session, so this is safe.\n if (this.opts.skillLoader && !this.skillCache) {\n try {\n const entries = await this.opts.skillLoader.listEntries();\n if (entries.length > 0) {\n const lines: string[] = [];\n for (const e of entries) {\n // Compact format: name + shortened trigger (full body in Active Skills)\n const shortTrigger = compactTrigger(e.trigger);\n lines.push(`- **${e.name}** (${shortTrigger})`);\n }\n this.skillCache = lines.join('\\n');\n }\n } catch {\n // skip\n }\n }\n\n const instructions = await this.instructions();\n const layer1 = instructions.system?.identity ?? LAYER_1_IDENTITY;\n const layer2 = await this.buildToolUsage(ctx.tools, ctx);\n const layer3 = await this.buildEnvironment(ctx);\n const layer3WithDir = `${layer3}\\n- Project root: ${ctx.projectRoot}`;\n const layer4 = await this.buildMemoryAndSkills();\n const layer5 = await this.buildMode();\n // Plans anchor the HOST agent across turns. Subagents run one\n // narrow task and shouldn't carry the host's strategic context \u2014\n // it just bloats their prompt and risks them mutating a plan\n // they weren't supposed to touch.\n const layer6 = ctx.subagent ? '' : await this.buildActivePlan();\n\n const core: TextBlock[] = [\n tagBlock({ type: 'text', text: layer1 }, 'identity'),\n tagBlock({ type: 'text', text: layer2 }, 'tool-usage'),\n ];\n const session: TextBlock[] = [\n tagBlock({ type: 'text', text: layer3WithDir }, 'environment'),\n ];\n const volatile: TextBlock[] = [];\n\n if (layer4.trim()) {\n session.push(\n tagBlock(\n { type: 'text', text: layer4, cache_control: { type: 'ephemeral' } },\n 'skills',\n ),\n );\n }\n\n if (layer5.trim()) {\n session.push(\n tagBlock(\n { type: 'text', text: layer5, cache_control: { type: 'ephemeral' } },\n 'mode',\n ),\n );\n }\n\n // Suggested skills for the active mode \u2014 helps the model know which\n // domain instructions to prioritize when multiple skills are loaded.\n if (this.opts.modeStore && this.opts.skillLoader) {\n try {\n const activeMode = await this.opts.modeStore.getActiveMode();\n if (activeMode?.suggestedSkills && activeMode.suggestedSkills.length > 0) {\n const skills = await this.opts.skillLoader.list();\n const loadedNames = new Set(skills.map((s) => s.name));\n const available = activeMode.suggestedSkills.filter((n) => loadedNames.has(n));\n if (available.length > 0) {\n session.push(\n tagBlock(\n {\n type: 'text',\n text: `Mode \"${activeMode.id}\" works best with these skills: ${available.join(', ')}. Their full instructions are in the Active Skills block above.`,\n cache_control: { type: 'ephemeral' },\n },\n 'mode',\n ),\n );\n }\n }\n } catch {\n // skip \u2014 non-critical hint\n }\n }\n\n if (layer6.trim()) {\n volatile.push(\n tagBlock(\n { type: 'text', text: layer6, cache_control: { type: 'ephemeral' } },\n 'plan',\n ),\n );\n }\n\n // System prompt contributors \u2014 plugins inject ephemeral context here.\n if (this.opts.contributors && this.opts.contributors.length > 0) {\n for (const c of this.opts.contributors) {\n try {\n const contributed = await c(ctx);\n for (const b of contributed) tagBlock(b, 'contributor');\n volatile.push(...contributed);\n } catch {\n // Contributor errors are swallowed \u2014 a bad plugin shouldn't\n // break the system prompt assembly.\n }\n }\n }\n\n // Leader-only after-task affordances (the `<nextsteps>` block + post-task\n // mailbox update). Host-only and appended last: subagents are headless\n // workers whose output is parsed (SDD spec/plan/task JSON) or rolled up by\n // the parent, so a `<nextsteps>` tag there is just noise that leaks into\n // specs/plans. Lives outside layer1 so the host keeps it in EVERY mode while\n // no subagent ever receives it.\n if (!ctx.subagent) {\n session.push(\n tagBlock(\n {\n type: 'text',\n text: instructions.system?.leaderAfterTask ?? LEADER_AFTER_TASK_PROMPT,\n },\n 'leader-after-task',\n ),\n );\n }\n\n return { core, session, volatile };\n }\n\n private async instructions(): Promise<InstructionBundle> {\n if (!this._instructionBundle) {\n this._instructionBundle = loadInstructionBundle(this.opts.instructionPaths).then((bundle) =>\n this.opts.instructionBundle\n ? mergeInstructionBundle(bundle, this.opts.instructionBundle)\n : bundle,\n );\n }\n return this._instructionBundle;\n }\n\n private instructionSection(\n bundle: InstructionBundle,\n key: string,\n vars: Record<string, string | number> = {},\n ): string {\n const template = bundle.sections?.[key];\n if (!template) return '';\n return template.replace(/\\{\\{\\s*([a-zA-Z0-9_.-]+)\\s*\\}\\}/g, (match, name: string) => {\n const value = vars[name];\n return value === undefined ? match : String(value);\n });\n }\n\n /**\n * Cached plan content keyed by (planPath, mtimeMs). The plan is read\n * once per system-prompt build; most turns don't change the plan, so\n * this avoids a blocking fs.readFile + JSON.parse on every iteration.\n * Cleared when the file's mtime changes (the `/plan` tool mutated it).\n */\n private _planCache?: { path: string; mtimeMs: number; text: string } | undefined;\n\n /**\n * Reads the session-scoped plan sidecar (when configured) and produces\n * a short \"Active plan\" block listing open items so the model is\n * anchored to the strategic roadmap every turn. Reads on every `build()`\n * so a plan edit (via `/plan` or the `plan` tool) reflects on the next\n * turn without restarting the session.\n */\n private async buildActivePlan(): Promise<string> {\n const planPath =\n typeof this.opts.planPath === 'function' ? this.opts.planPath() : this.opts.planPath;\n if (!planPath) return '';\n\n let raw: string;\n try {\n // Check mtime before reading \u2014 plans change at human pace (a few times\n // per session), not on every iteration. Stat is O(1) metadata; readFile\n // + JSON.parse is O(n) for the file content.\n const stat = await fs.stat(planPath);\n if (\n this._planCache &&\n this._planCache.path === planPath &&\n this._planCache.mtimeMs === stat.mtimeMs\n ) {\n return this._planCache.text;\n }\n raw = await fs.readFile(planPath, 'utf8');\n const text = this._formatPlan(raw);\n this._planCache = { path: planPath, mtimeMs: stat.mtimeMs, text };\n return text;\n } catch {\n // File missing, unreadable, or corrupt \u2014 clear cache and return empty.\n this._planCache = undefined;\n return '';\n }\n }\n\n private _formatPlan(raw: string): string {\n let parsed: {\n items?: Array<{ status?: string | undefined; title?: string | undefined }>;\n title?: string | undefined;\n };\n try {\n parsed = JSON.parse(raw);\n } catch {\n return '';\n }\n if (!Array.isArray(parsed.items) || parsed.items.length === 0) return '';\n const open = parsed.items.filter((i) => i?.status !== 'done');\n if (open.length === 0) return '';\n const lines = ['## Active plan'];\n if (parsed.title) lines.push(`*${parsed.title}*`, '');\n parsed.items.forEach((it, idx) => {\n const mark = it?.status === 'done' ? '[x]' : it?.status === 'in_progress' ? '[~]' : '[ ]';\n lines.push(`${idx + 1}. ${mark} ${it?.title ?? '(untitled)'}`);\n });\n lines.push(\n '',\n 'Use `/plan` (user) or the `plan` tool to update status as you progress. The roadmap survives session resume.',\n );\n return lines.join('\\n');\n }\n\n private async buildToolUsage(tools: Tool[], ctx: BuildContext): Promise<string> {\n if (tools.length === 0) return '## Tool usage\\n\\nNo tools registered.';\n const instructions = await this.instructions();\n\n // Cache: tools array is stable (same reference) until a registry mutation\n // thanks to B2 (ToolRegistry snapshot). Online agents are keyed by content\n // fingerprint \u2014 the mailbox rebuilds the array on every status check, so\n // reference equality would always miss. When all three keys match the\n // previous build, the full output is identical \u2014 return the cached\n // string. Including `tier` in the key ensures that mutating\n // `opts.tokenSavingMode` between builds (rare but supported via the\n // `private readonly opts` design) recomputes the prompt with the\n // new tier's truncation limits and tier-gated content.\n const agentsHash = this.agentsFingerprint(ctx.onlineAgents);\n const tier = this.tier;\n if (\n this._toolsUsageCache?.toolsRef === tools &&\n this._toolsUsageCache?.agentsHash === agentsHash &&\n this._toolsUsageCache?.tier === tier\n ) {\n return this._toolsUsageCache.text;\n }\n\n // Group tools by category for a cleaner listing when categories are used.\n const byCat = new Map<string, Tool[]>();\n const uncategorized: Tool[] = [];\n for (const t of tools) {\n if (t.category) {\n let group = byCat.get(t.category);\n if (!group) {\n group = [];\n byCat.set(t.category, group);\n }\n group.push(t);\n } else {\n uncategorized.push(t);\n }\n }\n\n const lines = ['## Tool usage'];\n const descLimit = this.toolDescLimit();\n\n // Categorized tools\n for (const [cat, catTools] of byCat) {\n lines.push(`\\n### ${cat}`);\n for (const t of catTools) {\n const hint = t.usageHint ?? t.description;\n // Trim to the tier-specific limit, preferring sentence boundaries.\n const desc =\n hint.length > descLimit\n ? hint.slice(0, hint.indexOf('.', 20) + 1 || descLimit) +\n (hint.length > descLimit ? '\u2026' : '')\n : hint.trim();\n lines.push(`- **${t.name}** \u2014 ${desc}`);\n const boundary = this.renderToolSelectionBoundary(t);\n if (boundary) lines.push(` ${boundary}`);\n }\n }\n\n // Uncategorized tools\n if (uncategorized.length > 0) {\n if (byCat.size > 0) lines.push('');\n for (const t of uncategorized) {\n const hint = t.usageHint ?? t.description;\n lines.push(`\\n### ${t.name}\\n${hint.trim()}`);\n const boundary = this.renderToolSelectionBoundary(t);\n if (boundary) lines.push(boundary);\n }\n }\n\n // Common tool chain patterns \u2014 teaches model how to compose tools effectively.\n // Skipped in minimal and aggressive tiers \u2014 model already knows these patterns\n // and aggressive users are under context pressure.\n if (this.tier !== 'minimal' && this.tier !== 'aggressive') {\n const commonPatterns = this.instructionSection(instructions, 'tool.common.patterns');\n if (commonPatterns) lines.push(commonPatterns);\n }\n\n // Delegation guidance \u2014 included when the `delegate` tool is present.\n // Without this block the model doesn't know that multi-agent work is\n // even an option, and `delegate` sits unused while the host agent\n // tries to do everything in one expensive context.\n // Tier behaviour:\n // - 'off' / 'medium' / 'aggressive' \u2192 full block\n // - 'light' \u2192 minimal one-liner\n // - 'minimal' \u2192 skipped\n const hasDelegate = tools.some((t) => t.name === 'delegate');\n if (hasDelegate) {\n const delegateTool = tools.find((t) => t.name === 'delegate');\n const enumValues = (() => {\n const role = (\n delegateTool?.inputSchema as\n | { properties?: { role?: { enum?: unknown | undefined } } }\n | undefined\n )?.properties?.role?.enum;\n return Array.isArray(role) ? (role.filter((r) => typeof r === 'string') as string[]) : [];\n })();\n const roleList = enumValues.length > 0 ? enumValues.join(', ') : '(no roster configured)';\n if (this.tier === 'minimal') {\n // Skip \u2014 don't emit any delegation guidance\n } else if (this.tier === 'light' || this.tier === 'medium' || this.tier === 'aggressive') {\n // Token-saving tiers get the compact one-liner instead of the full\n // multi-paragraph guidance. `aggressive` joins the compact group \u2014\n // a user under context pressure doesn't need a 600-token essay on\n // subagent scoping.\n const delegation = this.instructionSection(instructions, 'tool.delegation.compact', {\n roleList,\n });\n if (delegation) lines.push(delegation);\n } else {\n const delegation = this.instructionSection(instructions, 'tool.delegation.full', {\n roleList,\n });\n if (delegation) lines.push(delegation);\n }\n }\n\n // Mailbox guidance \u2014 included when any mailbox tool is present.\n // Tier behaviour:\n // - 'off' \u2192 full block\n // - every token-saving tier \u2192 compact project-wide contract\n //\n // Note: 'aggressive' was previously listed with 'off' for the\n // full block, but per the parallel-session decision (Option H,\n // `leader@1b68eb14`): at aggressive, the 400-token mailbox essay\n // is the largest single guidance section and users under context\n // pressure don't need it. The compact one-liner is enough.\n const hasMailbox = tools.some(\n (t) => t.name === 'mailbox' || t.name === 'mail_send' || t.name === 'mail_inbox',\n );\n if (hasMailbox) {\n // Build online-agent info \u2014 cached by a rendered-field fingerprint so\n // joins/leaves and live status/task/tool changes invalidate it.\n const onlineAgentsInfo = this.renderOnlineAgents(ctx.onlineAgents);\n const hasMailboxPowerTool = tools.some((t) => t.name === 'mailbox');\n const mailStatusCommand = tools.some((t) => t.name === 'fleet_status')\n ? '`fleet_status`'\n : hasMailboxPowerTool\n ? '`mailbox action=status` or `mailbox action=online`'\n : 'the online-agent list above';\n const mailInboxCommand = tools.some((t) => t.name === 'mail_inbox')\n ? '`mail_inbox`'\n : '`mailbox action=check`';\n const mailSendCommand = tools.some((t) => t.name === 'mail_send')\n ? '`mail_send`'\n : '`mailbox action=send`';\n const mailboxVars = {\n onlineAgentsInfo,\n mailStatusCommand,\n mailInboxCommand,\n mailSendCommand,\n };\n if (this.tier !== 'off') {\n // Minimal: keep just the header and agent count.\n // `aggressive` joins `light`/`medium` \u2014 the 400-token mailbox essay\n // is the largest single guidance section; users under context pressure\n // don't need it.\n const mailbox = this.instructionSection(\n instructions,\n 'tool.mailbox.compact',\n mailboxVars,\n );\n if (mailbox) lines.push(mailbox);\n } else {\n const mailbox = this.instructionSection(instructions, 'tool.mailbox.full', mailboxVars);\n if (mailbox) lines.push(mailbox);\n }\n }\n\n // Commit hygiene \u2014 shown whenever the structured `git` tool is available.\n // Other agents (or a separate wrongstack process, or a human) may be\n // editing the SAME working tree at the same time; a blanket commit captures\n // their half-done work and there is no clean way to undo a shared commit.\n const hasGitTool = tools.some((t) => t.name === 'git');\n if (hasGitTool && this.tier !== 'minimal' && this.tier !== 'light') {\n const commitHygiene = this.instructionSection(instructions, 'tool.commit.hygiene');\n if (commitHygiene) lines.push(commitHygiene);\n }\n\n // MCP lazy-loading guidance \u2014 shown whenever mcp_control is registered.\n // Tier behaviour:\n // - 'off' / 'medium' \u2192 full guidance block\n // - 'minimal' / 'light' / 'aggressive' \u2192 minimal one-liner\n //\n // Note: 'aggressive' was previously listed with 'off' for the\n // full block, but per the parallel-session decision (Option H):\n // at aggressive, the full MCP workflow (activate \u2192 use \u2192\n // deactivate) is documented elsewhere and the meta-tool\n // `mcp_use` is sufficient. The one-liner is enough.\n const hasMcpControl = tools.some((t) => t.name === 'mcp_control');\n const hasMcpUse = tools.some((t) => t.name === 'mcp_use');\n if (hasMcpControl) {\n if (this.tier === 'minimal' || this.tier === 'light' || this.tier === 'aggressive') {\n // Minimal one-liner \u2014 `aggressive` joins `minimal`/`light`. The full\n // MCP workflow (activate \u2192 use \u2192 deactivate) is documented elsewhere\n // and the meta-tool `mcp_use` is sufficient at any tier that has it.\n const mcp = this.instructionSection(\n instructions,\n hasMcpUse ? 'tool.mcp.compact.use' : 'tool.mcp.compact.control',\n );\n if (mcp) lines.push(mcp);\n } else {\n // Full block\n const mcp = this.instructionSection(\n instructions,\n hasMcpUse ? 'tool.mcp.full.use' : 'tool.mcp.full.control',\n );\n if (mcp) lines.push(mcp);\n }\n }\n\n // Context management guidance \u2014 shown when context_manager is registered.\n // Tier behaviour:\n // - 'off' / 'aggressive' \u2192 full block\n // - 'medium' \u2192 minimal one-liner\n // - 'minimal' / 'light' \u2192 skipped\n const hasContextManager = tools.some((t) => t.name === 'context_manager');\n if (hasContextManager) {\n if (this.tier === 'minimal' || this.tier === 'light') {\n // Skip\n } else if (this.tier === 'medium') {\n const contextManagement = this.instructionSection(\n instructions,\n 'tool.context.management.compact',\n );\n if (contextManagement) lines.push(contextManagement);\n } else {\n // Adaptive threshold based on model context window size.\n // Small context (<=32k) \u2192 trigger earlier; large context (>32k) \u2192 more relaxed.\n // Fallback to 0 when unknown \u2192 conservative compaction (50 % threshold).\n const maxCtx = this.modelCapabilities()?.maxContextTokens ?? 0;\n const threshold = maxCtx <= 32000 ? '50' : '70';\n const contextManagement = this.instructionSection(\n instructions,\n 'tool.context.management.full',\n { threshold },\n );\n if (contextManagement) lines.push(contextManagement);\n }\n }\n\n // Store cache \u2014 keyed by tools reference (B2 snapshot) + agents content\n // fingerprint + tier, so it auto-invalidates when tools change, agents\n // join/leave, or the token-saving tier changes.\n const text = lines.join('\\n');\n this._toolsUsageCache = { toolsRef: tools, agentsHash, tier, text };\n return text;\n }\n\n private renderToolSelectionBoundary(tool: Tool): string {\n const selection = tool.selection;\n if (!selection?.doNotUseWhen.trim()) return '';\n const alternatives = selection.useInstead?.filter(Boolean) ?? [];\n const instead = alternatives.length > 0 ? ` Use ${alternatives.map((name) => `\\`${name}\\``).join(' or ')} instead.` : '';\n return `Do not use when ${selection.doNotUseWhen.trim()}${instead}`;\n }\n\n /**\n * Cheap content fingerprint of the online agents array. The mailbox\n * rebuilds the array as a fresh object on every status check, so caching\n * by reference always misses \u2014 this lets the renderOnlineAgents and\n * buildToolUsage caches detect rendered identity/status/task/tool changes\n * instead.\n *\n * O(n) over every field rendered in the peer snapshot. This matters because\n * status/task/tool changes should invalidate the prompt just like joins and\n * leaves do. Uses FNV-1a over character codes; a collision would only leave a\n * stale cosmetic snapshot in the prompt.\n */\n private agentsFingerprint(agents: readonly MailboxAgentStatus[] | undefined): string {\n if (!agents || agents.length === 0) return '0';\n let h = 0x811c9dc5;\n for (const a of agents) {\n const fields = [\n a.agentId,\n a.name,\n a.source,\n a.sessionId,\n a.status,\n a.currentTask,\n a.currentTool,\n a.online ? '1' : '0',\n ];\n for (const field of fields) {\n const value = field ?? '';\n for (let i = 0; i < value.length; i++) {\n h ^= value.charCodeAt(i);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n h ^= 0xff;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n }\n return `${agents.length}:${h.toString(36)}`;\n }\n\n /**\n * Render the online agents list, cached by content fingerprint. The agents\n * list changes at join/leave pace (seconds to minutes), not every prompt\n * build turn (hundreds of ms). The fingerprint detects membership changes\n * without holding the array reference \u2014 the mailbox rebuilds the array as\n * a fresh object on every status check, so reference equality always misses.\n *\n * Tier behaviour:\n * - 'off' / 'medium' / 'aggressive' \u2192 full list with names, sessions, sources\n * - 'minimal' / 'light' \u2192 count only (no list)\n */\n private renderOnlineAgents(agents: readonly MailboxAgentStatus[] | undefined): string {\n if (!agents || agents.length === 0) return '';\n\n // Content fingerprint: detects membership changes without holding the\n // array reference, which is rebuilt as a fresh object on every status check.\n const hash = this.agentsFingerprint(agents);\n if (this._lastOnlineAgents?.hash === hash) {\n return this._lastOnlineAgents.text;\n }\n\n const totalCount = agents.length;\n // minimal / light tiers: count only, no list\n if (this.tier === 'minimal' || this.tier === 'light') {\n const text = ` (${totalCount} agent${totalCount !== 1 ? 's' : ''} online)`;\n this._lastOnlineAgents = { hash, text };\n return text;\n }\n\n const inlineData = (value: string, max = 120): string =>\n value.replace(/[`\\r\\n]+/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, max);\n const agentList = agents\n .map((a) => {\n const details = [\n `id: \\`${inlineData(a.agentId ?? a.name, 96)}\\``,\n `client: ${inlineData(a.source ?? 'unknown', 32)}`,\n a.status ? `status: ${inlineData(a.status, 32)}` : undefined,\n a.currentTask ? `task: \\`${inlineData(a.currentTask)}\\`` : undefined,\n a.currentTool ? `tool: \\`${inlineData(a.currentTool, 64)}\\`` : undefined,\n a.sessionId ? `session: ${shortSessionId(inlineData(a.sessionId, 96))}` : undefined,\n ].filter((part): part is string => part !== undefined);\n return `- **${inlineData(a.name, 96)}** \u2014 ${details.join('; ')}`;\n })\n .join('\\n');\n const text = `\\n\\n**Currently online (${totalCount} agent${totalCount !== 1 ? 's' : ''}):**\\n${agentList}`;\n this._lastOnlineAgents = { hash, text };\n return text;\n }\n\n private async buildEnvironment(ctx: BuildContext): Promise<string> {\n const modelCapabilities = this.modelCapabilities();\n const cacheKey = [\n ctx.projectRoot,\n ctx.provider ?? '',\n ctx.model ?? '',\n modelCapabilities?.maxContextTokens ?? 0,\n modelCapabilities?.supportsTools ? 1 : 0,\n modelCapabilities?.supportsVision ? 1 : 0,\n modelCapabilities?.supportsReasoning ? 1 : 0,\n ].join('\\0');\n const cached = this.envCacheByRoot.get(cacheKey);\n if (cached) return cached;\n const today = this.opts.todayIso ?? new Date().toISOString().slice(0, 10);\n const platform = `${os.platform()} ${os.release()}`;\n // The bash tool's effective shell, pinned at boot via WRONGSTACK_SHELL.\n // On POSIX we keep reporting the raw $SHELL; on Windows we report the\n // resolved shell + a \"write X syntax\" nudge, and append a syntax guidance\n // sub-block below so the model doesn't default to bash/POSIX idioms.\n const effShell = effectiveShell(os.platform(), process.env['WRONGSTACK_SHELL']);\n const shell =\n effShell === 'posix'\n ? (process.env.SHELL ?? process.env.ComSpec ?? 'unknown')\n : SHELL_DISPLAY[effShell];\n const node = process.version;\n const isGit = await this.dirExists(path.join(ctx.projectRoot, '.git'));\n // Fan out the per-root probes so the prompt build doesn't serialize\n // ~12 fs.access calls plus the git status spawn back-to-back. On a\n // cold cache (CI / first turn) this trims hundreds of ms.\n const [git, langs] = await Promise.all([\n isGit ? this.gitStatus(ctx.projectRoot) : Promise.resolve('not a git repo'),\n this.detectLanguages(ctx.projectRoot),\n ]);\n\n // Tier-aware environment block content.\n // - 'off': Full \u2014 all fields\n // - 'minimal': Compact single line \u2014 git + date only\n // - 'light': +platform\n // - 'medium': +languages\n // - 'aggressive': +capabilities (context window, provider/model)\n const tier = this.tier;\n const lines: string[] = ['## Environment'];\n\n if (tier === 'minimal') {\n // Single compact line\n lines.push(`- Git: ${git} | Date: ${today}`);\n } else {\n lines.push(`- Operating system: ${platform}`);\n if (tier !== 'light') {\n lines.push(`- Shell: ${shell}`);\n lines.push(`- Node.js: ${node}`);\n }\n // Languages appear in the full ('off') block and the richer trimming\n // tiers; only 'minimal' (single line) and 'light' (platform only) omit\n // them. 'off' is the most complete tier (no token saving), per the\n // toolDescLimit ordering off=80 > aggressive=70 > \u2026 > minimal=40.\n if (tier === 'off' || tier === 'medium' || tier === 'aggressive') {\n lines.push(`- Detected languages: ${langs}`);\n }\n lines.push(`- Git status: ${git}`);\n lines.push(`- Today's date: ${today}`);\n if (tier === 'aggressive') {\n if (ctx.provider || ctx.model) {\n lines.push(\n `- Running on: ${ctx.provider ?? '<unknown provider>'}/${ctx.model ?? '<unknown model>'}`,\n );\n }\n if (modelCapabilities) {\n lines.push(\n `- Context window: ${modelCapabilities.maxContextTokens.toLocaleString()} tokens max`,\n );\n }\n }\n if (tier !== 'aggressive' && modelCapabilities) {\n lines.push(\n `- Context window: ${modelCapabilities.maxContextTokens.toLocaleString()} tokens max`,\n );\n }\n if (tier !== 'aggressive' && (ctx.provider || ctx.model)) {\n lines.push(\n `- Running on: ${ctx.provider ?? '<unknown provider>'}/${ctx.model ?? '<unknown model>'}`,\n );\n }\n if (tier !== 'aggressive' && this.opts.modeId && this.opts.modeId !== 'default') {\n lines.push(`- Mode: ${this.opts.modeId}`);\n }\n }\n\n // Shell syntax guidance \u2014 only meaningful on Windows, where the model must\n // not fall back to bash/POSIX idioms. Tier-gated: full for off/medium/\n // aggressive, a one-liner for light, omitted for minimal. POSIX returns ''.\n if (effShell !== 'posix' && tier !== 'minimal') {\n const guide = shellGuidanceBlock(effShell, tier === 'light' ? 'short' : 'full');\n if (guide) lines.push('', guide);\n }\n\n if (this.skillCache) {\n lines.push(\n '',\n '## Skills in scope for this session',\n this.skillCache,\n '',\n this.opts.skillMode === 'progressive'\n ? 'Skill names and triggers are injected below; load full instructions deterministically with the `skill` tool before relying on one.'\n : this.isCompact\n ? 'Compact skill instructions are injected in the Active Skills block below (Overview + Rules only).'\n : 'Skill bodies are injected below up to the eager budget; overflow remains listed by name and trigger for deterministic loading with the `skill` tool.',\n );\n }\n const text = lines.join('\\n');\n this.envCacheByRoot.set(cacheKey, text);\n return text;\n }\n\n private modelCapabilities(): ModelCapabilities | undefined {\n const caps = this.opts.modelCapabilities;\n return typeof caps === 'function' ? caps() : caps;\n }\n\n private async buildMemoryAndSkills(): Promise<string> {\n const parts: string[] = [];\n // Memory injection count per tier: off=5, minimal=3, light=5, medium=5, aggressive=5\n const memoryCount = this.tier === 'minimal' || this.tier === 'light' ? 3 : 5;\n const compactMemory = this.tier === 'minimal'; // compact = text only, no badges/tags\n // When a per-turn memory retriever owns injection (Super Memory turn\n // middleware), skip the static prompt section so memory flows through a\n // single channel \u2014 no double injection.\n if (this.opts.memoryStore && this.opts.injectMemory !== false) {\n try {\n // Use relevance scoring when available, fall back to full dump.\n if (this.opts.memoryStore.scoreRelevant) {\n const toolNames = this._lastBuildTools?.map((t) => t.name) ?? [];\n const scored = await this.opts.memoryStore.scoreRelevant(\n {\n currentTask: '',\n toolNames,\n },\n 'project-memory',\n memoryCount,\n );\n if (scored.length > 0) {\n const lines: string[] = ['# Relevant Memory'];\n for (const e of scored) {\n if (compactMemory) {\n lines.push(`- ${e.text}`);\n } else {\n const badge = e.type ? `[\\`${e.type.replace('_', '-')}\\`] ` : '';\n const priorityMark =\n e.priority === 'critical' ? '\u26A1' : e.priority === 'high' ? '\u25B2' : '';\n lines.push(\n `- ${priorityMark}${badge}${e.text}${e.tags ? ` \\`#${e.tags.join(' #')}\\`` : ''}`,\n );\n }\n }\n parts.push(lines.join('\\n'));\n }\n } else {\n const mem = await this.opts.memoryStore.readAll();\n if (mem.trim()) parts.push(`# Project Memory\\n\\n${mem}`);\n }\n } catch {\n // skip\n }\n }\n // Skill bodies \u2014 load once and cache for the session lifetime.\n // Skills are listed by name+trigger in buildEnvironment (envCache);\n // here we inject the full body content so the model has the actual\n // domain instructions, not just a trigger hint.\n // In token-saving mode, skill bodies are compacted to save tokens:\n // only the Overview and Rules sections (~400 chars max per skill).\n if (this.opts.skillLoader) {\n if (this.opts.skillMode === 'progressive') {\n // Progressive disclosure \u2014 only the metadata manifest is injected; the\n // agent loads full bodies on demand via the `skill` tool.\n if (this.skillBodyCache === undefined) {\n await this.buildProgressiveSkillManifest();\n }\n } else if (this.isCompact) {\n // Compact mode \u2014 build once, cache\n if (this.skillBodyCache === undefined) {\n await this.buildCompactSkillBodies();\n }\n } else {\n // Full mode \u2014 build once, cache\n if (this.skillBodyCache === undefined) {\n await this.buildFullSkillBodies();\n }\n }\n }\n if (this.skillBodyCache) {\n parts.push(`# Active Skills\\n\\n${this.skillBodyCache}`);\n }\n return parts.join('\\n\\n');\n }\n\n /**\n * Build the progressive-disclosure manifest: list each skill's name + trigger\n * only and instruct the agent to call the `skill` tool to load a body. No\n * bodies are injected \u2014 the agent pulls them on demand (agentskills.io tier 2).\n */\n private async buildProgressiveSkillManifest(): Promise<void> {\n if (!this.opts.skillLoader) {\n this.skillBodyCache = '';\n return;\n }\n try {\n const entries = await this.opts.skillLoader.listEntries();\n if (entries.length === 0) {\n this.skillBodyCache = '';\n return;\n }\n const lines = [\n 'Call the `skill` tool to load a skill before relying on it.',\n '',\n '| Skill | Use when |',\n '|---|---|',\n ];\n for (const e of entries) {\n const trigger = (e.trigger ?? '').replace(/\\|/g, '\\\\|').replace(/\\n+/g, ' ').trim();\n lines.push(`| \\`${e.name}\\` | ${trigger} |`);\n }\n this.skillBodyCache = lines.join('\\n');\n } catch {\n this.skillBodyCache = '';\n }\n }\n\n /** Build full skill bodies (token-saving OFF), bounded by an overall budget. */\n private async buildFullSkillBodies(): Promise<void> {\n try {\n const skills = await this.opts.skillLoader!.list();\n if (skills.length === 0) {\n this.skillBodyCache = '';\n return;\n }\n // Overall budget: the loader returns skills highest-priority first, so the\n // most relevant (project, then user) skills get a full body; the rest are\n // listed as a manifest the agent loads on demand via the `skill` tool.\n // Without this, discovering many skills (foreign agents add a lot) would\n // bloat every prompt with every skill body.\n const budget = this.opts.skillEagerMaxChars ?? SKILL_LIMITS.EAGER_DEFAULT_MAX_CHARS;\n const bodies: string[] = [];\n const overflow: string[] = [];\n let used = 0;\n for (const s of skills) {\n try {\n const raw = await this.opts.skillLoader!.readBody(s.name);\n const trimmed = stripFrontmatter(raw).trim();\n if (!trimmed) continue;\n // Per-skill cap (I5 audit): a misconfigured multi-MB file can't bloat.\n const entry = `## Skill: ${s.name}\\n\\n${capSkillBody(trimmed)}`;\n if (used + entry.length <= budget) {\n bodies.push(entry);\n used += entry.length;\n } else {\n overflow.push(`- ${s.name}`);\n }\n } catch {\n // skip unreadable skill\n }\n }\n let out = bodies.join('\\n\\n---\\n\\n');\n if (overflow.length > 0) {\n const note =\n overflow.length === skills.length\n ? '## Available skills (load with the `skill` tool)'\n : '## Other available skills (not injected \u2014 load with the `skill` tool)';\n out += `${out ? '\\n\\n---\\n\\n' : ''}${note}\\n${overflow.join('\\n')}`;\n }\n this.skillBodyCache = out;\n } catch {\n this.skillBodyCache = '';\n }\n }\n\n /**\n * Build compact skill bodies for token-saving mode.\n * Uses `readSaveBody` from the skill loader which tries `SKILL.save.md`\n * first, then falls back to auto-compaction.\n */\n private async buildCompactSkillBodies(): Promise<void> {\n if (!this.opts.skillLoader) {\n this.skillBodyCache = '';\n return;\n }\n try {\n const skills = await this.opts.skillLoader.list();\n if (skills.length > 0) {\n const bodies: string[] = [];\n for (const s of skills) {\n try {\n const saveBody = await this.opts.skillLoader.readSaveBody(s.name);\n const clean = stripFrontmatter(saveBody);\n if (clean.trim()) {\n bodies.push(`## Skill: ${s.name}\\n\\n${clean.trim()}`);\n }\n } catch {\n // skip unreadable skill\n }\n }\n this.skillBodyCache = bodies.length > 0 ? bodies.join('\\n\\n---\\n\\n') : '';\n } else {\n this.skillBodyCache = '';\n }\n } catch {\n this.skillBodyCache = '';\n }\n }\n\n private async buildMode(): Promise<string> {\n // Use pre-resolved modePrompt if available (avoids redundant async call).\n if (this.opts.modePrompt) return this.opts.modePrompt;\n if (!this.opts.modeStore) return '';\n const mode = await this.opts.modeStore.getActiveMode();\n if (!mode?.prompt) return '';\n return mode.prompt;\n }\n\n private async dirExists(p: string): Promise<boolean> {\n try {\n const stat = await fs.stat(p);\n return stat.isDirectory();\n } catch {\n return false;\n }\n }\n\n private async gitStatus(root: string): Promise<string> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (s: string): void => {\n if (settled) return;\n settled = true;\n resolve(s);\n };\n let proc: ReturnType<typeof spawn> | undefined;\n // 2 s ceiling: a hung git status (corrupt index, .git/index.lock\n // held by another process, network FS hiccup) must not stall the\n // whole prompt build for a turn.\n const timer = setTimeout(() => {\n proc?.kill('SIGKILL');\n finish('git timeout');\n }, 2000);\n try {\n proc = spawn('git', ['status', '--porcelain=v1', '--branch'], {\n cwd: root,\n env: buildChildEnv(),\n stdio: ['ignore', 'pipe', 'ignore'],\n windowsHide: true,\n });\n let buf = '';\n proc.stdout?.on('data', (c) => {\n buf += c.toString();\n });\n proc.on('error', () => {\n clearTimeout(timer);\n finish('git error');\n });\n proc.on('close', () => {\n clearTimeout(timer);\n const lines = buf.split('\\n').filter(Boolean);\n const branchLine = lines[0] ?? '';\n const branchMatch = branchLine.match(/## ([^\\s.]+)/);\n const branch = branchMatch?.[1] ?? 'detached';\n const dirty = lines.slice(1);\n const staged = dirty.filter((l) => /^[MARCD]/.test(l)).length;\n const modified = dirty.length - staged;\n finish(`branch=${branch}, ${modified} modified, ${staged} staged`);\n });\n } catch {\n clearTimeout(timer);\n finish('git unavailable');\n }\n });\n }\n\n private async detectLanguages(root: string): Promise<string> {\n const checks: Array<[string, string]> = [\n ['package.json', 'JavaScript/TypeScript'],\n ['tsconfig.json', 'TypeScript'],\n ['go.mod', 'Go'],\n ['Cargo.toml', 'Rust'],\n ['pyproject.toml', 'Python'],\n ['requirements.txt', 'Python'],\n ['Gemfile', 'Ruby'],\n ['pom.xml', 'Java'],\n ['build.gradle', 'Java/Kotlin'],\n ['composer.json', 'PHP'],\n ['mix.exs', 'Elixir'],\n ];\n // Fan out the marker probes. Sequential await on 11 fs.access calls\n // adds latency on cold cache for no reason \u2014 each probe is independent.\n const hits = await Promise.all(\n checks.map(async ([marker, lang]) => {\n try {\n await fs.access(path.join(root, marker));\n return lang;\n } catch {\n return null;\n }\n }),\n );\n const langs = new Set(hits.filter((l): l is string => l !== null));\n return langs.size === 0 ? 'unknown' : Array.from(langs).join(', ');\n }\n}\n\n/** Strip YAML frontmatter from a SKILL.md file, returning only the body. */\nfunction stripFrontmatter(raw: string): string {\n if (!raw.startsWith('---')) return raw;\n const end = raw.indexOf('\\n---', 4);\n if (end === -1) return raw;\n // Skip past the closing `---` and the following newline\n let body = raw.slice(end + 4);\n if (body.startsWith('\\n')) body = body.slice(1);\n return body;\n}\n\n/**\n * Maximum number of characters of a skill body to inject into the\n * prompt when building the full (token-saving OFF) Active Skills block.\n *\n * Real-world SKILL.md files are <5 KB; 16 KB is generous headroom.\n * Without a cap, a misconfigured multi-MB skill file can bloat the\n * prompt by tens of thousands of tokens. This cap matches the\n * bash `MAX_OUTPUT` (32 KB) at a smaller scale and keeps the full\n * path comparable in size to the compact path's natural output.\n *\n * I5 audit (Sprint 3). See\n * `packages/core/tests/core/system-prompt-builder-i-skills.test.ts`.\n */\n/**\n * Cap a skill body at `SKILL_LIMITS.MAX_SKILL_BODY_CHARS`, truncating at a\n * paragraph boundary when possible to preserve readability. Appends an\n * ellipsis marker when truncated so the model can detect the cap.\n */\nfunction capSkillBody(body: string): string {\n const max = SKILL_LIMITS.MAX_SKILL_BODY_CHARS;\n if (body.length <= max) return body;\n // Try to cut at the last paragraph break (`\\n\\n`) within the\n // budget so the truncated body ends cleanly. Fall back to a\n // hard cut if no paragraph break exists.\n const budget = max - 1; // reserve 1 char for ellipsis\n const cut = body.lastIndexOf('\\n\\n', budget);\n const truncated = cut > budget / 2 ? body.slice(0, cut) : body.slice(0, budget);\n return truncated + '\u2026';\n}\n\n/**\n * Compact a skill trigger description into a short label.\n * \"Use this skill when scanning source code for bugs...\"\n * \u2192 \"scanning source code for bugs, anti-patterns, code smells\"\n */\nfunction compactTrigger(trigger: string): string {\n // Strip common prefixes\n let s = trigger\n .replace(/^Use this skill when /i, '')\n .replace(/^Use this skill for /i, '')\n .replace(/^Use when /i, '')\n .replace(/\\.$/, '');\n // Truncate to ~72 chars at a word boundary\n if (s.length > 72) {\n const cut = s.lastIndexOf(' ', 68);\n s = cut > 50 ? s.slice(0, cut) + '\u2026' : s.slice(0, 68) + '\u2026';\n }\n return s;\n}\n", "import type { JSONSchema } from '../types/tool.js';\n\nexport interface ToolWireDefinitionLike {\n name: string;\n description?: string | undefined;\n inputSchema: unknown;\n}\n\nexport interface CompactToolDefinitionForWireOptions {\n /** Top-level tool description budget. */\n descriptionMaxChars?: number | undefined;\n /** Per-JSON-Schema `description` annotation budget. */\n schemaDescriptionMaxChars?: number | undefined;\n}\n\nexport interface CompactWireToolDefinition {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nconst TOOL_DESCRIPTION_MAX_CHARS = 400;\nconst SCHEMA_DESCRIPTION_MAX_CHARS = 120;\n\nconst compactCache = new WeakMap<object, CompactWireToolDefinition>();\n\n/**\n * Return the provider-wire version of a tool definition.\n *\n * Tool schemas remain structurally intact: validation keywords, property\n * names, required fields, enum values, and nested shapes are preserved. The\n * only reduction is on human prose annotations (`description`), which are the\n * largest repeated cost in provider tool declarations.\n */\nexport function compactToolDefinitionForWire(\n tool: ToolWireDefinitionLike,\n opts: CompactToolDefinitionForWireOptions = {},\n): CompactWireToolDefinition {\n const useDefaultOptions =\n opts.descriptionMaxChars === undefined && opts.schemaDescriptionMaxChars === undefined;\n if (useDefaultOptions && typeof tool === 'object' && tool !== null) {\n const cached = compactCache.get(tool);\n if (cached) return cached;\n }\n\n const compact: CompactWireToolDefinition = {\n name: tool.name,\n description: compactDescription(\n tool.description ?? '',\n opts.descriptionMaxChars ?? TOOL_DESCRIPTION_MAX_CHARS,\n ),\n inputSchema: normalizeTopLevelToolSchema(\n compactSchemaDescriptions(\n tool.inputSchema,\n opts.schemaDescriptionMaxChars ?? SCHEMA_DESCRIPTION_MAX_CHARS,\n ),\n ),\n };\n\n if (useDefaultOptions && typeof tool === 'object' && tool !== null) {\n compactCache.set(tool, compact);\n }\n return compact;\n}\n\n/**\n * Tool inputs are always JSON objects, but dynamically supplied MCP/plugin\n * schemas sometimes express that object as a top-level oneOf/anyOf/allOf.\n * Anthropic-family endpoints reject those combinators at the input_schema\n * root (including when reached through an OpenAI-compatible gateway such as\n * OmniRoute). Keep nested combinators intact and flatten only the root.\n * Runtime validation still uses the tool's original schema.\n */\nexport function normalizeTopLevelToolSchema(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n const combinators = (['oneOf', 'anyOf', 'allOf'] as const)\n .map((keyword) => ({ keyword, branches: schema[keyword] }))\n .filter((entry): entry is { keyword: 'oneOf' | 'anyOf' | 'allOf'; branches: unknown[] } =>\n Array.isArray(entry.branches),\n );\n if (combinators.length === 0) return schema;\n\n const out: Record<string, unknown> = { ...schema, type: 'object' };\n delete out['oneOf'];\n delete out['anyOf'];\n delete out['allOf'];\n\n const properties: Record<string, unknown> = isRecord(schema['properties'])\n ? { ...schema['properties'] }\n : {};\n let required = stringSet(schema['required']);\n\n for (const { keyword, branches } of combinators) {\n const objectBranches = branches.filter(isRecord);\n for (const branch of objectBranches) {\n if (isRecord(branch['properties'])) Object.assign(properties, branch['properties']);\n }\n\n const branchRequired = objectBranches.map((branch) => stringSet(branch['required']));\n if (keyword === 'allOf') {\n for (const fields of branchRequired) for (const field of fields) required.add(field);\n } else if (branchRequired.length > 0) {\n const common = new Set(\n [...branchRequired[0]!].filter((field) =>\n branchRequired.slice(1).every((fields) => fields.has(field)),\n ),\n );\n required = new Set([...required, ...common]);\n }\n }\n\n out['properties'] = properties;\n if (required.size > 0) out['required'] = [...required];\n else delete out['required'];\n return out;\n}\n\nfunction stringSet(value: unknown): Set<string> {\n return new Set(\n Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [],\n );\n}\n\nexport function compactSchemaDescriptions(\n schema: unknown,\n maxDescriptionChars = SCHEMA_DESCRIPTION_MAX_CHARS,\n): Record<string, unknown> {\n const compact = compactSchemaNode(schema, maxDescriptionChars);\n return isRecord(compact) ? compact : { type: 'object', properties: {} };\n}\n\nfunction compactSchemaNode(node: unknown, maxDescriptionChars: number): unknown {\n if (Array.isArray(node)) {\n return node.map((item) => compactSchemaNode(item, maxDescriptionChars));\n }\n if (!isRecord(node)) return node;\n\n const out: JSONSchema = {};\n for (const [key, value] of Object.entries(node)) {\n if (key === 'description' && typeof value === 'string') {\n out[key] = compactDescription(value, maxDescriptionChars);\n } else {\n out[key] = compactSchemaNode(value, maxDescriptionChars);\n }\n }\n return out;\n}\n\nexport function compactDescription(text: string, maxChars: number): string {\n const normalized = text.replace(/\\s+/g, ' ').trim();\n if (normalized.length <= maxChars) return normalized;\n if (maxChars <= 20) return normalized.slice(0, maxChars);\n\n const hardLimit = maxChars - 12;\n const boundary = findSemanticBoundary(normalized, hardLimit);\n const head = normalized.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd();\n return `${head} ...`;\n}\n\nexport function findSemanticBoundary(text: string, limit: number): number {\n const punctuation = Math.max(\n text.lastIndexOf('. ', limit),\n text.lastIndexOf('; ', limit),\n text.lastIndexOf(': ', limit),\n );\n if (punctuation >= Math.floor(limit * 0.45)) return punctuation + 1;\n\n const comma = text.lastIndexOf(', ', limit);\n if (comma >= Math.floor(limit * 0.6)) return comma + 1;\n\n const space = text.lastIndexOf(' ', limit);\n return space >= Math.floor(limit * 0.6) ? space : limit;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n", "import type { Message } from '../types/messages.js';\nimport { compactToolDefinitionForWire } from './tool-wire-compact.js';\n\n/**\n * Shared token estimation with JSON.stringify caching.\n * Avoids repeated stringification of tool input objects.\n *\n * ## Calibration\n *\n * `estimateRequestTokens` uses a fixed 3.5 chars/token heuristic \u2014 a\n * conservative overestimate that prevents underestimation but reduces\n * accuracy. After each API call, call `recordActualUsage()` with the\n * provider-authoritative effective prompt tokens (`input + cacheRead +\n * cacheWrite` after adapter normalization). The module maintains a rolling\n * average of `actual / estimated` ratio (EWM, \u03B1=0.3) and applies it to\n * subsequent calls via `estimateRequestTokensCalibrated`.\n *\n * Calibration is per-module (shared across all callers), which is\n * sufficient: the chars/token ratio is a property of the tokenizer,\n * not the model. Uncalibrated calls (before any samples, or when\n * `recordActualUsage` is not called) fall back to the uncalibrated\n * estimate so nothing breaks.\n */\n\nconst RoughTokenEstimate = (text: string, charsPerToken = 3.5): number =>\n Math.max(1, Math.ceil(text.length / charsPerToken));\n\n/** Calibration state: actual/estimated ratio via exponential weighted moving average. */\ninterface CalState {\n ratio: number; // current calibration multiplier (actual / estimated)\n count: number; // number of samples recorded\n prevEst: number; // estimated tokens from the most recent estimateRequestTokens call\n}\n\n/** EWM \u03B1 \u2014 higher = faster adaptation, more volatile. */\nconst CAL_ALPHA = 0.3;\n\n/**\n * Calibration is keyed so that, in a multi-agent / model-switching process,\n * each (provider, model) tokenizer gets its own ratio instead of all of them\n * collapsing onto one shared number. Callers that don't pass a key use the\n * shared `__global__` bucket \u2014 that preserves the original single-session\n * behavior and keeps all existing call sites working unchanged.\n */\nconst CALIBRATION_GLOBAL_KEY = '__global__';\nconst _cals = new Map<string, CalState>();\n\nfunction calState(key: string): CalState {\n let state = _cals.get(key);\n if (!state) {\n state = { ratio: 1.0, count: 0, prevEst: 0 };\n _cals.set(key, state);\n }\n return state;\n}\n\nconst MIN_SAMPLES_FOR_CALIBRATION = 3;\n\n/**\n * Fallback chars/token ratios per model family for providers that don't return\n * usage data. Used when `recordActualUsage` receives zero/negative tokens and\n * we have enough samples to trust the fallback. Keys are lowercase prefixes.\n */\nconst MODEL_FAMILY_RATIO: Record<string, number> = {\n // Anthropic: ~3.8-4.0 chars/token depending on model\n claude: 3.8,\n // OpenAI: ~4.0 chars/token\n 'gpt-4': 4.0,\n 'gpt-3.5': 4.0,\n // Google: ~3.5 chars/token\n gemini: 3.5,\n // DeepSeek: ~3.5 chars/token\n deepseek: 3.5,\n};\n\n/**\n * Cache of computed estimates keyed by the stringified input \u2014 not the\n * input object itself. Previously the cache was keyed by the input object\n * via WeakMap, but JSON.stringify() produces a new object reference each\n * call so the cache never hit. Now we use a Map with string keys so that\n * repeated stringifications of the same structure share a single entry.\n */\nconst ESTIMATE_CACHE = new Map<string, number>();\n/** Insertion-order queue for O(1) LRU eviction: shift from front on overcapacity. */\nconst _estimateCacheOrder: string[] = [];\n\nconst ESTIMATE_CACHE_MAX_SIZE = 50_000;\n\nfunction getCachedEstimate(key: string, compute: (key: string) => number): number {\n const existing = ESTIMATE_CACHE.get(key);\n if (existing !== undefined) return existing;\n if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {\n // Evict oldest half \u2014 O(1) per eviction (array shift + Map.delete) instead\n // of O(n) iteration over all 50 000 keys in the Map.\n while (ESTIMATE_CACHE.size > Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) {\n const oldest = _estimateCacheOrder.shift();\n if (oldest !== undefined) ESTIMATE_CACHE.delete(oldest);\n }\n }\n const estimate = compute(key);\n ESTIMATE_CACHE.set(key, estimate);\n _estimateCacheOrder.push(key);\n return estimate;\n}\n\n/**\n * Estimate tokens for a tool_use block input.\n * Caches the stringified result keyed by the stable string representation\n * to avoid repeated JSON.stringify calls during context window checks.\n */\nexport function estimateToolInputTokens(input: unknown): number {\n if (typeof input === 'string') return RoughTokenEstimate(input);\n if (input === null || typeof input !== 'object') {\n return RoughTokenEstimate(String(input));\n }\n // JSON.stringify is called once to form the cache key; RoughTokenEstimate\n // is deferred only on cache miss (compute callback), not wrapped unnecessarily.\n return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));\n}\n\n/**\n * Estimate tokens for a tool_result content.\n */\nexport function estimateToolResultTokens(content: string | unknown): number {\n if (typeof content === 'string') return RoughTokenEstimate(content);\n return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));\n}\n\n/**\n * Estimate tokens for a text block.\n */\nexport function estimateTextTokens(text: string): number {\n return RoughTokenEstimate(text);\n}\n\n/**\n * Compute and cache the token estimate for a single message. This is the\n * canonical per-message estimator \u2014 called once by ConversationState on\n * append/replace so the O(n\u00B7m) content-block walk happens at mutation time,\n * not on every context-pressure check.\n */\nexport function computeMessageTokens(msg: Message): number {\n if (typeof msg.content === 'string') return estimateTextTokens(msg.content);\n let total = 0;\n for (const b of msg.content) {\n if (b.type === 'text') total += estimateTextTokens(b.text);\n else if (b.type === 'tool_use') total += estimateToolInputTokens(b.input);\n else if (b.type === 'tool_result') total += estimateToolResultTokens(b.content);\n else total += RoughTokenEstimate(JSON.stringify(b));\n }\n return total;\n}\n\n/**\n * Estimate tokens for an array of messages (text + tool I/O), using the shared\n * 3.5 chars/token basis. This is the single canonical message-array estimator \u2014\n * compactors, the context_manager tool, and the `/context` display all route\n * through it so the number a user sees matches the number compaction decides on.\n *\n * When a message carries a pre-computed `_estTokens` field (set by\n * ConversationState on append/replace), it is used directly instead of\n * re-walking the content blocks \u2014 turning the O(n\u00B7m) scan into an O(n)\n * sum for fully-cached arrays.\n */\nexport function estimateMessageTokens(messages: readonly Message[]): number {\n let total = 0;\n for (const m of messages) {\n if (typeof m._estTokens === 'number' && m._estTokens > 0) {\n total += m._estTokens;\n continue;\n }\n total += computeMessageTokens(m);\n }\n return total;\n}\n\n/**\n * Real-usage-anchored input-token count. Given the provider's authoritative\n * prompt-token count from the last response (`anchorTokens`, a REAL number) and\n * the `messages.length` of the request that produced it (`anchorMsgCount`),\n * returns `anchorTokens + estimate(messages appended since)` \u2014 so everything up\n * to the last turn is exact and only the newest, not-yet-sent messages are\n * estimated. This is deliberately NOT calibrated: the base is already real, and\n * on the next response the whole thing re-anchors to the new real count.\n *\n * Returns `null` when there is no usable anchor (no response yet, or the\n * message array shrank below the anchor \u2014 e.g. after compaction \u2014 in which case\n * the caller falls back to a full estimate until the next response re-anchors).\n */\nexport function realAnchoredInputTokens(\n messages: readonly Message[],\n anchorTokens: number | undefined,\n anchorMsgCount: number | undefined,\n): number | null {\n if (typeof anchorTokens !== 'number' || anchorTokens <= 0) return null;\n if (typeof anchorMsgCount !== 'number' || anchorMsgCount < 0) return null;\n if (messages.length < anchorMsgCount) return null;\n const delta = anchorMsgCount === messages.length ? 0 : estimateMessageTokens(messages.slice(anchorMsgCount));\n return anchorTokens + delta;\n}\n\n/**\n * Rough estimate of tokens in a tool definition (name + description + schema).\n * Accounts for the JSON-serialized inputSchema which is sent to the API\n * but NOT included in roughEstimate(content).\n */\nexport function estimateToolDefTokens(tool: {\n name: string;\n description?: string | undefined;\n inputSchema: unknown;\n}): number {\n // Fast path: pre-computed by ToolRegistry at registration time.\n const cached = (tool as { _estDefTokens?: number | undefined })._estDefTokens;\n if (typeof cached === 'number' && cached > 0) return cached;\n\n const compact = compactToolDefinitionForWire(tool);\n return (\n RoughTokenEstimate(tool.name) +\n RoughTokenEstimate(compact.description) +\n RoughTokenEstimate(JSON.stringify(compact.inputSchema))\n );\n}\n\n/**\n * Estimate the total API request token count: system prompt + tool definitions\n * + conversation messages. Use this for context-window bar calculations\n * instead of roughEstimate (which only counts messages).\n *\n * The overhead ratio (overhead / messages) varies by conversation length:\n * - Short conversations (< 10 messages): ~30-50% overhead (large system+tools)\n * - Medium (10-50 messages): ~15-30%\n * - Long (> 50 messages): ~5-15%\n *\n * Returns { messages, systemPrompt, tools, total } for debugging display.\n */\nexport interface RequestTokenBreakdown {\n messages: number;\n systemPrompt: number;\n tools: number;\n total: number;\n}\n\nexport function estimateRequestTokens(\n messages: unknown,\n systemPrompt: unknown,\n tools: { name: string; description?: string | undefined; inputSchema: unknown }[],\n calibrationKey: string = CALIBRATION_GLOBAL_KEY,\n): RequestTokenBreakdown {\n // Messages: apply the same logic as roughEstimate\n let messagesTokens = 0;\n if (typeof messages === 'string') {\n messagesTokens = RoughTokenEstimate(messages);\n } else if (Array.isArray(messages)) {\n for (const m of messages) {\n if (typeof m === 'object' && m !== null && 'content' in m) {\n // Fast path: pre-computed per-message token estimate (set by\n // ConversationState on append/replace). Skips the O(m) content-block\n // walk entirely for cached messages.\n const cached = (m as { _estTokens?: number | undefined })._estTokens;\n if (typeof cached === 'number' && cached > 0) {\n messagesTokens += cached;\n continue;\n }\n const content = (m as { content: unknown }).content;\n if (typeof content === 'string') {\n messagesTokens += RoughTokenEstimate(content);\n } else if (Array.isArray(content)) {\n for (const b of content) {\n if (typeof b === 'object' && b !== null) {\n if ((b as { type?: string | undefined }).type === 'text') {\n messagesTokens += RoughTokenEstimate((b as { text: string }).text);\n } else {\n messagesTokens += RoughTokenEstimate(JSON.stringify(b));\n }\n }\n }\n }\n }\n }\n }\n\n // System prompt\n let systemTokens = 0;\n if (typeof systemPrompt === 'string') {\n systemTokens = RoughTokenEstimate(systemPrompt);\n } else if (Array.isArray(systemPrompt)) {\n for (const b of systemPrompt) {\n if (\n typeof b === 'object' &&\n b !== null &&\n (b as { type?: string | undefined }).type === 'text'\n ) {\n systemTokens += RoughTokenEstimate((b as { text: string }).text);\n }\n }\n }\n\n // Tool definitions\n let toolsTokens = 0;\n for (const t of tools) {\n toolsTokens += estimateToolDefTokens(t);\n }\n\n const total = messagesTokens + systemTokens + toolsTokens;\n\n // Record the raw estimate for calibration: the next recordActualUsage()\n // call will pair this against the actual API usage so the rolling ratio\n // stays in sync with the real chars/token ratio of the content.\n calState(calibrationKey).prevEst = total;\n\n return {\n messages: messagesTokens,\n systemPrompt: systemTokens,\n tools: toolsTokens,\n total,\n };\n}\n\n/**\n * Record the actual API input token count after a provider call so\n * `estimateRequestTokensCalibrated` can self-correct on subsequent calls.\n *\n * Prefer passing `estimatedInputTokens` explicitly (the calibrated pre-flight\n * estimate from the middleware) \u2014 this avoids race conditions when other code\n * also calls `estimateRequestTokens` between the pre-flight and this call\n * (e.g. audit logging in agent.ts).\n *\n * When `estimatedInputTokens` is omitted, falls back to the keyed bucket's\n * `prevEst` for backward compatibility with callers that don't have the\n * pre-flight value. `calibrationKey` selects the per-(provider,model) bucket\n * (defaults to the shared global bucket).\n */\nexport function recordActualUsage(\n actualInputTokens: number,\n estimatedInputTokens?: number,\n calibrationKey: string = CALIBRATION_GLOBAL_KEY,\n): void {\n if (actualInputTokens <= 0) return;\n const cal = calState(calibrationKey);\n const est = estimatedInputTokens ?? cal.prevEst;\n if (est <= 0) return;\n\n const sampleRatio = actualInputTokens / est;\n if (cal.count === 0) {\n cal.ratio = sampleRatio;\n } else {\n // EWM: new = \u03B1 * sample + (1-\u03B1) * old \u2192 \u03B1=0.3 = fast initial converge\n cal.ratio = CAL_ALPHA * sampleRatio + (1 - CAL_ALPHA) * cal.ratio;\n }\n // Sanity bound: keep the rolling ratio within [0.5, 1.5] so a sequence\n // of bad samples can't blow up the calibration for everyone.\n cal.ratio = Math.min(1.5, Math.max(0.5, cal.ratio));\n cal.count++;\n}\n\n/**\n * Returns the current calibration state for a bucket. Exposed for debugging\n * and tests \u2014 not needed by normal callers.\n */\nexport function getCalibrationState(calibrationKey: string = CALIBRATION_GLOBAL_KEY): {\n ratio: number;\n count: number;\n calibrated: boolean;\n} {\n const cal = calState(calibrationKey);\n return {\n ratio: cal.ratio,\n count: cal.count,\n calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION,\n };\n}\n\n/**\n * Like `estimateRequestTokens` but applies the rolling calibration factor\n * so context pressure readings converge on reality within a few iterations.\n *\n * Before any `recordActualUsage` samples are collected, returns the same\n * result as `estimateRequestTokens` (ratio = 1.0, no distortion).\n * After `MIN_SAMPLES_FOR_CALIBRATION` samples, applies the calibrated\n * multiplier capped to the range [0.5, 1.5] as a sanity bound.\n */\nexport function estimateRequestTokensCalibrated(\n messages: unknown,\n systemPrompt: unknown,\n tools: { name: string; description?: string | undefined; inputSchema: unknown }[],\n calibrationKey: string = CALIBRATION_GLOBAL_KEY,\n): RequestTokenBreakdown {\n const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);\n const cal = calState(calibrationKey);\n\n if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {\n const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));\n return {\n messages: Math.round(result.messages * safeRatio),\n systemPrompt: Math.round(result.systemPrompt * safeRatio),\n tools: Math.round(result.tools * safeRatio),\n total: Math.round(result.total * safeRatio),\n };\n }\n\n // No calibration samples yet \u2014 fall back to model-family ratio if available,\n // otherwise use the uncalibrated estimate (ratio = 1.0).\n const fallbackRatio = getModelFamilyRatio(calibrationKey);\n if (fallbackRatio !== null) {\n return {\n messages: Math.round(result.messages * fallbackRatio),\n systemPrompt: Math.round(result.systemPrompt * fallbackRatio),\n tools: Math.round(result.tools * fallbackRatio),\n total: Math.round(result.total * fallbackRatio),\n };\n }\n\n return result;\n}\n\n/** Per-block sample cap for the density scan \u2014 bounds work on giant blocks. */\nconst DENSITY_SAMPLE_PER_BLOCK = 4_096;\n/** Hard cap on total sampled chars so the density scan stays cheap. */\nconst DENSITY_SAMPLE_TOTAL_CAP = 2_000_000;\n\n/**\n * Estimate a **token-density multiplier** for content that the flat 3.5\n * chars/token basis under-counts. The basis is tuned for ASCII English\n * (~4 chars/token); CJK, and other high-codepoint scripts tokenize at ~1.5-2\n * chars/token, so a message that is mostly CJK carries up to ~2.3\u00D7 the tokens\n * the flat basis predicts. Long unbroken ASCII runs (base64, minified blobs)\n * pack slightly denser too. This scans a bounded sample and returns a\n * multiplier in [1, 2.5] \u2014 always \u2265 1, so it can only push the estimate UP,\n * never down. Used only by the send-time overflow guard, never for display.\n */\nfunction textDensityMultiplier(messages: readonly Message[]): number {\n let sampled = 0;\n let nonAscii = 0;\n let maxRun = 0;\n const consider = (s: string): void => {\n const n = Math.min(s.length, DENSITY_SAMPLE_PER_BLOCK);\n let run = 0;\n for (let i = 0; i < n; i++) {\n const c = s.charCodeAt(i);\n if (c > 127) nonAscii++;\n if (c === 32 || c === 9 || c === 10 || c === 13) {\n if (run > maxRun) maxRun = run;\n run = 0;\n } else {\n run++;\n }\n }\n if (run > maxRun) maxRun = run;\n sampled += n;\n };\n\n for (const m of messages) {\n if (typeof m.content === 'string') {\n consider(m.content);\n } else if (Array.isArray(m.content)) {\n for (const b of m.content) {\n if (b.type === 'text') consider(b.text);\n else if (b.type === 'tool_result' && typeof b.content === 'string') consider(b.content);\n else if (b.type === 'thinking') consider(b.thinking);\n }\n }\n if (sampled >= DENSITY_SAMPLE_TOTAL_CAP) break;\n }\n\n if (sampled === 0) return 1;\n const nonAsciiRatio = nonAscii / sampled;\n // 3.5 chars/token at 0% non-ASCII \u2192 1.5 at 100% (heavy CJK).\n let charsPerToken = 3.5 - 2.0 * nonAsciiRatio;\n // A very long unbroken ASCII run (base64/minified) packs a little denser.\n if (maxRun > 2_000 && nonAsciiRatio < 0.1) charsPerToken = Math.min(charsPerToken, 3.0);\n const multiplier = 3.5 / Math.max(1.4, charsPerToken);\n return Math.min(2.5, Math.max(1, multiplier));\n}\n\n/**\n * Never-undercount upper bound for the request token total, for the **send\n * guard** only. Takes the flat estimate and scales it up by the greater of the\n * content-density multiplier and the calibration ceiling, so the guarded value\n * satisfies `real \u2264 upperBound`. The context bar and `/context` keep using the\n * calibrated estimate \u2014 this deliberately over-counts, which is only ever safe\n * for the \"must this be trimmed before sending?\" decision.\n */\nexport function estimateRequestTokensUpperBound(\n messages: unknown,\n systemPrompt: unknown,\n tools: { name: string; description?: string | undefined; inputSchema: unknown }[],\n calibrationKey: string = CALIBRATION_GLOBAL_KEY,\n): RequestTokenBreakdown {\n const base = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);\n const density = Array.isArray(messages)\n ? textDensityMultiplier(messages as readonly Message[])\n : 1;\n const cal = calState(calibrationKey);\n const calCeiling =\n cal.count >= MIN_SAMPLES_FOR_CALIBRATION ? Math.min(1.5, Math.max(1, cal.ratio)) : 1;\n const mult = Math.max(density, calCeiling);\n if (mult <= 1) return base;\n return {\n messages: Math.ceil(base.messages * mult),\n systemPrompt: Math.ceil(base.systemPrompt * mult),\n tools: Math.ceil(base.tools * mult),\n total: Math.ceil(base.total * mult),\n };\n}\n\n/** Look up the fallback chars/token ratio for a calibration key (e.g. \"provider/model\"). */\nfunction getModelFamilyRatio(calibrationKey: string): number | null {\n const lower = calibrationKey.toLowerCase();\n for (const [family, ratio] of Object.entries(MODEL_FAMILY_RATIO)) {\n if (lower.includes(family)) return ratio / 3.5; // MODEL_FAMILY_RATIO is chars/token, we need multiplier\n }\n return null;\n}\n\n/**\n * Resets calibration state. Primarily for tests that run in the same\n * process and need a clean slate between suites. With no argument it clears\n * every bucket (including the global one); pass a key to reset just that bucket.\n */\nexport function resetCalibration(calibrationKey?: string): void {\n if (calibrationKey === undefined) {\n _cals.clear();\n return;\n }\n _cals.delete(calibrationKey);\n}\n", "import { buildLiveNextStepsGateBlock } from '../core/agent-response.js';\nimport {\n SYSTEM_BLOCK_SOURCE,\n type SystemBlockSource,\n} from '../core/system-prompt-builder.js';\nimport type { Context } from '../core/context.js';\nimport type { TextBlock } from '../types/blocks.js';\nimport type { Tool } from '../types/tool.js';\nimport { buildCompletedWorkLedgerBlock } from './context-evidence.js';\nimport {\n estimateTextTokens,\n estimateToolDefTokens,\n estimateToolInputTokens,\n estimateToolResultTokens,\n} from './token-estimate.js';\n\n/**\n * Real, per-category token accounting for the live context window \u2014 the data\n * behind an honest `/context` display. Every number is measured from the actual\n * assembled inputs (`ctx.systemPrompt`, `ctx.tools`, `ctx.messages`) using the\n * same 3.5-chars/token estimator the compactor and live bar use, so the total\n * here reconciles with the context-fill bar. It replaces the hardcoded fake\n * percentages the TUI dashboard used to show.\n */\nexport interface ContextBreakdown {\n system: {\n total: number;\n /** Tokens attributed to each system-prompt section (see SystemBlockSource). */\n bySource: Record<SystemBlockSource | 'other', number>;\n };\n tools: {\n total: number;\n builtin: number;\n mcp: number;\n /** Number of tool definitions in the request. */\n count: number;\n /** MCP tool tokens grouped by originating server. */\n mcpByServer: Record<string, number>;\n };\n history: {\n total: number;\n /** User/assistant text + tool_use inputs + thinking. */\n text: number;\n /** tool_result output content. */\n toolResults: number;\n messageCount: number;\n };\n volatile: {\n /** Completed-work ledger, appended to `system` at request time. */\n ledger: number;\n /** Next-steps gate, appended to `system` at request time. */\n nextsteps: number;\n total: number;\n };\n /** system.total + tools.total + history.total + volatile.total. */\n total: number;\n effectiveMaxContext: number;\n /** total / effectiveMaxContext \u2014 may exceed 1 before compaction fires. */\n usedPct: number;\n /**\n * Non-fatal build warnings encountered during breakdown computation.\n * Empty when everything succeeded; populated when volatile blocks\n * (ledger, nextsteps) threw during construction and were silently omitted.\n */\n warnings: string[];\n}\n\nconst SYSTEM_BLOCK_SOURCES: readonly (SystemBlockSource | 'other')[] = [\n 'identity',\n 'tool-usage',\n 'environment',\n 'skills',\n 'mode',\n 'plan',\n 'leader-after-task',\n 'contributor',\n 'ledger',\n 'nextsteps',\n 'other',\n];\n\nfunction emptyBySource(): Record<SystemBlockSource | 'other', number> {\n const out = {} as Record<SystemBlockSource | 'other', number>;\n for (const key of SYSTEM_BLOCK_SOURCES) out[key] = 0;\n return out;\n}\n\n/** An MCP-proxied tool, identified by capability first then the `mcp__` prefix. */\nfunction isMcpTool(tool: Tool): boolean {\n return (tool.capabilities?.includes('mcp.proxy') ?? false) || tool.name.startsWith('mcp__');\n}\n\n/** Server segment of a `mcp__<server>__<tool>` name, or a generic bucket. */\nfunction mcpServerOf(tool: Tool): string {\n const parts = tool.name.split('__');\n return parts.length >= 3 && parts[0] === 'mcp' ? (parts[1] ?? 'mcp') : 'mcp';\n}\n\nfunction safeBuild(\n fn: () => TextBlock | undefined,\n warn: (e: unknown) => void = () => {},\n): TextBlock | undefined {\n try {\n return fn();\n } catch (e) {\n warn(e);\n return undefined;\n }\n}\n\n/**\n * Mirror the denominator the agent loop (`currentMaxContext`) and the\n * auto-compaction middleware use: an explicit `effectiveMaxContext` override\n * wins, then the provider window, then a safe default. Inline-replicated here\n * to keep this module free of an `execution/`/`core/` runtime dependency for\n * the denominator (the builders it already imports are the only exception).\n */\nfunction resolveEffectiveMaxContext(ctx: Context): number {\n const metaLimit = ctx.meta?.['effectiveMaxContext'];\n const providerMax = ctx.provider.capabilities.maxContext;\n return typeof metaLimit === 'number' && metaLimit > 0\n ? metaLimit\n : typeof providerMax === 'number' && providerMax > 0\n ? providerMax\n : 200_000;\n}\n\n/**\n * Compute the real per-category token breakdown of a live context. Safe to call\n * interactively (e.g. from `/context`): it walks the full message array once, so\n * it is O(messages\u00B7blocks), not the O(1) cached path the per-turn bar uses.\n */\nexport function getContextBreakdown(ctx: Context): ContextBreakdown {\n // --- System prompt: attributed per section via the builder's WeakMap tag ---\n const bySource = emptyBySource();\n let systemTotal = 0;\n for (const block of ctx.systemPrompt) {\n const tokens = estimateTextTokens(block.text);\n systemTotal += tokens;\n bySource[SYSTEM_BLOCK_SOURCE.get(block) ?? 'other'] += tokens;\n }\n\n // --- Tool definitions: builtin vs MCP (grouped by server) ---\n let toolsBuiltin = 0;\n let toolsMcp = 0;\n const mcpByServer: Record<string, number> = {};\n for (const tool of ctx.tools) {\n const tokens = estimateToolDefTokens(tool);\n if (isMcpTool(tool)) {\n toolsMcp += tokens;\n const server = mcpServerOf(tool);\n mcpByServer[server] = (mcpByServer[server] ?? 0) + tokens;\n } else {\n toolsBuiltin += tokens;\n }\n }\n\n // --- Conversation history: text/tool_use/thinking vs tool_result output ---\n let histText = 0;\n let histToolResults = 0;\n for (const msg of ctx.messages) {\n if (typeof msg.content === 'string') {\n histText += estimateTextTokens(msg.content);\n continue;\n }\n for (const b of msg.content) {\n switch (b.type) {\n case 'text':\n histText += estimateTextTokens(b.text);\n break;\n case 'tool_use':\n histText += estimateToolInputTokens(b.input);\n break;\n case 'tool_result':\n histToolResults += estimateToolResultTokens(b.content);\n break;\n case 'thinking':\n histText += estimateTextTokens(b.thinking);\n break;\n default:\n histText += estimateTextTokens(JSON.stringify(b));\n }\n }\n }\n\n // --- Volatile per-turn blocks: re-derived (they live in the request, not\n // in ctx.systemPrompt) and tagged by construction. ---\n const warnings: string[] = [];\n const warn = (e: unknown) => {\n const msg = e instanceof Error ? e.message : String(e);\n warnings.push(msg);\n };\n const ledgerBlock = safeBuild(() => buildCompletedWorkLedgerBlock(ctx), warn);\n const nextstepsBlock = safeBuild(() => buildLiveNextStepsGateBlock(ctx), warn);\n const ledger = ledgerBlock ? estimateTextTokens(ledgerBlock.text) : 0;\n const nextsteps = nextstepsBlock ? estimateTextTokens(nextstepsBlock.text) : 0;\n\n const toolsTotal = toolsBuiltin + toolsMcp;\n const historyTotal = histText + histToolResults;\n const volatileTotal = ledger + nextsteps;\n const total = systemTotal + toolsTotal + historyTotal + volatileTotal;\n const effectiveMaxContext = resolveEffectiveMaxContext(ctx);\n\n return {\n system: { total: systemTotal, bySource },\n tools: {\n total: toolsTotal,\n builtin: toolsBuiltin,\n mcp: toolsMcp,\n count: ctx.tools.length,\n mcpByServer,\n },\n history: {\n total: historyTotal,\n text: histText,\n toolResults: histToolResults,\n messageCount: ctx.messages.length,\n },\n volatile: { ledger, nextsteps, total: volatileTotal },\n total,\n effectiveMaxContext,\n usedPct: effectiveMaxContext > 0 ? total / effectiveMaxContext : 0,\n warnings,\n };\n}\n", "/**\n * Deep merge utility \u2014 safely merges nested objects with configurable\n * conflict resolution, array merging, and prototype-pollution guarding.\n *\n * Used by:\n * - config-loader (config layer merging with primitive-array concatenation)\n * - secret-vault (config patching)\n * - json-path (json_merge tool with prefer-base / prefer-patch semantics)\n *\n * @module utils/deep-merge\n */\n\n// ---------------------------------------------------------------------------\n// Prototype-pollution guard \u2014 shared set of forbidden __proto__ keys\n// ---------------------------------------------------------------------------\n\nexport const FORBIDDEN_PROTO_KEYS = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n '__defineGetter__',\n '__defineSetter__',\n '__lookupGetter__',\n '__lookupSetter__',\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** True when every element is a primitive or null (no nested objects/arrays). */\nexport function isPrimitiveArray(a: unknown[]): boolean {\n return a.every((v) => v === null || (typeof v !== 'object' && typeof v !== 'function'));\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface DeepMergeOptions {\n /**\n * Which side wins on collision for scalars and arrays.\n *\n * - `'prefer-patch'` (default): patch value replaces base value.\n * - `'prefer-base'`: base value is kept, patch value is ignored.\n */\n conflictResolution?: 'prefer-base' | 'prefer-patch';\n\n /**\n * How to handle array values.\n *\n * - `'replace'` (default): patch array replaces base array entirely.\n * - `'concat-primitives'`: when both values are primitive arrays,\n * they are concatenated and deduped (via Set). Non-primitive\n * arrays still replace the base wholesale.\n */\n arrayMode?: 'replace' | 'concat-primitives';\n\n /**\n * Skip prototype-pollution keys (`__proto__`, `constructor`, etc.).\n * Enabled by default. Only disable when you control both inputs\n * and the keyset (e.g. when merging trusted JSON schemas).\n */\n protectProto?: boolean;\n\n /**\n * Optional callback fired when a non-primitive (object) array is\n * replaced wholesale (only relevant with `arrayMode: 'concat-primitives'`).\n * Receives the key name, existing array length, and patch array length.\n * Used by config-loader for debug logging.\n */\n onNonPrimitiveArrayReplace?: (\n key: string,\n existingLen: number,\n patchLen: number,\n ) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively merge `patch` into `base`, returning a new object.\n *\n * - Nested plain objects are merged recursively.\n * - Arrays are handled per `options.arrayMode`.\n * - Scalar collisions are resolved per `options.conflictResolution`.\n * - `null` and non-object values in `patch` replace the base value\n * (unless `conflictResolution` is `'prefer-base'`).\n * - Keys in `base` that are absent from `patch` are preserved.\n * - `FORBIDDEN_PROTO_KEYS` are skipped in the patch (unless\n * `options.protectProto` is set to `false`).\n *\n * The function is generic over `T extends Record<string, unknown>` for\n * callers that pass typed config objects, but the runtime signature\n * also accepts `unknown` inputs (used by the json-path plugin).\n */\nexport function deepMerge<T extends Record<string, unknown>>(\n base: T,\n patch: Record<string, unknown>,\n options?: DeepMergeOptions,\n): T;\n\nexport function deepMerge(\n base: unknown,\n patch: unknown,\n options?: DeepMergeOptions,\n): unknown;\n\nexport function deepMerge(\n base: unknown,\n patch: unknown,\n options: DeepMergeOptions = {},\n): unknown {\n const {\n conflictResolution = 'prefer-patch',\n arrayMode = 'replace',\n protectProto = true,\n onNonPrimitiveArrayReplace,\n } = options;\n\n // Non-object / null handling \u2014 delegate to conflict resolution.\n if (typeof base !== 'object' || base === null) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n if (typeof patch !== 'object' || patch === null) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // Arrays \u2014 handled *before* the object merge so array-of-objects\n // aren't accidentally treated as plain records.\n if (Array.isArray(base) && Array.isArray(patch)) {\n if (\n arrayMode === 'concat-primitives' &&\n isPrimitiveArray(base) &&\n isPrimitiveArray(patch)\n ) {\n return [...new Set([...base, ...patch])];\n }\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // If only one side is an array, treat as scalar collision.\n if (Array.isArray(base) || Array.isArray(patch)) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // Plain object merge.\n const baseObj = base as Record<string, unknown>;\n const patchObj = patch as Record<string, unknown>;\n const out: Record<string, unknown> = { ...baseObj };\n\n for (const [k, v] of Object.entries(patchObj)) {\n if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;\n\n const existing = out[k];\n if (\n v !== null &&\n typeof v === 'object' &&\n !Array.isArray(v) &&\n existing !== null &&\n typeof existing === 'object' &&\n !Array.isArray(existing)\n ) {\n // Recursive merge for nested plain objects.\n out[k] = deepMerge(existing, v, options);\n } else if (Array.isArray(v) && Array.isArray(existing)) {\n // Delegate to top-level array handling so arrayMode\n // (e.g. 'concat-primitives') applies to nested arrays too.\n // Fire debug hook when a non-primitive array replaces an existing\n // array (for non-primitive arrays, concat-primitives is a no-op and\n // the result is always a wholesale replacement).\n if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {\n onNonPrimitiveArrayReplace(k, existing.length, v.length);\n }\n out[k] = deepMerge(existing, v, options);\n } else if (v !== undefined) {\n // Fire debug hook when a non-primitive (object) array replaces an\n // existing value in concat-primitives mode.\n if (\n onNonPrimitiveArrayReplace &&\n Array.isArray(v) &&\n !isPrimitiveArray(v)\n ) {\n const existingLen = Array.isArray(existing) ? existing.length : 0;\n onNonPrimitiveArrayReplace(k, existingLen, v.length);\n }\n out[k] = v;\n }\n // When v === undefined, leave the existing value untouched\n // (this matches config-loader's behaviour: undefined in patch\n // means \"don't change this key\").\n }\n\n return out;\n}\n", "/**\n * Myers diff with unified-format output. No external dependencies.\n * Operates on arrays of lines (newline-terminated or stripped).\n */\n\ninterface Edit {\n op: 'equal' | 'insert' | 'delete';\n a: number;\n b: number;\n line: string;\n}\n\nfunction myersDiff(a: string[], b: string[]): Edit[] {\n const N = a.length;\n const M = b.length;\n const max = N + M;\n if (max === 0) return [];\n\n const v = new Map<number, number>();\n v.set(1, 0);\n const trace: Map<number, number>[] = [];\n\n for (let d = 0; d <= max; d++) {\n const snapshot = new Map(v);\n trace.push(snapshot);\n for (let k = -d; k <= d; k += 2) {\n const left = v.get(k - 1) ?? -1;\n const right = v.get(k + 1) ?? -1;\n let x: number;\n if (k === -d || (k !== d && left < right)) {\n x = right;\n } else {\n x = left + 1;\n }\n let y = x - k;\n while (x < N && y < M && a[x] === b[y]) {\n x++;\n y++;\n }\n v.set(k, x);\n if (x >= N && y >= M) {\n return backtrack(trace, a, b, N, M, d);\n }\n }\n }\n return [];\n}\n\nfunction backtrack(\n trace: Map<number, number>[],\n a: string[],\n b: string[],\n N: number,\n M: number,\n finalD: number,\n): Edit[] {\n const edits: Edit[] = [];\n let x = N;\n let y = M;\n for (let d = finalD; d > 0; d--) {\n const v = trace[d];\n if (!v) break;\n const k = x - y;\n const left = v.get(k - 1) ?? -1;\n const right = v.get(k + 1) ?? -1;\n let prevK: number;\n if (k === -d || (k !== d && left < right)) {\n prevK = k + 1;\n } else {\n prevK = k - 1;\n }\n const prevX = v.get(prevK) ?? 0;\n const prevY = prevX - prevK;\n while (x > prevX && y > prevY) {\n edits.push({ op: 'equal', a: x - 1, b: y - 1, line: a[x - 1] ?? '' });\n x--;\n y--;\n }\n if (d > 0) {\n if (x === prevX) {\n edits.push({ op: 'insert', a: x, b: y - 1, line: b[y - 1] ?? '' });\n } else {\n edits.push({ op: 'delete', a: x - 1, b: y, line: a[x - 1] ?? '' });\n }\n x = prevX;\n y = prevY;\n }\n }\n while (x > 0 && y > 0) {\n edits.push({ op: 'equal', a: x - 1, b: y - 1, line: a[x - 1] ?? '' });\n x--;\n y--;\n }\n return edits.reverse();\n}\n\nexport interface UnifiedDiffOptions {\n context?: number | undefined;\n fromFile?: string | undefined;\n toFile?: string | undefined;\n}\n\nexport function unifiedDiff(\n oldText: string,\n newText: string,\n opts: UnifiedDiffOptions = {},\n): string {\n const context = opts.context ?? 3;\n const a = oldText.split('\\n');\n const b = newText.split('\\n');\n // Handle trailing newline: split adds an empty string we don't want to diff\n if (a[a.length - 1] === '') a.pop();\n if (b[b.length - 1] === '') b.pop();\n const edits = myersDiff(a, b);\n if (edits.every((e) => e.op === 'equal')) return '';\n\n const hunks: { aStart: number; bStart: number; lines: string[] }[] = [];\n let i = 0;\n while (i < edits.length) {\n while (i < edits.length && edits[i]?.op === 'equal') i++;\n if (i >= edits.length) break;\n const hunkStart = Math.max(0, i - context);\n const lines: string[] = [];\n let aStart = (edits[hunkStart]?.a ?? 0) + 1;\n let bStart = (edits[hunkStart]?.b ?? 0) + 1;\n let aCount = 0;\n let bCount = 0;\n let cursor = hunkStart;\n let trailing = 0;\n while (cursor < edits.length) {\n const e = edits[cursor];\n if (!e) break;\n if (e.op === 'equal') {\n trailing++;\n if (trailing > context * 2) break;\n } else {\n trailing = 0;\n }\n if (e.op === 'equal') {\n lines.push(` ${e.line}`);\n aCount++;\n bCount++;\n } else if (e.op === 'delete') {\n lines.push(`-${e.line}`);\n aCount++;\n } else {\n lines.push(`+${e.line}`);\n bCount++;\n }\n cursor++;\n }\n // Trim trailing context lines beyond `context`\n while (lines.length > 0 && lines[lines.length - 1]?.startsWith(' ') && trailing > context) {\n lines.pop();\n aCount--;\n bCount--;\n trailing--;\n }\n if (aCount === 0) aStart = 0;\n if (bCount === 0) bStart = 0;\n hunks.push({ aStart, bStart, lines });\n i = cursor;\n }\n if (hunks.length === 0) return '';\n\n let out = '';\n out += `--- ${opts.fromFile ?? 'a'}\\n`;\n out += `+++ ${opts.toFile ?? 'b'}\\n`;\n for (const h of hunks) {\n let aCount = 0;\n let bCount = 0;\n for (const l of h.lines) {\n if (l.startsWith(' ')) {\n aCount++;\n bCount++;\n } else if (l.startsWith('-')) aCount++;\n else if (l.startsWith('+')) bCount++;\n }\n out += `@@ -${h.aStart},${aCount} +${h.bStart},${bCount} @@\\n`;\n out += `${h.lines.join('\\n')}\\n`;\n }\n return out;\n}\n", "import { expectDefined } from './expect-defined.js';\n/**\n * Glob pattern \u2192 concrete file path expansion.\n *\n * Supports: *, **, ?, [...]\n * Does NOT support brace expansion {a,b}.\n *\n * Returns the input as-is if it contains no glob metacharacters.\n * On Windows, both / and \\ are accepted as path separators.\n */\n\nimport * as fsp from 'node:fs/promises';\nimport { isAbsolute, resolve } from 'node:path';\nconst GLOB_CHARS = new Set(['*', '?', '[']);\nconst IS_WINDOWS = process.platform === 'win32';\nconst SEP = IS_WINDOWS ? '\\\\' : '/';\n\nfunction isGlob(p: string): boolean {\n for (const c of p) {\n if (GLOB_CHARS.has(c)) return true;\n }\n return false;\n}\n\nfunction globToRegex(pat: string): RegExp {\n let i = 0;\n let re = '^';\n while (i < pat.length) {\n const c = expectDefined(pat[i]);\n if (c === '*') {\n if (pat[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pat[i] === '/') i++;\n } else {\n re += '[^/\\\\\\\\]*';\n i++;\n }\n } else if (c === '?') {\n re += '[^/\\\\\\\\]';\n i++;\n } else if (c === '[') {\n let cls = '[';\n i++;\n if (pat[i] === '!' || pat[i] === '^') {\n cls += '^';\n i++;\n }\n while (i < pat.length && pat[i] !== ']') {\n const ch = pat[i] ?? '';\n if (ch === '\\\\') cls += '\\\\\\\\';\n else if (ch === ']' || ch === '^') cls += `\\\\${ch}`;\n else cls += ch;\n i++;\n }\n cls += ']';\n re += cls;\n i++;\n } else {\n re += c.replace(/[.+^${}()|\\\\]/g, '\\\\$&');\n i++;\n }\n }\n return new RegExp(re + '$');\n}\n\nfunction baseDir(pat: string): string {\n // Deepest literal directory prefix: cut at the last separator BEFORE the\n // first glob char. Scanning from the end instead finds separators inside\n // glob segments \u2014 '**/*.ts' would yield base '**' on POSIX (native sep '/').\n let firstGlob = pat.length;\n for (let i = 0; i < pat.length; i++) {\n if (GLOB_CHARS.has(expectDefined(pat[i]))) {\n firstGlob = i;\n break;\n }\n }\n const cut = Math.max(\n pat.lastIndexOf(SEP, firstGlob - 1),\n pat.lastIndexOf('/', firstGlob - 1),\n );\n return cut < 0 ? '.' : pat.slice(0, cut);\n}\n\n/**\n * Resolve `pattern` to the set of concrete file paths it matches.\n * Literal paths (no glob chars) are returned as-is.\n *\n * @example\n * await expandGlob('src/**\\/*.ts') // \u2192 ['src/a.ts', 'src/b/c.ts', ...]\n * await expandGlob('foo.txt') // \u2192 ['foo.txt']\n */\nexport async function expandGlob(pattern: string): Promise<string[]> {\n if (!isGlob(pattern)) return [pattern];\n\n const results = new Set<string>();\n const abs = isAbsolute(pattern);\n const base = abs ? baseDir(pattern) : baseDir(pattern);\n const relPat = base === '.' ? pattern : pattern.slice(base.length + 1);\n\n async function walk(dir: string, pat: string): Promise<void> {\n let entries: string[];\n try {\n entries = await fsp.readdir(dir);\n } catch {\n return;\n }\n\n const firstGlob = pat.search(/[*?[[]/);\n\n if (firstGlob < 0) {\n const re = globToRegex(pat);\n for (const e of entries) {\n if (re.test(e)) {\n const full = `${dir}${SEP}${e}`;\n results.add(abs ? resolve(full) : full);\n }\n }\n return;\n }\n\n const before = pat.slice(0, firstGlob);\n const rest = pat.slice(firstGlob);\n\n if (before.endsWith('**')) {\n // Match at current dir then recurse into subdirs\n await walk(dir, rest);\n for (const e of entries) {\n const full = `${dir}${SEP}${e}`;\n try {\n const stat = await fsp.stat(full);\n if (stat.isDirectory()) await walk(full, rest);\n } catch {\n /* skip inaccessible */\n }\n }\n } else if (before === '') {\n // Pattern starts with a glob char \u2014 match files in current dir only\n const re = globToRegex(rest);\n for (const e of entries) {\n if (re.test(e)) {\n const full = `${dir}${SEP}${e}`;\n results.add(abs ? resolve(full) : full);\n }\n }\n } else {\n // Literal segment(s) before the glob \u2014 descend into matching subdir\n const seg = before.replace(/[*?[\\]]/g, '').replace(/\\/$/, '');\n if (entries.includes(seg)) {\n const full = `${dir}${SEP}${seg}`;\n try {\n const stat = await fsp.stat(full);\n if (stat.isDirectory()) await walk(full, rest);\n } catch {\n /* skip */\n }\n }\n }\n }\n\n await walk(base === '.' ? '.' : base, relPat);\n return [...results];\n}\n", "import { expectDefined } from './expect-defined.js';\n/**\n * Minimal glob matcher for trust patterns.\n * Supports: *, **, ?, character classes [abc], [a-z], negation [!...] or [^...].\n *\n * Compiled regexes are cached so repeated calls with the same pattern\n * avoid recompilation overhead.\n */\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.+^${}()|\\\\]/g, '\\\\$&');\n}\n\n// Module-level cache to avoid recompiling the same pattern on every call.\n// LRU-ish eviction keeps unbounded growth in check for long-running processes.\nconst COMPILED_GLOB_CACHE = new Map<string, RegExp>();\nconst CACHE_MAX_SIZE = 2000;\n\n// Matches nothing \u2014 `[^\\s\\S]` can never be satisfied. Used as the cached\n// result for patterns that fail to compile (e.g. an over-long auto-trusted\n// command) so one bad trust entry degrades to \"no match\" instead of throwing.\nconst NEVER_MATCH = /[^\\s\\S]/;\n\nfunction getCachedGlob(pattern: string): RegExp {\n const cached = COMPILED_GLOB_CACHE.get(pattern);\n if (cached) return cached;\n if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {\n // Evict oldest 25% when at capacity\n const keys = [...COMPILED_GLOB_CACHE.keys()];\n for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {\n COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));\n }\n }\n let re: RegExp;\n try {\n re = compileGlob(pattern);\n } catch {\n // A pathological trust pattern (over MAX_GLOB_PATTERN_LEN \u2014 e.g. a long\n // one-liner auto-trusted in YOLO/Auto mode) must NOT throw out of every\n // subsequent permission check and break unrelated commands like `true`\n // or `ls` (#20). Cache a never-matching regex so the bad entry is inert.\n re = NEVER_MATCH;\n }\n COMPILED_GLOB_CACHE.set(pattern, re);\n return re;\n}\n\n// Cap glob pattern length to prevent excessively long compiled regexes.\nconst MAX_GLOB_PATTERN_LEN = 1024;\n\nexport function compileGlob(pattern: string): RegExp {\n if (pattern.length > MAX_GLOB_PATTERN_LEN) {\n throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);\n }\n let i = 0;\n let re = '^';\n while (i < pattern.length) {\n const c = pattern[i];\n if (c === '*') {\n if (pattern[i + 1] === '*') {\n // ** matches any number of chars including /\n re += '.*';\n i += 2;\n // Skip trailing slash so '**/x' matches 'x'\n if (pattern[i] === '/') i++;\n } else {\n // single * matches any chars except /\n re += '[^/]*';\n i++;\n }\n } else if (c === '?') {\n re += '[^/]';\n i++;\n } else if (c === '[') {\n let cls = '[';\n i++;\n if (pattern[i] === '!' || pattern[i] === '^') {\n cls += '^';\n i++;\n }\n while (i < pattern.length && pattern[i] !== ']') {\n const ch = pattern[i] ?? '';\n // Inside a regex class, only `]`, `\\`, and `^`/`-` at boundaries need\n // escaping. We've already consumed the leading `^`; the rest are\n // literal. Escape `\\` defensively and pass the rest through verbatim\n // so ranges like `a-z` continue to work.\n if (ch === '\\\\') {\n cls += '\\\\\\\\';\n } else if (ch === ']' || ch === '^') {\n cls += `\\\\${ch}`;\n } else {\n cls += ch;\n }\n i++;\n }\n cls += ']';\n re += cls;\n i++; // skip closing ]\n } else {\n re += escapeRegex(c ?? '');\n i++;\n }\n }\n re += '$';\n return new RegExp(re);\n}\n\nexport function matchGlob(pattern: string, input: string): boolean {\n return getCachedGlob(pattern).test(input);\n}\n\nexport function matchAny(patterns: string[], input: string): boolean {\n return patterns.some((p) => matchGlob(p, input));\n}\n", "import type { ContentBlock, ImageBlock } from '../types/blocks.js';\n\n/**\n * Wire shape for one image attached to a WebUI `user_message`. `data` may be\n * a bare base64 string or a full `data:` URL (the client normally strips the\n * prefix, but legacy senders shipped the whole URL).\n */\nexport interface IncomingImagePayload {\n data: string;\n mediaType?: string | undefined;\n /** Original filename, when the image came from a file picker or drop. */\n name?: string | undefined;\n}\n\nexport const MAX_INCOMING_IMAGES = 8;\n\n/**\n * Decoded-byte cap per image. The WebUI client downscales before sending, so\n * anything larger than this is either a bypassed client or an abuse attempt.\n * Kept under the servers' WS maxPayload once base64 overhead (~4/3) and the\n * surrounding JSON envelope are added.\n */\nexport const MAX_INCOMING_IMAGE_BYTES = 8 * 1024 * 1024;\n\n/** Media types every supported vision wire accepts (Anthropic passthrough,\n * OpenAI data-URLs, Gemini inlineData). */\nconst ALLOWED_IMAGE_MEDIA_TYPES = new Set<string>([\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/gif',\n]);\n\n/** Validation failure on user-supplied image payloads. The message is safe to\n * echo back to the client verbatim. */\nexport class IncomingImageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IncomingImageError';\n }\n}\n\nconst DATA_URL_RE = /^data:([a-z0-9.+-]+\\/[a-z0-9.+-]+)?(?:;[a-z0-9-]+=[^;,]*)*(;base64)?,/i;\n\nfunction splitDataUrl(data: string): { base64: string; mediaType?: string | undefined } {\n const match = DATA_URL_RE.exec(data);\n if (!match) return { base64: data.trim() };\n return {\n base64: data.slice(match[0].length).trim(),\n mediaType: match[1]?.toLowerCase(),\n };\n}\n\n/**\n * Validate and normalize the `images` field of a `user_message` payload into\n * canonical {@link ImageBlock}s. Accepts the legacy single `imageBase64`\n * field (a data-URL) as a trailing entry so old clients keep working.\n *\n * Throws {@link IncomingImageError} on count/size/media-type violations.\n */\nexport function parseIncomingImages(\n images?: readonly IncomingImagePayload[] | undefined,\n legacyImageBase64?: string | undefined,\n): ImageBlock[] {\n const raw: IncomingImagePayload[] = [...(images ?? [])];\n if (legacyImageBase64) raw.push({ data: legacyImageBase64 });\n if (raw.length === 0) return [];\n if (raw.length > MAX_INCOMING_IMAGES) {\n throw new IncomingImageError(\n `Too many images: ${raw.length} (max ${MAX_INCOMING_IMAGES} per message).`,\n );\n }\n\n return raw.map((img, i) => {\n const { base64, mediaType: fromUrl } = splitDataUrl(img.data ?? '');\n const mediaType = (img.mediaType ?? fromUrl ?? 'image/png').toLowerCase();\n if (!ALLOWED_IMAGE_MEDIA_TYPES.has(mediaType)) {\n throw new IncomingImageError(\n `Image ${i + 1}: unsupported media type \"${mediaType}\" (allowed: ${[...ALLOWED_IMAGE_MEDIA_TYPES].join(', ')}).`,\n );\n }\n if (!base64) {\n throw new IncomingImageError(`Image ${i + 1}: empty image data.`);\n }\n // Base64 alphabet check \u2014 cheap linear scan that rejects raw binary or\n // JSON smuggled into the field before it reaches a provider wire.\n if (!/^[A-Za-z0-9+/]+={0,2}$/.test(base64)) {\n throw new IncomingImageError(`Image ${i + 1}: data is not valid base64.`);\n }\n const bytes = Math.floor((base64.length * 3) / 4);\n if (bytes > MAX_INCOMING_IMAGE_BYTES) {\n throw new IncomingImageError(\n `Image ${i + 1}: ${(bytes / (1024 * 1024)).toFixed(1)} MB exceeds the ${MAX_INCOMING_IMAGE_BYTES / (1024 * 1024)} MB limit.`,\n );\n }\n return {\n type: 'image',\n source: { type: 'base64', media_type: mediaType, data: base64 },\n } satisfies ImageBlock;\n });\n}\n\n/**\n * Assemble the agent input for a user message that carries images: image\n * blocks first (the order vision providers prefer), then the text block.\n */\nexport function buildUserContentBlocks(\n text: string,\n images: readonly ImageBlock[],\n): ContentBlock[] {\n const blocks: ContentBlock[] = [...images];\n if (text) blocks.push({ type: 'text', text });\n return blocks;\n}\n", "/**\n * Shared IP-address guards for SSRF protection.\n *\n * Exported so `fetch.ts` (tools), `web-search/index.ts` (plugins), and any\n * other package that needs to validate IPs can all consume the same logic.\n * Any future additions (e.g. extra CIDR blocks) need only be made here.\n */\n\nimport * as dns from 'node:dns/promises';\nimport * as net from 'node:net';\n\n/**\n * True if `addr` is in a private / loopback / link-local / reserved / CGNAT /\n * multicast range. `net.isIP` is called by the caller first so `addr` is\n * guaranteed to be a canonical dotted-quad at this point.\n */\nexport function isPrivateIPv4(addr: string): boolean {\n const parts = addr.split('.').map((p) => Number.parseInt(p, 10));\n if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) {\n return true; // defensive: malformed \u2192 block\n }\n const [a, b, c] = parts as [number, number, number, number];\n if (a === 0) return true; // 0.0.0.0/8 \"this host\"\n if (a === 10) return true; // 10.0.0.0/8 private\n if (a === 127) return true; // 127.0.0.0/8 loopback\n if (a === 169 && b === 254) return true; // 169.254.0.0/16 link-local + AWS/GCE/Azure IMDS\n if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12 private\n if (a === 192 && b === 168) return true; // 192.168.0.0/16 private\n if (a === 192 && b === 0 && c === 0) return true; // 192.0.0.0/24 reserved\n if (a === 100 && b >= 64 && b <= 127) return true; // 100.64.0.0/10 CGNAT\n if (a >= 224) return true; // 224.0.0.0/4 multicast + 240.0.0.0/4 reserved\n return false;\n}\n\n/**\n * True if `raw` (an IPv6 literal, already lowercased) is loopback / unique-local /\n * link-local / unspecified / IPv4-mapped-private.\n */\nexport function isPrivateIPv6(raw: string): boolean {\n const lower = raw.toLowerCase();\n if (lower === '::' || lower === '::1') return true; // loopback / unspecified\n\n // Expand to 8-group canonical form so range checks don't have to handle every\n // shorthand notation. Returns null on malformed input \u2014 we conservatively\n // block in that case rather than leaking.\n const groups = expandIPv6(lower);\n if (!groups) return true;\n\n // IPv4-mapped: ::ffff:0:0/96 \u2192 groups[0..5] all 0, groups[6..7] hold the\n // embedded IPv4 as two 16-bit words. Node URL normalises the dotted form to\n // this representation (e.g. ::ffff:127.0.0.1 \u2192 ::ffff:7f00:1).\n if (\n groups[0] === 0 &&\n groups[1] === 0 &&\n groups[2] === 0 &&\n groups[3] === 0 &&\n groups[4] === 0 &&\n groups[5] === 0xffff\n ) {\n const a = (groups[6] ?? 0) >> 8;\n const b = (groups[6] ?? 0) & 0xff;\n const c = (groups[7] ?? 0) >> 8;\n const d = (groups[7] ?? 0) & 0xff;\n return isPrivateIPv4(`${a}.${b}.${c}.${d}`);\n }\n\n const high = groups[0] ?? 0;\n if ((high & 0xfe00) === 0xfc00) return true; // fc00::/7 unique local (fc..fd)\n if ((high & 0xffc0) === 0xfe80) return true; // fe80::/10 link-local\n if ((high & 0xff00) === 0xff00) return true; // ff00::/8 multicast\n return false;\n}\n\n/**\n * Expand an IPv6 string into exactly 8 16-bit numbers. Handles `::` compression.\n * Returns null on malformed input \u2014 caller should treat that as \"block\".\n */\nexport function expandIPv6(addr: string): number[] | null {\n const parts = addr.split('::');\n if (parts.length > 2) return null;\n\n const parseGroups = (s: string): number[] | null => {\n if (s === '') return [];\n const out: number[] = [];\n for (const g of s.split(':')) {\n if (g.length === 0 || g.length > 4) return null;\n const n = Number.parseInt(g, 16);\n if (Number.isNaN(n) || n < 0 || n > 0xffff) return null;\n out.push(n);\n }\n return out;\n };\n\n if (parts.length === 1) {\n const groups = parseGroups(parts[0] ?? '');\n if (groups?.length !== 8) return null;\n return groups;\n }\n\n const head = parseGroups(parts[0] ?? '');\n const tail = parseGroups(parts[1] ?? '');\n if (!head || !tail) return null;\n const fill = 8 - head.length - tail.length;\n if (fill < 0) return null;\n return [...head, ...new Array<number>(fill).fill(0), ...tail];\n}\n\n/**\n * Convenience: throw if `hostname` resolves to a private / loopback IP.\n * Use as a pre-flight check before opening a socket.\n *\n * \u26A0\uFE0F This is not sufficient alone \u2014 connections must also use a pinned\n * dispatcher (so the OS re-uses the already-resolved address) or the same\n * check must be applied after every redirect hop. See `guardedLookup` in\n * `fetch.ts` for the connection-level enforcement.\n */\nexport async function assertNotPrivateHost(hostname: string): Promise<void> {\n const host =\n hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;\n\n if (host === 'localhost' || host.endsWith('.localhost')) {\n throw new Error('fetch: blocked localhost target');\n }\n\n const ipVersion = net.isIP(host);\n if (ipVersion === 4) {\n if (isPrivateIPv4(host)) {\n throw new Error(`fetch: blocked private/loopback address \"${host}\"`);\n }\n } else if (ipVersion === 6) {\n if (isPrivateIPv6(host)) {\n throw new Error(`fetch: blocked private/loopback address \"${host}\"`);\n }\n } else {\n // Hostname \u2014 resolve and reject if ANY record is private.\n try {\n const records = await dns.lookup(host, { all: true });\n for (const r of records) {\n // dns.lookup family: 4 = IPv4, 6 = IPv6\n const bad = r.family === 4 ? isPrivateIPv4(r.address) : isPrivateIPv6(r.address);\n if (bad) {\n throw new Error(`fetch: resolved to private address ${r.address}`);\n }\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('fetch:')) throw err;\n // DNS failure \u2014 let fetch handle it rather than doubling the error.\n }\n }\n}\n", "import { expectDefined } from './expect-defined.js';\n\n/**\n * Attempt to close an incomplete JSON object string by auto-closing braces\n * and completing any unclosed double-quoted string values.\n *\n * Strategy:\n * 1. Compute origOpen from the ORIGINAL input (how many braces are unclosed).\n * 2. Add that many closing braces. If result is now valid JSON \u2192 return it.\n * 3. If still invalid: trim trailing whitespace, strip trailing backslash.\n * 4. Walk backwards to detect an unclosed string value.\n * - Quote followed by `:` \u2192 key-name, skip\n * - Quote followed by `,` `}` or end-of-string \u2192 toggle in/out of string\n * 5. If we end INSIDE a string (unclosed opening `\"`), append `\"` + origOpen `}`.\n *\n * Known limitations:\n * - Strings whose content ends with a `\"` character cannot be repaired\n * (algorithm can't distinguish content-`\"` from string-terminator `\"`).\n * - Input ending in bare `:` (incomplete value expression) can't be meaningfully repaired.\n * - Bare `{` returns unchanged.\n * - If origOpen=0 (braces balanced) but string is unclosed, repair is skipped\n * (the input would be valid JSON per JSON.parse, so it's returned as-is).\n */\nexport function completePartialObject(s: string): string {\n if (!s.trim().startsWith('{')) return s;\n if (tryParse(s).ok) return s;\n return repairTruncated(s);\n}\n\nfunction repairTruncated(s: string): string {\n // Single forward scan capturing the structural state at the truncation point:\n // the open-container stack, whether we are inside a string, a dangling escape,\n // and where the last significant (non-trailing-whitespace) character sits.\n const stack: ('{' | '[')[] = [];\n let inString = false;\n let escaped = false;\n let sawKey = false; // have we seen any string (i.e. real content) yet?\n let prevSig = ''; // last significant char seen outside of a string\n let contentEnd = 0; // index just past the last significant char\n // Count unbalanced `{` accumulated *inside* the currently-open string value,\n // so a truncation mid-string like `\"a{` can be balanced before closing it.\n let stringBraceDepth = 0;\n\n for (let i = 0; i < s.length; i++) {\n const ch = expectDefined(s[i]);\n if (inString) {\n contentEnd = i + 1;\n if (escaped) {\n escaped = false;\n continue;\n }\n if (ch === '\\\\') {\n escaped = true;\n continue;\n }\n if (ch === '\"') {\n inString = false;\n prevSig = '\"';\n stringBraceDepth = 0;\n continue;\n }\n if (ch === '{') stringBraceDepth++;\n else if (ch === '}' && stringBraceDepth > 0) stringBraceDepth--;\n continue;\n }\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') continue;\n contentEnd = i + 1;\n if (ch === '\"') {\n inString = true;\n sawKey = true;\n stringBraceDepth = 0;\n prevSig = '\"';\n } else if (ch === '{' || ch === '[') {\n stack.push(ch);\n prevSig = ch;\n } else if (ch === '}' || ch === ']') {\n stack.pop();\n prevSig = ch;\n } else {\n prevSig = ch;\n }\n }\n\n // A lone open brace (or anything with no key/content) can't be meaningfully\n // completed \u2014 return it untouched.\n if (!sawKey && !inString) return s;\n\n // Drop trailing whitespace that sits outside any string.\n let result = s.slice(0, contentEnd);\n\n if (inString) {\n // A dangling lone backslash can't begin a valid escape \u2014 drop it.\n if (escaped) {\n result = result.slice(0, -1);\n } else if (endsWithInvalidEscape(result)) {\n // A trailing invalid escape (e.g. `\\}`) can't be completed into valid\n // JSON \u2014 strip the backslash and its bogus escapee.\n result = result.slice(0, -2);\n }\n // Balance braces opened inside the truncated string before closing it.\n if (stringBraceDepth > 0) result += '}'.repeat(stringBraceDepth);\n result += '\"';\n } else if (prevSig === ':') {\n // A key with no value (e.g. `{\"k\":`) \u2014 complete it to null.\n result += 'null';\n }\n\n // Close any still-open containers in reverse order.\n for (let k = stack.length - 1; k >= 0; k--) {\n result += stack[k] === '{' ? '}' : ']';\n }\n\n // Last resort: an empty value sitting before an existing close (`{\"k\":}`)\n // leaves invalid JSON \u2014 fill it with null.\n if (!tryParse(result).ok) {\n const patched = result.replace(/:(\\s*)([}\\]])/g, ':null$2');\n if (tryParse(patched).ok) result = patched;\n }\n\n return result;\n}\n\nconst VALID_ESCAPE = new Set(['\"', '\\\\', '/', 'b', 'f', 'n', 'r', 't', 'u']);\n\n/** True when `str` ends with a backslash escape that JSON does not allow. */\nfunction endsWithInvalidEscape(str: string): boolean {\n const last = str[str.length - 1];\n if (str[str.length - 2] !== '\\\\' || last === undefined) return false;\n if (VALID_ESCAPE.has(last)) return false;\n // The backslash must itself be unescaped (odd run of backslashes before it).\n let backslashes = 0;\n for (let k = str.length - 2; k >= 0 && str[k] === '\\\\'; k--) backslashes++;\n return backslashes % 2 === 1;\n}\n\nfunction tryParse(s: string): { ok: true; value: unknown } | { ok: false } {\n try {\n return { ok: true, value: JSON.parse(s) };\n } catch {\n return { ok: false };\n }\n}\n", "/**\n * Minimal JSON Schema validator \u2014 covers the subset needed for plugin\n * configSchema validation and tool inputSchema sanity checks. Intentionally\n * small (~80 lines, zero deps) and tolerant: unknown keywords are ignored so\n * authors can mix in non-standard extensions without breaking validation.\n *\n * NOT for full JSON Schema 2020-12 conformance. If a plugin needs $ref,\n * conditional schemas, format validation, or anything else exotic, it should\n * bring its own ajv-based validator and call this only for the cheap path.\n */\nimport type { JSONSchema } from '../types/tool.js';\n\nexport interface ValidationError {\n path: string;\n message: string;\n}\n\nexport interface ValidationResult {\n ok: boolean;\n errors: ValidationError[];\n}\n\nexport function validateAgainstSchema(value: unknown, schema: JSONSchema): ValidationResult {\n const errors: ValidationError[] = [];\n walk(value, schema, '', errors, 0);\n return { ok: errors.length === 0, errors };\n}\n\n/**\n * Maximum nesting depth before the validator stops recursing and reports a\n * \"schema too deep\" error. Deeply nested input (e.g. batch_tool_use with 100\n * nested calls \u2192 tool_use \u2192 input) can otherwise hit `RangeError: Maximum\n * call stack size exceeded` and crash the tool executor.\n *\n * 64 is generous: real-world tool schemas rarely nest beyond 5-6 levels, and\n * even pathological inputs (deeply recursive JSON) stay well under this.\n * The limit is a safety net against unbounded recursion, not a tight bound.\n */\nconst MAX_SCHEMA_DEPTH = 64;\n\nfunction walk(\n value: unknown,\n schema: JSONSchema,\n path: string,\n errors: ValidationError[],\n depth: number,\n): void {\n // P2 #8 (before-release.md): cap recursion depth to prevent\n // `RangeError: Maximum call stack size exceeded` on deeply nested input.\n // Push a validation error and stop descending \u2014 the caller still gets a\n // usable (ok: false) result instead of a crash.\n if (depth > MAX_SCHEMA_DEPTH) {\n errors.push({\n path: path || '<root>',\n message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`,\n });\n return;\n }\n if (schema.enum !== undefined) {\n if (!enumIncludes(schema.enum, value)) {\n errors.push({\n path: path || '<root>',\n message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`,\n });\n return;\n }\n }\n\n if (typeof schema.type === 'string') {\n if (!checkType(value, schema.type)) {\n errors.push({\n path: path || '<root>',\n message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`,\n });\n return;\n }\n }\n\n if (schema.type === 'object' && isPlainObject(value)) {\n const obj = value as Record<string, unknown>;\n for (const req of schema.required ?? []) {\n if (!(req in obj)) {\n const expected = schema.properties?.[req]?.type;\n errors.push({\n path: joinPath(path, req),\n message: `required property missing${typeof expected === 'string' ? ` (expected ${expected})` : ''}`,\n });\n }\n }\n if (schema.properties) {\n for (const [key, subSchema] of Object.entries(schema.properties)) {\n if (key in obj) {\n walk(obj[key], subSchema, joinPath(path, key), errors, depth + 1);\n }\n }\n }\n }\n\n if (schema.type === 'array' && Array.isArray(value) && schema.items) {\n for (let i = 0; i < value.length; i++) {\n walk(value[i], schema.items as JSONSchema, `${path}[${i}]`, errors, depth + 1);\n }\n }\n}\n\nexport interface CoercionResult {\n value: unknown;\n /** True when at least one leaf was rewritten. */\n changed: boolean;\n}\n\n/**\n * Best-effort, lossless coercion of a value toward a JSON Schema. Models \u2014\n * especially through OpenAI-compatible proxies \u2014 frequently deliver\n * arguments with the right *content* in the wrong *type*: numbers and\n * booleans encoded as strings (\"5\", \"true\"), scalars where a string is\n * expected, or a whole nested object serialized into a JSON string.\n *\n * Only conversions that cannot lose information are applied:\n * - string \u2192 number/integer when the whole trimmed string parses cleanly\n * - string \"true\"/\"false\" \u2192 boolean\n * - number/boolean \u2192 string when a string is expected\n * - JSON-serialized string \u2192 object/array when the parse yields that shape\n * Recurses into `properties`/`items`. Returns the (possibly new) value and\n * whether anything changed \u2014 callers should re-validate before trusting it.\n */\nexport function coerceAgainstSchema(value: unknown, schema: JSONSchema): CoercionResult {\n return coerceWalk(value, schema, 0);\n}\n\nfunction coerceWalk(value: unknown, schema: JSONSchema, depth: number): CoercionResult {\n if (depth > MAX_SCHEMA_DEPTH) return { value, changed: false };\n\n const type = typeof schema.type === 'string' ? schema.type : undefined;\n\n // Leaf conversions (only when the current type does NOT already match).\n if (type && !checkType(value, type)) {\n if (type === 'string' && (typeof value === 'number' || typeof value === 'boolean')) {\n return { value: String(value), changed: true };\n }\n if ((type === 'number' || type === 'integer') && typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed !== '' && /^-?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$/.test(trimmed)) {\n const num = Number(trimmed);\n if (!Number.isNaN(num) && (type === 'number' || Number.isInteger(num))) {\n return { value: num, changed: true };\n }\n }\n return { value, changed: false };\n }\n if (type === 'boolean' && typeof value === 'string') {\n const lowered = value.trim().toLowerCase();\n if (lowered === 'true') return { value: true, changed: true };\n if (lowered === 'false') return { value: false, changed: true };\n return { value, changed: false };\n }\n if ((type === 'object' || type === 'array') && typeof value === 'string') {\n // A nested structure serialized into a string (double-encoded args).\n try {\n const parsed: unknown = JSON.parse(value);\n if (checkType(parsed, type)) {\n // Recurse so leaves inside the revived structure also coerce.\n return { value: coerceWalk(parsed, schema, depth + 1).value, changed: true };\n }\n } catch {\n // fall through \u2014 leave as-is, validation will report it\n }\n return { value, changed: false };\n }\n return { value, changed: false };\n }\n\n // Structural recursion.\n if (type === 'object' && isPlainObject(value) && schema.properties) {\n const obj = value as Record<string, unknown>;\n let changed = false;\n const out: Record<string, unknown> = { ...obj };\n for (const [key, subSchema] of Object.entries(schema.properties)) {\n if (!(key in obj)) continue;\n const r = coerceWalk(obj[key], subSchema, depth + 1);\n if (r.changed) {\n out[key] = r.value;\n changed = true;\n }\n }\n return changed ? { value: out, changed } : { value, changed: false };\n }\n\n if (type === 'array' && Array.isArray(value) && schema.items) {\n let changed = false;\n const out = value.map((item) => {\n const r = coerceWalk(item, schema.items as JSONSchema, depth + 1);\n if (r.changed) changed = true;\n return r.value;\n });\n return changed ? { value: out, changed } : { value, changed: false };\n }\n\n return { value, changed: false };\n}\n\nfunction checkType(value: unknown, type: string): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n return typeof value === 'number' && !Number.isNaN(value);\n case 'integer':\n return typeof value === 'number' && Number.isInteger(value);\n case 'boolean':\n return typeof value === 'boolean';\n case 'null':\n return value === null;\n case 'array':\n return Array.isArray(value);\n case 'object':\n return isPlainObject(value);\n default:\n return true;\n }\n}\n\nfunction isPlainObject(v: unknown): boolean {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction describeType(v: unknown): string {\n if (v === null) return 'null';\n if (Array.isArray(v)) return 'array';\n return typeof v;\n}\n\n/** Short serialized preview of the offending value for error messages. */\nfunction previewValue(v: unknown): string {\n try {\n const s = JSON.stringify(v);\n if (s === undefined) return String(v);\n return s.length > 80 ? `${s.slice(0, 80)}\u2026` : s;\n } catch {\n return String(v);\n }\n}\n\nfunction joinPath(parent: string, key: string): string {\n if (!parent) return key;\n return `${parent}.${key}`;\n}\n\nfunction enumIncludes(values: readonly unknown[], value: unknown): boolean {\n if (value === null || typeof value !== 'object') return values.includes(value);\n return values.some((candidate) => deepEqual(candidate, value));\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));\n }\n if (typeof a === 'object' && typeof b === 'object') {\n const ak = Object.keys(a as object);\n const bk = Object.keys(b as object);\n if (ak.length !== bk.length) return false;\n return ak.every((k) =>\n deepEqual((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),\n );\n }\n return false;\n}\n", "import type { CustomModelDefinition } from '../types/config.js';\n\n/**\n * Merge per-provider `customModels` into top-level `configModels`.\n *\n * Keys present in `configModels` always win over `providerCustomModels`\n * when the same model id appears in both places. This lets the user\n * override provider-attached definitions from the top-level config.\n *\n * Pure: never mutates its inputs.\n */\nexport function mergeCustomModelDefs(\n providerCustomModels: Record<string, CustomModelDefinition> | undefined,\n configModels: Record<string, CustomModelDefinition> | undefined,\n): Record<string, CustomModelDefinition> | undefined {\n const out: Record<string, CustomModelDefinition> = {};\n\n // Layer 1: provider-level definitions (weaker).\n if (providerCustomModels) {\n for (const [id, def] of Object.entries(providerCustomModels)) {\n out[id] = { ...def };\n }\n }\n\n // Layer 2: top-level definitions (stronger).\n if (configModels) {\n for (const [id, def] of Object.entries(configModels)) {\n out[id] = { ...def }; // top-level overwrites provider-level\n }\n }\n\n if (Object.keys(out).length === 0) return undefined;\n return out;\n}\n", "import type {\n ModelsDevModel,\n ModelsDevProvider,\n ModelsDevPayload,\n} from '../types/models-registry.js';\n\n/**\n * Deep-merge a curated `overlay` payload on top of a `base` payload (both in\n * the models.dev `api.json` shape). The overlay always wins: it can add\n * providers/models the base lacks and override fields the base gets wrong.\n *\n * Precedence rules:\n * - Provider present in both \u2192 scalar fields (`name`, `npm`, `api`, `env`,\n * `doc`) come from the overlay when set; `models` maps merge by model id.\n * - Provider only in the overlay \u2192 added wholesale.\n * - Model present in both \u2192 overlay model fields override base model fields\n * (`{ ...base, ...overlay }`), with the nested `limit` / `cost` /\n * `modalities` objects merged one level deeper so an overlay can fix just\n * `limit.context` without restating the rest of the model.\n * - Model only in the overlay \u2192 added.\n *\n * Pure: never mutates its inputs.\n */\nexport function mergeModelsPayload(\n base: ModelsDevPayload,\n overlay: ModelsDevPayload,\n): ModelsDevPayload {\n const out: ModelsDevPayload = {};\n for (const [id, provider] of Object.entries(base)) {\n out[id] = cloneProvider(provider);\n }\n for (const [id, ovProvider] of Object.entries(overlay)) {\n const existing = out[id];\n out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);\n }\n return out;\n}\n\nfunction mergeProvider(base: ModelsDevProvider, overlay: ModelsDevProvider): ModelsDevProvider {\n const models: Record<string, ModelsDevModel> = {};\n for (const [mid, m] of Object.entries(base.models ?? {})) {\n models[mid] = { ...m };\n }\n for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {\n const existing = models[mid];\n models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };\n }\n return {\n ...base,\n // Overlay scalar fields win when explicitly provided; otherwise keep base.\n ...stripUndefined({\n id: overlay.id,\n name: overlay.name,\n npm: overlay.npm,\n api: overlay.api,\n env: overlay.env,\n doc: overlay.doc,\n }),\n models,\n };\n}\n\nfunction mergeModel(base: ModelsDevModel, overlay: ModelsDevModel): ModelsDevModel {\n const merged: ModelsDevModel = { ...base, ...overlay };\n // One level deeper for the structured fields so a partial overlay (e.g. only\n // `limit.context`) doesn't blow away the base's other sub-fields.\n if (base.limit || overlay.limit) {\n merged.limit = { ...base.limit, ...overlay.limit };\n }\n if (base.cost || overlay.cost) {\n merged.cost = { ...base.cost, ...overlay.cost };\n }\n if (base.modalities || overlay.modalities) {\n merged.modalities = { ...base.modalities, ...overlay.modalities };\n }\n return merged;\n}\n\nfunction cloneProvider(p: ModelsDevProvider): ModelsDevProvider {\n const models: Record<string, ModelsDevModel> = {};\n for (const [mid, m] of Object.entries(p.models ?? {})) {\n models[mid] = { ...m };\n }\n return { ...p, models };\n}\n\n/** Drop keys whose value is `undefined` so they don't clobber base fields. */\nfunction stripUndefined<T extends Record<string, unknown>>(obj: T): Partial<T> {\n const out: Partial<T> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) out[k as keyof T] = v as T[keyof T];\n }\n return out;\n}\n", "export type NewlineStyle = 'lf' | 'crlf' | 'cr';\n\nexport function detectNewlineStyle(text: string): NewlineStyle {\n let lf = 0;\n let crlf = 0;\n let cr = 0;\n for (let i = 0; i < text.length; i++) {\n const c = text.charCodeAt(i);\n if (c === 0x0d) {\n if (text.charCodeAt(i + 1) === 0x0a) {\n crlf++;\n i++;\n } else {\n cr++;\n }\n } else if (c === 0x0a) {\n lf++;\n }\n }\n if (crlf > lf && crlf > cr) return 'crlf';\n if (cr > lf && cr > crlf) return 'cr';\n return 'lf';\n}\n\nexport function toStyle(text: string, style: NewlineStyle): string {\n const normalized = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n if (style === 'lf') return normalized;\n if (style === 'crlf') return normalized.replace(/\\n/g, '\\r\\n');\n return normalized.replace(/\\n/g, '\\r');\n}\n\nexport function normalizeToLf(text: string): string {\n return text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n}\n", "/**\n * Compile a user-supplied regex with conservative bounds against ReDoS.\n *\n * Duplicated from @wrongstack/tools/_regex.ts to avoid a circular\n * dependency (tools depends on core, not vice versa). Keep both copies\n * in sync if the heuristics change.\n *\n * V8's regex engine is backtracking-based and cannot interrupt a\n * synchronous match \u2014 a pattern like `(a+)+$` against a sufficiently\n * long line will pin a worker for seconds.\n */\n\nconst MAX_PATTERN_LEN = 512;\n\n// Heuristics for catastrophic-backtracking constructs.\nconst DANGEROUS_PATTERNS: ReadonlyArray<RegExp> = [\n /(\\([^)]*[+*][^)]*\\))[+*]/, // (a+)+, (.*)+, etc\n /(\\(\\?:[^)]*[+*][^)]*\\))[+*]/, // same, with non-capturing group\n];\n\nexport interface CompileResult {\n ok: true;\n regex: RegExp;\n}\n\nexport interface CompileFail {\n ok: false;\n reason: string;\n}\n\nexport function compileUserRegex(pattern: string, flags: string): CompileResult | CompileFail {\n if (typeof pattern !== 'string') {\n return { ok: false, reason: 'pattern must be a string' };\n }\n if (pattern.length === 0) {\n return { ok: false, reason: 'pattern is empty' };\n }\n if (pattern.length > MAX_PATTERN_LEN) {\n return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };\n }\n for (const rx of DANGEROUS_PATTERNS) {\n if (rx.test(pattern)) {\n return {\n ok: false,\n reason:\n 'pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers',\n };\n }\n }\n try {\n return { ok: true, regex: new RegExp(pattern, flags) };\n } catch (err) {\n return {\n ok: false,\n reason: err instanceof Error ? err.message : 'invalid regex',\n };\n }\n}\n", "import { toErrorMessage } from './error.js';\n\nexport interface SafeParseResult<T> {\n ok: boolean;\n value?: T | undefined;\n error?: string | undefined;\n}\n\nexport function safeParse<T = unknown>(input: string, maxBytes = 5_000_000): SafeParseResult<T> {\n if (Buffer.byteLength(input, 'utf8') > maxBytes) {\n return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };\n }\n try {\n return { ok: true, value: JSON.parse(input) as T };\n } catch (err) {\n return {\n ok: false,\n error: toErrorMessage(err),\n };\n }\n}\n\nexport function safeStringify(value: unknown, pretty = false): string {\n const seen = new WeakSet();\n const replacer = (_k: string, v: unknown): unknown => {\n if (typeof v === 'bigint') return v.toString();\n if (v instanceof Error) {\n return { name: v.name, message: v.message, stack: v.stack };\n }\n if (typeof v === 'object' && v !== null) {\n if (seen.has(v as object)) return '[Circular]';\n seen.add(v as object);\n }\n return v;\n };\n try {\n return JSON.stringify(value, replacer, pretty ? 2 : undefined) ?? 'null';\n } catch (err) {\n return JSON.stringify({\n __serialization_error: toErrorMessage(err),\n });\n }\n}\n\n/**\n * Attempt to parse JSON5-style input and return a valid JSON string.\n * Handles trailing commas, line/block comments, and unquoted keys\n * that are common in provider output.\n *\n * Returns the sanitized string if it parses successfully as JSON,\n * or `null` if the input cannot be made valid. Callers use this to\n * decide whether to proceed with the parsed result or fall back to\n * raw handling.\n */\nexport function sanitizeJsonString(s: string): string | null {\n let out = s.trim();\n\n // Stage 1: strip line and block comments outside JSON string values.\n out = stripJsonComments(out);\n\n // Stage 2: strip trailing commas before } or ]\n out = out.replace(/,(\\s*[}\\]])/g, '$1');\n\n // Stage 3: escape literal control characters that appear *inside* string\n // values. Models frequently emit raw newlines/tabs inside a code payload\n // (e.g. edit's old_string/new_string) instead of the required \\n / \\t, which\n // makes JSON.parse throw. This is the single most common malformed-args case.\n out = escapeControlCharsInStrings(out);\n\n // Stage 4: attempt full parse; return null if it fails so callers can\n // distinguish \"already valid JSON\" from \"unrecoverable\".\n try {\n JSON.parse(out);\n return out;\n } catch {\n return null; // stripped but still not valid JSON; caller handles it\n }\n}\n\n/**\n * Strip a Markdown code-fence wrapper from a payload.\n *\n * Models occasionally return tool-call arguments wrapped in ```json fences\n * (or embedded in prose around one) instead of bare JSON. Returns the inner\n * content when the input starts with a fence (closing fence optional, so a\n * truncated stream still unwraps) or contains one complete fenced block;\n * returns null when no fence is present. Callers should only invoke this\n * after a direct parse failed, so fences inside legitimate string values are\n * never touched.\n */\nexport function stripCodeFences(s: string): string | null {\n const trimmed = s.trim();\n // Whole-payload fence: ```lang? \u2026 ```? (closer optional for truncation)\n const opener = /^```[\\w+-]*[ \\t]*\\r?\\n?/.exec(trimmed);\n if (opener) {\n const inner = trimmed.slice(opener[0].length).replace(/(\\r?\\n)?[ \\t]*```[ \\t]*$/, '');\n return inner.trim();\n }\n // Fence embedded in prose: extract the first complete fenced block.\n const embedded = /```[\\w+-]*[ \\t]*\\r?\\n([\\s\\S]*?)\\r?\\n[ \\t]*```/.exec(trimmed);\n if (embedded) return (embedded[1] ?? '').trim();\n return null;\n}\n\n/**\n * Walk the string tracking whether we are inside a JSON string literal and\n * replace raw control characters (U+0000\u2013U+001F) that appear inside strings\n * with their valid JSON escape sequences. Characters outside strings are left\n * untouched (insignificant whitespace stays as-is). Already-escaped sequences\n * are not double-escaped because we only act on *literal* control bytes.\n */\nfunction escapeControlCharsInStrings(s: string): string {\n let inString = false;\n let out = '';\n for (let i = 0; i < s.length; i++) {\n const c = s.charAt(i);\n if (c === '\"' && (i === 0 || s[i - 1] !== '\\\\')) {\n inString = !inString;\n out += c;\n continue;\n }\n const code = c.charCodeAt(0);\n if (inString && code < 0x20) {\n switch (c) {\n case '\\n':\n out += '\\\\n';\n break;\n case '\\r':\n out += '\\\\r';\n break;\n case '\\t':\n out += '\\\\t';\n break;\n case '\\b':\n out += '\\\\b';\n break;\n case '\\f':\n out += '\\\\f';\n break;\n default:\n out += `\\\\u${code.toString(16).padStart(4, '0')}`;\n }\n continue;\n }\n out += c;\n }\n return out;\n}\n\nfunction stripJsonComments(s: string): string {\n let inString = false;\n let escaped = false;\n const chars: string[] = [];\n let i = 0;\n\n while (i < s.length) {\n const c = s.charAt(i);\n\n if (inString) {\n chars.push(c);\n if (escaped) {\n escaped = false;\n } else if (c === '\\\\') {\n escaped = true;\n } else if (c === '\"') {\n inString = false;\n }\n i++;\n continue;\n }\n\n if (c === '\"') {\n inString = true;\n chars.push(c);\n i++;\n continue;\n }\n\n if (c === '/' && s.charAt(i + 1) === '/') {\n while (i < s.length && s.charAt(i) !== '\\n') i++;\n continue;\n }\n\n if (c === '/' && s.charAt(i + 1) === '*') {\n const end = s.indexOf('*/', i + 2);\n if (end === -1) {\n // Preserve an unterminated opener so the final JSON.parse rejects it.\n chars.push(s.slice(i));\n break;\n }\n i = end + 2;\n continue;\n }\n\n chars.push(c);\n i++;\n }\n\n return chars.join('');\n}\n", "import * as path from 'node:path';\nimport { ERROR_CODES, FsError } from '../types/errors.js';\n\n/**\n * Resolve `<dir>/<sessionId><suffix>` for per-session sidecar files\n * (annotations, audit chain, replay log, the session JSONL itself).\n *\n * Modern session ids are date-sharded (\"2026-06-11/sess_<ULID>\"),\n * so a forward slash is a legitimate shard separator \u2014 NOT traversal.\n * Escape attempts are blocked two ways: an explicit ban on `..` and\n * backslashes, plus a resolved-path containment check that rejects any\n * id whose resolved target leaves `dir`. Character bans alone are how\n * several stores ended up throwing on every modern session id.\n */\nexport function sessionScopedPath(dir: string, sessionId: string, suffix: string): string {\n if (!sessionId || sessionId.includes('\\\\') || sessionId.includes('..')) {\n throw invalid(sessionId);\n }\n const resolved = path.resolve(dir, `${sessionId}${suffix}`);\n const rel = path.relative(path.resolve(dir), resolved);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw invalid(sessionId);\n }\n return resolved;\n}\n\nfunction invalid(sessionId: string): FsError {\n return new FsError({\n message: `Invalid sessionId: ${sessionId}`,\n code: ERROR_CODES.FS_DELETE_FAILED,\n path: sessionId,\n context: { reason: 'path_traversal' },\n });\n}\n", "/** Resolve a promise after `ms` milliseconds. Prefer this over raw\n * `setTimeout` wrappers so all delay sites use a single implementation\n * and an abortable variant can be introduced without a codebase-wide hunt. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n", "/**\n * Turn an arbitrary string into a filesystem- and URL-safe lowercase slug.\n *\n * Collapses every run of non-alphanumeric characters into a single hyphen,\n * trims leading/trailing hyphens, and caps the length. Returns `fallback`\n * when the input slugifies to the empty string.\n *\n * Used as the stable dedup + registry key for prompts. (Distinct from the\n * project-folder slug in `wstack-paths.ts`, which has its own `'project'`\n * fallback and shorter cap.)\n */\nexport function slugify(name: string, fallback = 'prompt', maxLen = 64): string {\n return (\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, maxLen)\n .replace(/-+$/g, '') || fallback\n );\n}\n", "/**\n * String utilities shared across the WrongStack codebase.\n */\n\n/**\n * Truncate a string to at most `max` characters, appending an ellipsis if it\n * was longer. Returns the original string unchanged when it fits.\n */\nexport function truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;\n}\n", "import type {\n TaskPriority,\n TaskStatus,\n TaskType,\n TaskProgress as TaskGraphProgress,\n} from '../types/task-graph.js';\nimport { color } from './color.js';\n\n// Re-export graph types for convenience\nexport type { TaskStatus, TaskPriority, TaskType };\n\n// ---------------------------------------------------------------------------\n// Session-level task item \u2014 mirrors TaskNode but with string timestamps\n// for JSON serialization and a flat-list structure (no graph edges).\n// ---------------------------------------------------------------------------\n\nexport interface TaskItem {\n id: string;\n title: string;\n description?: string | undefined;\n type: TaskType;\n priority: TaskPriority;\n status: TaskStatus;\n /** IDs of tasks this one depends on. */\n dependsOn?: string[] | undefined;\n /** Agent/subagent name assigned to this task. */\n assignee?: string | undefined;\n estimateHours?: number | undefined;\n tags?: string[] | undefined;\n createdAt: string;\n updatedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Progress (re-export computeTaskItemProgress adapted for TaskItem[])\n// ---------------------------------------------------------------------------\n\nexport function computeTaskItemProgress(tasks: TaskItem[]): TaskGraphProgress {\n let completed = 0;\n let pending = 0;\n let inProgress = 0;\n let blocked = 0;\n let failed = 0;\n let review = 0;\n let estimatedHours = 0;\n const actualHours = 0;\n for (const t of tasks) {\n switch (t.status) {\n case 'completed':\n completed++;\n break;\n case 'pending':\n pending++;\n break;\n case 'in_progress':\n inProgress++;\n break;\n case 'blocked':\n blocked++;\n break;\n case 'failed':\n failed++;\n break;\n case 'review':\n review++;\n break;\n }\n estimatedHours += t.estimateHours ?? 0;\n }\n return {\n total: tasks.length,\n pending,\n inProgress,\n blocked,\n failed,\n review,\n completed,\n percentComplete: tasks.length > 0 ? Math.round((completed / tasks.length) * 100) : 0,\n estimatedHours,\n actualHours,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Icons\n// ---------------------------------------------------------------------------\n\nconst STATUS_ICON: Record<TaskStatus, string> = {\n pending: '\u25CB',\n in_progress: '\u25D0',\n blocked: '\u2298',\n failed: '\u2717',\n review: '\u25D1',\n completed: '\u25CF',\n};\n\nconst PRIORITY_ICON: Record<TaskPriority, string> = {\n critical: '\uD83D\uDD34',\n high: '\uD83D\uDFE0',\n medium: '\uD83D\uDFE1',\n low: '\uD83D\uDFE2',\n};\n\nconst TYPE_ICON: Record<TaskType, string> = {\n feature: '\u26A1',\n bugfix: '\uD83D\uDC1B',\n refactor: '\u267B\uFE0F',\n docs: '\uD83D\uDCDD',\n test: '\uD83E\uDDEA',\n chore: '\uD83D\uDD27',\n};\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\nexport function formatTaskProgress(tasks: TaskItem[]): string {\n const p = computeTaskItemProgress(tasks);\n if (p.total === 0) return 'No tasks.';\n const barWidth = 24;\n const filled = Math.round((p.percentComplete / 100) * barWidth);\n const empty = barWidth - filled;\n const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);\n return [\n `${color.bold('Tasks')} [${bar}] ${p.percentComplete}%`,\n ` ${color.green('\u25CF')} ${p.completed} done \u2502 ${color.yellow('\u25D0')} ${p.inProgress} active \u2502 ${color.dim('\u25CB')} ${p.pending} pending \u2502 \u2298 ${p.blocked} blocked \u2502 \u2717 ${p.failed} failed`,\n p.estimatedHours > 0\n ? ` ${color.dim(`est. ${p.estimatedHours}h`)}`\n : '',\n ]\n .filter(Boolean)\n .join('\\n');\n}\n\nexport function formatTaskList(tasks: TaskItem[]): string {\n if (tasks.length === 0) return 'No tasks.';\n\n // Group by status\n const order: TaskStatus[] = ['in_progress', 'blocked', 'review', 'pending', 'failed', 'completed'];\n const groups = new Map<TaskStatus, TaskItem[]>();\n for (const t of tasks) {\n const list = groups.get(t.status) ?? [];\n list.push(t);\n groups.set(t.status, list);\n }\n\n const lines: string[] = [];\n lines.push(color.dim(`Tasks (${tasks.length} total):`));\n\n for (const status of order) {\n const group = groups.get(status);\n if (!group || group.length === 0) continue;\n const icon = STATUS_ICON[status];\n lines.push(` ${icon} ${status.toUpperCase()} (${group.length})`);\n for (const t of group) {\n const prio = PRIORITY_ICON[t.priority];\n const type = TYPE_ICON[t.type];\n const deps =\n t.dependsOn && t.dependsOn.length > 0\n ? ` ${color.dim('\u2190')} ${color.dim(t.dependsOn.map((d) => d.slice(0, 8)).join(', '))}`\n : '';\n const who = t.assignee ? ` ${color.dim(`@${t.assignee}`)}` : '';\n const hrs = t.estimateHours ? ` ${color.dim(`${t.estimateHours}h`)}` : '';\n lines.push(` ${type} ${prio} ${t.title}${deps}${who}${hrs}`);\n }\n }\n\n return lines.join('\\n');\n}\n", "import type { TodoItem } from '../core/context.js';\nimport { color } from './color.js';\n\n/**\n * Canonical text rendering of the live todo list, shared by the CLI's\n * `/todos` slash command and the TUI's auto-echo (which prints the same\n * snapshot to chat history each time the `todo` tool mutates the list).\n *\n * Layout: a header line with the `done/total done` count, then one row\n * per item \u2014 `[ ]` pending, `[~]` in-progress, `[x]` completed. In-\n * progress rows prefer `activeForm` (\"Building the project\") over the\n * imperative `content` (\"Build the project\") when present.\n *\n * Returned as a single newline-joined string so callers can hand it\n * straight to a history dispatcher or stdout.\n */\nexport function formatTodosList(todos: TodoItem[]): string {\n if (todos.length === 0) return 'No todos.';\n const lines: string[] = [];\n const done = todos.filter((t) => t.status === 'completed').length;\n lines.push(color.dim(`Todos (${done}/${todos.length} done):`));\n todos.forEach((t, i) => {\n const mark =\n t.status === 'completed'\n ? color.green('[x]')\n : t.status === 'in_progress'\n ? color.yellow('[~]')\n : color.dim('[ ]');\n const text = t.status === 'in_progress' && t.activeForm ? t.activeForm : t.content;\n const label = t.status === 'completed' ? color.dim(text) : text;\n lines.push(` ${color.dim(String(i + 1).padStart(2))}. ${mark} ${label}`);\n });\n return lines.join('\\n');\n}\n\n/**\n * True when the todos list still has at least one unfinished item \u2014 either\n * `pending` (not started) or `in_progress` (underway). The REPL and other\n * post-turn handlers call this to decide whether to surface `<nextsteps>`\n * suggestions to the user: as long as the agent has open todos, finishing\n * them takes priority over offering new prompt options. Surfacing\n * `<nextsteps>` mid-task is what causes YOLO+auto mode and the autonomy\n * 'auto' loop to prematurely pivot away from the in-flight todo list.\n *\n * Returns false for an empty / undefined list (nothing pending, nothing to\n * block on) and false for an all-completed list. Treats any non-array input\n * (legacy contexts, mocks) as \"no todos\" rather than throwing.\n */\nexport function hasOpenTodos(todos: readonly TodoItem[] | undefined | null): boolean {\n if (!Array.isArray(todos) || todos.length === 0) return false;\n return todos.some((t) => t.status === 'pending' || t.status === 'in_progress');\n}\n", "import type { ToolDescriptionMode, ToolDescriptionModeConfig } from '../types/config.js';\nimport type { Tool } from '../types/tool.js';\n\nexport const DEFAULT_TOOL_DESCRIPTION_MODE: ToolDescriptionMode = 'extend';\n\nconst ORIGINAL_TOOL_DESCRIPTION = Symbol.for('wrongstack.tool.originalDescription');\n\ninterface OriginalToolDescription {\n description: string;\n usageHint?: string | undefined;\n}\n\ntype ToolWithOriginalDescription = Tool & {\n [ORIGINAL_TOOL_DESCRIPTION]?: OriginalToolDescription | undefined;\n};\n\nexport interface ToolDescriptionRegistryLike {\n get(name: string): Tool | undefined;\n list(): Tool[];\n wrap?(name: string, wrapper: (tool: Tool) => Tool, owner?: string): void;\n setDescriptionMode?(name: string, mode: ToolDescriptionMode): boolean;\n applyDescriptionModes?(\n modes?: ToolDescriptionModeConfig,\n ): { applied: number; missing: string[] };\n getDescriptionMode?(name: string): ToolDescriptionMode;\n}\n\nexport function normalizeToolDescriptionMode(value: unknown): ToolDescriptionMode | undefined {\n if (typeof value !== 'string') return undefined;\n const raw = value.trim().toLowerCase();\n if (raw === 'extend' || raw === 'extended' || raw === 'full') return 'extend';\n if (raw === 'simple' || raw === 'short' || raw === 'brief') return 'simple';\n return undefined;\n}\n\nexport function resolveToolDescriptionMode(\n modes: ToolDescriptionModeConfig | undefined,\n toolName: string,\n): ToolDescriptionMode {\n return normalizeToolDescriptionMode(modes?.[toolName]) ?? DEFAULT_TOOL_DESCRIPTION_MODE;\n}\n\nexport function simplifyToolDescription(\n text: string,\n opts: { maxSentences?: number | undefined; maxChars?: number | undefined } = {},\n): string {\n const maxSentences = Math.max(1, opts.maxSentences ?? 2);\n const maxChars = Math.max(40, opts.maxChars ?? 180);\n const normalized = text\n .replace(/\\r\\n?/g, '\\n')\n .split('\\n')\n .map((line) => line.trim())\n .filter(Boolean)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n if (normalized.length <= maxChars) return normalized;\n\n const sentences = normalized.match(/[^.!?]+[.!?]+(?=\\s|$)|[^.!?]+$/g) ?? [normalized];\n const selected: string[] = [];\n for (const sentence of sentences) {\n selected.push(sentence.trim());\n const candidate = selected.join(' ');\n if (selected.length >= maxSentences || candidate.length >= maxChars) break;\n }\n\n /* v8 ignore next -- defensive: selected always has \u22651 non-empty sentence so summary is never falsy */\n const summary = selected.join(' ').trim() || normalized;\n if (summary.length <= maxChars) return summary;\n\n const hardLimit = maxChars - 4;\n const boundary = findWordBoundary(summary, hardLimit);\n /* v8 ignore next -- findWordBoundary always returns >0 (semantic/space/limit floor), so the hardLimit fallback is dead */\n return `${summary.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd()} ...`;\n}\n\nexport function applyToolDescriptionModeToTool(\n tool: Tool,\n mode: ToolDescriptionMode,\n): Tool {\n const existingOriginal = getOriginalDescription(tool);\n if (mode === 'extend' && !existingOriginal) return tool;\n\n const original = existingOriginal ?? {\n description: tool.description,\n usageHint: tool.usageHint,\n };\n\n const next =\n mode === 'simple'\n ? withDescription(tool, {\n description: simplifyToolDescription(original.description),\n usageHint:\n original.usageHint === undefined\n ? undefined\n : simplifyToolDescription(original.usageHint),\n })\n : withDescription(tool, original);\n\n return attachOriginalDescription(next, original);\n}\n\nexport function setToolDescriptionMode(\n registry: ToolDescriptionRegistryLike,\n name: string,\n mode: ToolDescriptionMode,\n): boolean {\n if (typeof registry.setDescriptionMode === 'function') {\n return registry.setDescriptionMode(name, mode);\n }\n if (!registry.get(name) || typeof registry.wrap !== 'function') return false;\n registry.wrap(\n name,\n (tool) => applyToolDescriptionModeToTool(tool, mode),\n 'tool-description-mode',\n );\n return true;\n}\n\nexport function getToolDescriptionMode(\n registry: ToolDescriptionRegistryLike,\n name: string,\n): ToolDescriptionMode {\n return registry.getDescriptionMode?.(name) ?? DEFAULT_TOOL_DESCRIPTION_MODE;\n}\n\nexport function applyToolDescriptionModes(\n registry: ToolDescriptionRegistryLike,\n modes?: ToolDescriptionModeConfig,\n): { applied: number; missing: string[] } {\n if (typeof registry.applyDescriptionModes === 'function') {\n return registry.applyDescriptionModes(modes);\n }\n\n const entries = Object.entries(modes ?? {});\n const missing: string[] = [];\n let applied = 0;\n for (const [name, rawMode] of entries) {\n const mode = normalizeToolDescriptionMode(rawMode);\n if (!mode) continue;\n if (setToolDescriptionMode(registry, name, mode)) applied++;\n else missing.push(name);\n }\n return { applied, missing };\n}\n\nfunction getOriginalDescription(tool: Tool): OriginalToolDescription | undefined {\n return (tool as ToolWithOriginalDescription)[ORIGINAL_TOOL_DESCRIPTION];\n}\n\nfunction attachOriginalDescription(tool: Tool, original: OriginalToolDescription): Tool {\n Object.defineProperty(tool, ORIGINAL_TOOL_DESCRIPTION, {\n configurable: true,\n enumerable: false,\n value: original,\n writable: true,\n });\n return tool;\n}\n\nfunction withDescription(tool: Tool, next: OriginalToolDescription): Tool {\n const copy: Tool = {\n ...tool,\n description: next.description,\n usageHint: next.usageHint,\n };\n if (next.usageHint === undefined) {\n delete (copy as { usageHint?: string | undefined }).usageHint;\n }\n return copy;\n}\n\nfunction findWordBoundary(text: string, limit: number): number {\n const semantic = Math.max(\n text.lastIndexOf('. ', limit),\n text.lastIndexOf('; ', limit),\n text.lastIndexOf(', ', limit),\n );\n if (semantic > 40) return semantic + 1;\n const space = text.lastIndexOf(' ', limit);\n return space > 40 ? space : limit;\n}\n", "/**\n * Tool output serialization utilities.\n * Extracted from Agent.executeTools to allow reuse and consistent output handling.\n */\n\nexport interface ToolOutputSerializerOptions {\n perIterationOutputCapBytes?: number | undefined;\n estimator?: ((text: string) => number) | undefined;\n}\n\nexport interface ToolOutputSerializeContext {\n toolName?: string | undefined;\n input?: unknown;\n /**\n * Optional reference to the Tool object. When present and the tool defines\n * a `serialize()` method, the serializer delegates to it instead of the\n * central `renderToolObject()` switch (P3 #21).\n */\n tool?: { serialize?: (output: unknown, input: unknown) => string } | undefined;\n}\n\ntype RecordValue = Record<string, unknown>;\n\nconst DEFAULT_LIST_LIMIT = 500;\nconst LOG_ENTRY_LIMIT = 200;\nconst INLINE_LIMIT = 240;\nconst GREP_FILE_LIMIT = 80;\nconst GREP_MATCHES_PER_FILE = 3;\nconst DIFF_INLINE_LINE_LIMIT = 260;\nconst DIFF_HUNK_LIMIT = 8;\nconst DIFF_HUNK_CONTEXT = 14;\n\n// Pre-compiled regex \u2014 used in parseGrepContentLine() for every grep match line.\n// Compiling once at module load avoids repeated RegExp construction overhead.\nconst GREP_LINE_RE = /^(.+?):(\\d+):(.*)$/;\n\nexport function createToolOutputSerializer(opts: ToolOutputSerializerOptions = {}) {\n const capBytes = opts.perIterationOutputCapBytes ?? 100_000;\n\n function serialize(value: unknown, context: ToolOutputSerializeContext = {}): string {\n if (typeof value === 'string') return value;\n if (value === null || value === undefined) return '';\n if (typeof value === 'object') {\n if (Array.isArray(value)) return value.map((item) => serialize(item)).join('\\n');\n // P3 #21 (before-release.md): prefer the tool's own serialize() method\n // when it defines one \u2014 lets tools own their output formatting without\n // adding a branch to the central renderToolObject() god function.\n if (context.tool?.serialize) {\n try {\n return context.tool.serialize(value, context.input);\n } catch {\n // Fall through to the central renderer if the tool's serializer\n // throws \u2014 never let a formatting error break the tool result.\n }\n }\n if (context.toolName) {\n const compact = renderToolObject(context.toolName, value as RecordValue, context.input);\n if (compact !== undefined) return compact;\n return renderGenericToolObject(context.toolName, value as RecordValue);\n }\n if ('text' in (value as Record<string, unknown>)) {\n const t = (value as Record<string, unknown>).text;\n return typeof t === 'string' ? t : JSON.stringify(value, null, 2);\n }\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }\n return String(value);\n }\n\n function enforceCap(text: string, remainingBudget: number): { text: string; newBudget: number } {\n if (remainingBudget <= 0) {\n return { text: '[truncated: iteration output cap exceeded]', newBudget: 0 };\n }\n const textBytes = Buffer.byteLength(text, 'utf8');\n if (textBytes <= remainingBudget) {\n return { text, newBudget: remainingBudget - textBytes };\n }\n const marker = `\\n\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026\\n`;\n const markerBytes = Buffer.byteLength(marker, 'utf8');\n const available = remainingBudget - markerBytes;\n if (available <= 0) {\n return { text: '[truncated: iteration output cap exceeded]', newBudget: 0 };\n }\n const half = Math.floor(available / 2);\n const first = text.slice(0, half);\n const second = text.slice(text.length - half);\n return { text: `${first}${marker}${second}`, newBudget: 0 };\n }\n\n return { serialize, enforceCap, capBytes };\n}\n\nfunction renderToolObject(toolName: string, obj: RecordValue, input: unknown): string | undefined {\n if (toolName === 'read' && typeof obj['text'] === 'string') {\n return joinSections([\n renderHeader(\n `read: ${stringFromInput(input, 'path') ?? stringField(obj, 'path') ?? '<unknown>'}`,\n {\n offset: numberFromInput(input, 'offset'),\n limit: numberFromInput(input, 'limit'),\n total_lines: obj['total_lines'],\n encoding: obj['encoding'],\n truncated: obj['truncated'],\n cached: obj['cached'],\n note: obj['note'],\n },\n ),\n obj['text'],\n ]);\n }\n\n if (toolName === 'grep' && Array.isArray(obj['matches'])) {\n const matches = stringArrayField(obj, 'matches');\n return joinSections([\n renderHeader(`grep: ${stringFromInput(input, 'pattern') ?? '<pattern>'}`, {\n path: stringFromInput(input, 'path'),\n glob: stringFromInput(input, 'glob'),\n mode: stringFromInput(input, 'output_mode'),\n count: obj['count'],\n shown: matches.length,\n truncated: obj['truncated'],\n used: obj['used'],\n }),\n renderGrepMatches(matches, stringFromInput(input, 'output_mode')),\n ]);\n }\n\n if (toolName === 'patch' && Array.isArray(obj['files'])) {\n const files = stringArrayField(obj, 'files');\n return joinSections([\n renderHeader('patch', {\n applied: obj['applied'],\n rejected: obj['rejected'],\n files: files.length,\n dry_run: obj['dry_run'],\n }),\n typeof obj['message'] === 'string' ? `message:\\n${obj['message']}` : undefined,\n files.length > 0 ? `files:\\n${renderStringList(files)}` : undefined,\n ]);\n }\n\n if (toolName === 'glob' && Array.isArray(obj['files'])) {\n const files = stringArrayField(obj, 'files');\n return joinSections([\n renderHeader(\n `${toolName}: ${stringFromInput(input, 'pattern') ?? stringFromInput(input, 'files') ?? stringFromInput(input, 'path') ?? ''}`.trim(),\n {\n path: stringFromInput(input, 'path'),\n files: files.length,\n truncated: obj['truncated'],\n },\n ),\n renderStringList(files, '(no files)'),\n ]);\n }\n\n if (toolName === 'tree' && typeof obj['tree'] === 'string') {\n return joinSections([\n renderHeader(\n `tree: ${stringField(obj, 'path') ?? stringFromInput(input, 'path') ?? '<cwd>'}`,\n {\n total_files: obj['total_files'],\n total_dirs: obj['total_dirs'],\n truncated: obj['truncated'],\n },\n ),\n obj['tree'],\n ]);\n }\n\n if (toolName === 'fetch' && typeof obj['content'] === 'string') {\n return joinSections([\n renderHeader(\n `fetch: ${stringField(obj, 'url') ?? stringFromInput(input, 'url') ?? '<url>'}`,\n {\n status: obj['status'],\n content_type: obj['content_type'],\n },\n ),\n obj['content'],\n ]);\n }\n\n if (toolName === 'replace' && Array.isArray(obj['results'])) {\n const results = obj['results'].filter(isRecord);\n const sections: Array<string | undefined> = [\n renderHeader('replace', {\n files_modified: obj['files_modified'],\n total_replacements: obj['total_replacements'],\n dry_run: obj['dry_run'],\n }),\n ];\n for (const r of results.slice(0, DEFAULT_LIST_LIMIT)) {\n sections.push(\n joinSections([\n renderHeader(`file: ${stringField(r, 'path') ?? '<unknown>'}`, {\n replacements: r['replacements'],\n }),\n typeof r['diff'] === 'string' ? r['diff'] : undefined,\n ]),\n );\n }\n if (results.length > DEFAULT_LIST_LIMIT) {\n sections.push(`[serializer omitted ${results.length - DEFAULT_LIST_LIMIT} result item(s)]`);\n }\n return joinSections(sections);\n }\n\n if (typeof obj['diff'] === 'string') {\n const diff = obj['diff'];\n // matched_by: 'exact' is the default and carries no information \u2014 only\n // surface the field when a fallback tier actually fired.\n const matchedBy =\n typeof obj['matched_by'] === 'string' && obj['matched_by'] !== 'exact'\n ? obj['matched_by']\n : undefined;\n const syntaxErrors = Array.isArray(obj['syntax_errors'])\n ? obj['syntax_errors'].filter((e): e is string => typeof e === 'string')\n : [];\n return joinSections([\n renderHeader(toolName, {\n path: obj['path'],\n replacements: obj['replacements'],\n bytes_written: obj['bytes_written'],\n created: obj['created'],\n matched_by: matchedBy,\n note: obj['note'],\n files: Array.isArray(obj['files']) ? obj['files'].length : undefined,\n truncated: obj['truncated'],\n mode: obj['mode'],\n }),\n compactDiff(diff),\n syntaxErrors.length > 0 ? `syntax_errors:\\n${renderStringList(syntaxErrors)}` : undefined,\n ]);\n }\n\n if (toolName === 'test' && typeof obj['output'] === 'string') {\n return renderTestOutput(obj, input);\n }\n\n if (\n (toolName === 'typecheck' || toolName === 'lint' || toolName === 'format') &&\n typeof obj['output'] === 'string'\n ) {\n return renderVerifierOutput(toolName, obj, input);\n }\n\n if (hasCommandOutputShape(obj)) {\n return renderCommandOutput(toolName, obj, input);\n }\n\n if (toolName === 'json' && typeof obj['formatted'] === 'string') {\n return joinSections([\n renderHeader('json', {\n type: obj['type'],\n keys: Array.isArray(obj['keys']) ? obj['keys'].length : undefined,\n query: stringFromInput(input, 'query'),\n error: obj['error'],\n }),\n obj['formatted'],\n ]);\n }\n\n if (toolName === 'logs' && Array.isArray(obj['entries'])) {\n const entries = obj['entries'].filter(isRecord);\n const lines = entries.slice(0, LOG_ENTRY_LIMIT).map((entry) => {\n const ts = stringField(entry, 'timestamp') ?? '';\n const level = stringField(entry, 'level') ?? 'info';\n const message = stringField(entry, 'message') ?? '';\n const source = stringField(entry, 'source');\n return [ts, level, source, message].filter(Boolean).join(' ');\n });\n if (entries.length > LOG_ENTRY_LIMIT) {\n lines.push(`[serializer omitted ${entries.length - LOG_ENTRY_LIMIT} log entry item(s)]`);\n }\n return joinSections([\n renderHeader(`logs: ${stringField(obj, 'source') ?? '<source>'}`, {\n total: obj['total'],\n shown: Math.min(entries.length, LOG_ENTRY_LIMIT),\n truncated: obj['truncated'],\n stream_mode: obj['stream_mode'],\n }),\n lines.length > 0 ? lines.join('\\n') : '(no log entries)',\n ]);\n }\n\n if (toolName === 'audit' && Array.isArray(obj['vulnerabilities'])) {\n const vulns = obj['vulnerabilities'].filter(isRecord);\n const lines = vulns.slice(0, DEFAULT_LIST_LIMIT).map((v) => {\n const severity = stringField(v, 'severity') ?? 'unknown';\n const pkg = stringField(v, 'package') ?? '<package>';\n const title = stringField(v, 'title') ?? '';\n const url = stringField(v, 'url');\n return [severity, pkg, title, url].filter(Boolean).join(' | ');\n });\n if (vulns.length > DEFAULT_LIST_LIMIT) {\n lines.push(`[serializer omitted ${vulns.length - DEFAULT_LIST_LIMIT} vulnerability item(s)]`);\n }\n return joinSections([\n renderHeader('audit', {\n exit_code: obj['exit_code'],\n total: obj['total'],\n summary: obj['summary'],\n truncated: obj['truncated'],\n }),\n lines.length > 0 ? lines.join('\\n') : stringField(obj, 'output'),\n ]);\n }\n\n if (toolName === 'outdated' && Array.isArray(obj['packages'])) {\n const packages = obj['packages'].filter(isRecord);\n const lines = packages\n .slice(0, DEFAULT_LIST_LIMIT)\n .map((p) =>\n [\n stringField(p, 'name') ?? '<package>',\n `current=${stringField(p, 'current') ?? 'unknown'}`,\n `wanted=${stringField(p, 'wanted') ?? 'unknown'}`,\n `latest=${stringField(p, 'latest') ?? 'unknown'}`,\n stringField(p, 'type'),\n ]\n .filter(Boolean)\n .join(' | '),\n );\n if (packages.length > DEFAULT_LIST_LIMIT) {\n lines.push(`[serializer omitted ${packages.length - DEFAULT_LIST_LIMIT} package item(s)]`);\n }\n return joinSections([\n renderHeader('outdated', {\n exit_code: obj['exit_code'],\n total: obj['total'],\n truncated: obj['truncated'],\n }),\n lines.length > 0 ? lines.join('\\n') : stringField(obj, 'output'),\n ]);\n }\n\n return undefined;\n}\n\nfunction renderTestOutput(obj: RecordValue, input: unknown): string {\n const exitCode = numberField(obj, 'exit_code') ?? 0;\n const failed = numberField(obj, 'failed') ?? 0;\n const output = stringField(obj, 'output') ?? '';\n const header = renderHeader(`test: ${stringField(obj, 'runner') ?? 'runner'}`, {\n exit_code: obj['exit_code'],\n tests_run: obj['tests_run'],\n passed: obj['passed'],\n failed: obj['failed'],\n duration_ms: obj['duration_ms'],\n truncated: obj['truncated'],\n files: inputListSummary(input, 'files'),\n grep: stringFromInput(input, 'grep'),\n });\n\n if (exitCode === 0 && failed === 0) {\n return joinSections([\n header,\n joinSections([\n 'report:',\n `status=passed`,\n `tests_run=${obj['tests_run'] ?? 0}`,\n `passed=${obj['passed'] ?? 0}`,\n `failed=${obj['failed'] ?? 0}`,\n `duration_ms=${obj['duration_ms'] ?? 0}`,\n extractSpoolNote(output),\n ]),\n ]);\n }\n\n return joinSections([\n header,\n `error_context:\\n${compactFailureOutput(output || '(no runner output)')}`,\n ]);\n}\n\nfunction renderVerifierOutput(toolName: string, obj: RecordValue, input: unknown): string {\n const exitCode = numberField(obj, 'exit_code') ?? 0;\n const errors = numberField(obj, 'errors') ?? 0;\n const warnings = numberField(obj, 'warnings') ?? 0;\n const output = stringField(obj, 'output') ?? '';\n const changed = numberField(obj, 'files_changed') ?? 0;\n const header = renderHeader(toolName, {\n exit_code: obj['exit_code'],\n errors: obj['errors'],\n warnings: obj['warnings'],\n files_checked: obj['files_checked'],\n files_changed: obj['files_changed'],\n fix_applied: obj['fix_applied'],\n fixer: obj['fixer'],\n linter: obj['linter'],\n project: obj['project'],\n truncated: obj['truncated'],\n files: inputListSummary(input, 'files'),\n cwd: stringFromInput(input, 'cwd'),\n });\n\n if (exitCode === 0 && errors === 0 && (toolName !== 'format' || changed === 0)) {\n return joinSections([\n header,\n joinSections([\n 'report:',\n 'status=passed',\n `errors=${errors}`,\n `warnings=${warnings}`,\n toolName === 'format' ? `files_changed=${changed}` : undefined,\n extractSpoolNote(output),\n ]),\n ]);\n }\n\n if (exitCode === 0 && toolName === 'format') {\n return joinSections([\n header,\n joinSections([\n 'report:',\n 'status=changed',\n `files_changed=${changed}`,\n extractSpoolNote(output),\n ]),\n ]);\n }\n\n return joinSections([\n header,\n `error_context:\\n${compactFailureOutput(output || '(no verifier output)')}`,\n ]);\n}\n\nfunction renderGrepMatches(matches: string[], mode: string | undefined): string {\n if (matches.length === 0) return '(no matches)';\n if (mode === 'files_with_matches') return renderStringList(matches, '(no files)');\n if (mode === 'count') return renderStringList(matches, '(no counts)');\n\n const groups = new Map<string, string[]>();\n const passthrough: string[] = [];\n for (const match of matches) {\n const parsed = parseGrepContentLine(match);\n if (!parsed) {\n passthrough.push(match);\n continue;\n }\n const list = groups.get(parsed.file) ?? [];\n list.push(`${parsed.line}:${parsed.text}`);\n groups.set(parsed.file, list);\n }\n\n if (groups.size === 0) return renderStringList(matches, '(no matches)');\n\n const sections: string[] = [];\n let fileIndex = 0;\n for (const [file, lines] of groups) {\n fileIndex++;\n if (fileIndex > GREP_FILE_LIMIT) break;\n const shown = lines.slice(0, GREP_MATCHES_PER_FILE);\n sections.push(\n `${file} (${lines.length} match(es), showing ${shown.length})\\n${shown.join('\\n')}`,\n );\n }\n if (groups.size > GREP_FILE_LIMIT) {\n sections.push(`[serializer omitted ${groups.size - GREP_FILE_LIMIT} file group(s)]`);\n }\n if (passthrough.length > 0) {\n sections.push(`ungrouped:\\n${renderStringList(passthrough, '', 50)}`);\n }\n return sections.join('\\n');\n}\n\nfunction parseGrepContentLine(\n line: string,\n): { file: string; line: string; text: string } | undefined {\n const match = GREP_LINE_RE.exec(line);\n if (!match?.[1] || !match[2]) return undefined;\n return { file: match[1], line: match[2], text: match[3] ?? '' };\n}\n\nfunction compactDiff(diff: string): string {\n const lines = diff.split(/\\r?\\n/);\n if (lines.length <= DIFF_INLINE_LINE_LIMIT) return diff;\n\n const fileCount = Math.max(\n new Set(\n lines\n .map(\n (line) => /^diff --git\\s+a\\/(.+?)\\s+b\\//.exec(line)?.[1] ?? /^---\\s+(.+)/.exec(line)?.[1],\n )\n .filter(Boolean),\n ).size,\n 0,\n );\n const hunks = lines.filter((line) => line.startsWith('@@')).length;\n const added = lines.filter((line) => line.startsWith('+') && !line.startsWith('+++')).length;\n const removed = lines.filter((line) => line.startsWith('-') && !line.startsWith('---')).length;\n\n // Collect [start, end] intervals as we scan lines sequentially.\n // Intervals are naturally ordered by line index \u2014 no sort needed.\n const intervals: Array<[number, number]> = [];\n let hunkCount = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n if (line.startsWith('diff --git') || line.startsWith('--- ') || line.startsWith('+++ ')) {\n intervals.push([i, i]);\n continue;\n }\n if (!line.startsWith('@@')) continue;\n if (hunkCount >= DIFF_HUNK_LIMIT) continue;\n hunkCount++;\n intervals.push([i, Math.min(lines.length - 1, i + DIFF_HUNK_CONTEXT)]);\n }\n\n if (intervals.length === 0) {\n return joinSections([\n renderHeader('diff_summary', {\n files: fileCount,\n hunks,\n added,\n removed,\n lines: lines.length,\n }),\n lines.slice(0, DIFF_INLINE_LINE_LIMIT).join('\\n'),\n `[serializer omitted ${Math.max(0, lines.length - DIFF_INLINE_LINE_LIMIT)} diff line(s)]`,\n ]);\n }\n\n // Merge overlapping / adjacent intervals in a single O(n) pass.\n // Intervals are already in ascending order from the sequential scan.\n const merged: Array<[number, number]> = [intervals[0]!];\n for (let i = 1; i < intervals.length; i++) {\n const last = merged[merged.length - 1]!;\n const current = intervals[i]!;\n if (current[0] <= last[1] + 1) {\n last[1] = Math.max(last[1], current[1]);\n } else {\n merged.push(current);\n }\n }\n\n // Build excerpt from merged intervals \u2014 O(n), no sort.\n const excerpt: string[] = [];\n let prevLine = -1;\n for (const [start, end] of merged) {\n if (start > prevLine + 1) {\n const omitted = prevLine === -1 ? start : start - prevLine - 1;\n excerpt.push(`[serializer omitted ${omitted} diff line(s)]`);\n }\n for (let j = start; j <= end; j++) {\n excerpt.push(lines[j] ?? '');\n }\n prevLine = end;\n }\n\n const trailing = lines.length - prevLine - 1;\n if (trailing > 0) excerpt.push(`[serializer omitted ${trailing} trailing diff line(s)]`);\n\n return joinSections([\n renderHeader('diff_summary', {\n files: fileCount,\n hunks,\n shown_hunks: Math.min(hunks, DIFF_HUNK_LIMIT),\n added,\n removed,\n lines: lines.length,\n }),\n excerpt.join('\\n'),\n ]);\n}\n\nfunction compactFailureOutput(output: string): string {\n const lines = output.split(/\\r?\\n/);\n if (lines.length <= 260) return output.trimEnd();\n\n const selected = new Set<number>();\n const marker =\n /\\b(fail|failed|failure|error|exception|assertionerror|expected|received|actual|timeout|stack)\\b/i;\n let markerHits = 0;\n for (let i = 0; i < lines.length; i++) {\n if (!marker.test(lines[i] ?? '')) continue;\n markerHits++;\n for (let j = Math.max(0, i - 4); j <= Math.min(lines.length - 1, i + 10); j++) {\n selected.add(j);\n }\n }\n\n if (markerHits === 0) {\n return lines.slice(-220).join('\\n').trimEnd();\n }\n\n const ordered = [...selected].sort((a, b) => a - b);\n const out: string[] = [];\n let previous = -1;\n for (const index of ordered) {\n if (index > previous + 1) {\n const omitted = previous === -1 ? index : index - previous - 1;\n out.push(`[serializer omitted ${omitted} line(s)]`);\n }\n out.push(lines[index] ?? '');\n previous = index;\n }\n return out.join('\\n').trimEnd();\n}\n\nfunction extractSpoolNote(output: string): string | undefined {\n return output\n .split(/\\r?\\n/)\n .find((line) => line.startsWith('[output truncated') && line.includes('full'));\n}\n\nfunction hasCommandOutputShape(obj: RecordValue): boolean {\n return (\n typeof obj['stdout'] === 'string' ||\n typeof obj['stderr'] === 'string' ||\n typeof obj['output'] === 'string' ||\n typeof obj['exitCode'] === 'number' ||\n typeof obj['exit_code'] === 'number'\n );\n}\n\nfunction renderCommandOutput(toolName: string, obj: RecordValue, input: unknown): string {\n const command = stringField(obj, 'command') ?? stringFromInput(input, 'command');\n const args = stringArrayField(obj, 'args');\n const commandLine = command ? [command, ...args].join(' ') : undefined;\n const output = stringField(obj, 'output');\n const stdout = stringField(obj, 'stdout');\n const stderr = stringField(obj, 'stderr');\n return joinSections([\n renderHeader(commandLine ? `${toolName}: ${commandLine}` : toolName, {\n exit_code: obj['exit_code'] ?? obj['exitCode'],\n timed_out: obj['timed_out'],\n pid: obj['pid'],\n allowed: obj['allowed'],\n truncated: obj['truncated'],\n runner: obj['runner'],\n linter: obj['linter'],\n fixer: obj['fixer'],\n project: obj['project'],\n tests_run: obj['tests_run'],\n passed: obj['passed'],\n failed: obj['failed'],\n duration_ms: obj['duration_ms'],\n errors: obj['errors'],\n warnings: obj['warnings'],\n files_checked: obj['files_checked'],\n files_changed: obj['files_changed'],\n fix_applied: obj['fix_applied'],\n }),\n stringField(obj, 'error') ? `error:\\n${stringField(obj, 'error')}` : undefined,\n output ? `output:\\n${output}` : undefined,\n stdout ? `stdout:\\n${stdout}` : undefined,\n stderr ? `stderr:\\n${stderr}` : undefined,\n ]);\n}\n\nfunction renderGenericToolObject(toolName: string, obj: RecordValue): string {\n const scalars: RecordValue = {};\n const blocks: string[] = [];\n for (const [key, value] of Object.entries(obj)) {\n if (value === undefined) continue;\n if (isScalar(value)) {\n const inline = String(value);\n if (inline.length <= INLINE_LIMIT && !inline.includes('\\n')) {\n scalars[key] = value;\n } else {\n blocks.push(`${key}:\\n${inline}`);\n }\n continue;\n }\n if (Array.isArray(value)) {\n if (value.every((item) => typeof item === 'string')) {\n blocks.push(`${key}:\\n${renderStringList(value as string[])}`);\n } else {\n blocks.push(`${key}:\\n${renderUnknownList(value)}`);\n }\n continue;\n }\n blocks.push(`${key}: ${clipInline(oneLineJson(value))}`);\n }\n return joinSections([renderHeader(toolName, scalars), ...blocks]);\n}\n\nfunction renderHeader(label: string, fields: RecordValue): string {\n const parts = Object.entries(fields)\n .filter(([, value]) => value !== undefined && value !== null && value !== '')\n .map(([key, value]) => `${key}=${clipInline(formatInlineValue(value))}`);\n return parts.length > 0 ? `${label} (${parts.join(' ')})` : label;\n}\n\nfunction renderStringList(items: string[], empty = '', limit = DEFAULT_LIST_LIMIT): string {\n if (items.length === 0) return empty;\n const shown = items.slice(0, limit);\n const omitted = items.length - shown.length;\n return [\n ...shown,\n ...(omitted > 0\n ? [`[serializer omitted ${omitted} item(s); narrow the request for more]`]\n : []),\n ].join('\\n');\n}\n\nfunction renderUnknownList(items: unknown[], limit = DEFAULT_LIST_LIMIT): string {\n const shown = items.slice(0, limit).map((item) => clipInline(oneLineJson(item), 1_000));\n const omitted = items.length - shown.length;\n if (omitted > 0)\n shown.push(`[serializer omitted ${omitted} item(s); narrow the request for more]`);\n return shown.join('\\n');\n}\n\nfunction joinSections(sections: Array<string | undefined>): string {\n return sections\n .map((section) => (typeof section === 'string' ? section.trimEnd() : undefined))\n .filter((section): section is string => !!section)\n .join('\\n');\n}\n\nfunction formatInlineValue(value: unknown): string {\n /* v8 ignore next -- no renderHeader field is ever an array (all callers pass scalars) */\n if (Array.isArray(value)) return `[${value.map(formatInlineValue).join(',')}]`;\n if (isScalar(value)) return String(value);\n return oneLineJson(value);\n}\n\nfunction clipInline(value: string, max = INLINE_LIMIT): string {\n const compact = value.replace(/\\s+/g, ' ').trim();\n return compact.length <= max\n ? compact\n : `${compact.slice(0, max - 15)}...(${compact.length} chars)`;\n}\n\nfunction oneLineJson(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction stringField(obj: RecordValue, key: string): string | undefined {\n const value = obj[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction numberField(obj: RecordValue, key: string): number | undefined {\n const value = obj[key];\n return typeof value === 'number' ? value : undefined;\n}\n\nfunction stringArrayField(obj: RecordValue, key: string): string[] {\n const value = obj[key];\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === 'string')\n : [];\n}\n\nfunction stringFromInput(input: unknown, key: string): string | undefined {\n if (!isRecord(input)) return undefined;\n const value = input[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction numberFromInput(input: unknown, key: string): number | undefined {\n if (!isRecord(input)) return undefined;\n const value = input[key];\n return typeof value === 'number' ? value : undefined;\n}\n\nfunction inputListSummary(input: unknown, key: string): string | undefined {\n if (!isRecord(input)) return undefined;\n const value = input[key];\n if (typeof value === 'string') return value;\n if (Array.isArray(value)) return value.filter((item) => typeof item === 'string').join(',');\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is RecordValue {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isScalar(value: unknown): value is string | number | boolean | null {\n return value === null || ['string', 'number', 'boolean'].includes(typeof value);\n}\n\n/**\n * Render a tool result body for inclusion in the `tool.executed` event.\n * Tool outputs can be large (file dumps, command output); UIs only want a\n * preview line, so cap at ~400 chars with an ellipsis marker.\n */\nexport function truncateForEvent(content: string, max = 400): string {\n if (!content) return '';\n return content.length <= max ? content : `${content.slice(0, max - 1)}\u2026`;\n}\n\n/**\n * Derive size signals (bytes / tokens / lines) for the chip rendered beside\n * each tool result. Computed once over the FULL `content` BEFORE the\n * 400-char event preview is taken.\n *\n * - bytes: UTF-8 byte length (multi-byte aware).\n * - tokens: standard ~3.5 chars/token heuristic.\n * - lines: read prefixes lines with `<n>\u2192`; for shell/grep/logs we fall\n * back to a newline count. Undefined for tools without a line notion.\n */\nconst READ_LINE_PREFIX_RE = /^\\s*\\d+\u2192/gm;\n\nexport function sizeSignals(\n toolName: string | undefined,\n content: string,\n): { outputBytes: number; outputTokens: number; outputLines: number | undefined } {\n if (!content || content.length === 0) {\n return { outputBytes: 0, outputTokens: 0, outputLines: undefined };\n }\n const outputBytes = Buffer.byteLength(content, 'utf8');\n const outputTokens = Math.max(1, Math.round(outputBytes / 3.5));\n let outputLines: number | undefined;\n if (toolName === 'read') {\n READ_LINE_PREFIX_RE.lastIndex = 0;\n let count = 0;\n while (READ_LINE_PREFIX_RE.exec(content) !== null) count++;\n if (count > 0) outputLines = count;\n } else if (\n toolName === 'bash' ||\n toolName === 'shell' ||\n toolName === 'grep' ||\n toolName === 'logs'\n ) {\n let nl = 0;\n for (let i = 0; i < content.length; i++) if (content.charCodeAt(i) === 10) nl++;\n outputLines = nl + (content.endsWith('\\n') ? 0 : 1);\n }\n return { outputBytes, outputTokens, outputLines };\n}\n", "import type {\n ToolResultRenderMode,\n ToolResultRenderModeConfig,\n} from '../types/config.js';\nimport type { Tool } from '../types/tool.js';\n\nexport const DEFAULT_TOOL_RESULT_RENDER_MODE: ToolResultRenderMode = 'extend';\n\n/**\n * Normalize a raw value to a {@link ToolResultRenderMode}. Accepts the\n * canonical strings (`'extend' | 'simple'`) plus a few synonyms so the\n * slash command feels forgiving (`extended`/`full` \u2192 `extend`,\n * `short`/`brief` \u2192 `simple`). Returns `undefined` for anything else so\n * the caller can reject unknown input without throwing.\n */\nexport function normalizeToolResultRenderMode(value: unknown): ToolResultRenderMode | undefined {\n if (typeof value !== 'string') return undefined;\n const raw = value.trim().toLowerCase();\n if (raw === 'extend' || raw === 'extended' || raw === 'full') return 'extend';\n if (raw === 'simple' || raw === 'short' || raw === 'brief') return 'simple';\n return undefined;\n}\n\n/**\n * Look up the result-render mode for `toolName` from a config map. Falls\n * back to the default `'extend'` when the map is missing the entry.\n */\nexport function resolveToolResultRenderMode(\n modes: ToolResultRenderModeConfig | undefined,\n toolName: string,\n): ToolResultRenderMode {\n return normalizeToolResultRenderMode(modes?.[toolName]) ?? DEFAULT_TOOL_RESULT_RENDER_MODE;\n}\n\n/**\n * Subset of {@link import('../registry/tool-registry.js').ToolRegistry}\n * the result-render-mode setters need. Decouples this module from the\n * concrete registry class so it can be reused by tests and by tools\n * that wrap their own registry.\n */\nexport interface ToolResultRenderModeRegistryLike {\n get(name: string): Tool | undefined;\n setResultRenderMode?(name: string, mode: ToolResultRenderMode): boolean;\n applyResultRenderModes?(\n modes?: ToolResultRenderModeConfig,\n ): { applied: number; missing: string[] };\n getResultRenderMode?(name: string): ToolResultRenderMode;\n}\n\n/**\n * Set a single tool's result-render mode on a registry. Prefers the\n * registry's native accessor (so it can update any internal state, e.g.\n * usage caches); falls back to a no-op so callers stay decoupled from\n * the registry implementation.\n */\nexport function setToolResultRenderMode(\n registry: ToolResultRenderModeRegistryLike,\n name: string,\n mode: ToolResultRenderMode,\n): boolean {\n if (typeof registry.setResultRenderMode === 'function') {\n return registry.setResultRenderMode(name, mode);\n }\n return false;\n}\n\n/**\n * Look up the current result-render mode for a single tool. Returns the\n * registry's view if it has one, otherwise the default. This is what the\n * tool-executor calls on each tool invocation to decide whether the next\n * `writeToolResult` should be `simple` or `extend`.\n */\nexport function getToolResultRenderMode(\n registry: ToolResultRenderModeRegistryLike,\n name: string,\n): ToolResultRenderMode {\n return registry.getResultRenderMode?.(name) ?? DEFAULT_TOOL_RESULT_RENDER_MODE;\n}\n\n/**\n * Bulk-apply a config map (`tools.resultRenderMode`) to a registry.\n * Mirrors {@link import('./tool-description-mode.js').applyToolDescriptionModes}\n * for symmetry with the LLM-side description mode.\n */\nexport function applyToolResultRenderModes(\n registry: ToolResultRenderModeRegistryLike,\n modes?: ToolResultRenderModeConfig,\n): { applied: number; missing: string[] } {\n if (typeof registry.applyResultRenderModes === 'function') {\n return registry.applyResultRenderModes(modes);\n }\n\n const entries = Object.entries(modes ?? {});\n const missing: string[] = [];\n let applied = 0;\n for (const [name, rawMode] of entries) {\n const mode = normalizeToolResultRenderMode(rawMode);\n if (!mode) continue;\n if (setToolResultRenderMode(registry, name, mode)) applied++;\n else missing.push(name);\n }\n return { applied, missing };\n}", "const GLOB_METACHARACTERS = /[*?[\\]]/g;\n\nexport function escapeGlobSubject(value: string): string {\n return value.replace(GLOB_METACHARACTERS, (char) => `\\\\${char}`);\n}\n\nexport function normalizePathSubject(value: string): string {\n return escapeGlobSubject(value.replace(/\\\\/g, '/'));\n}\n\nexport function isPathSubjectKey(subjectKey: string): boolean {\n return subjectKey === 'path' || subjectKey === 'file' || subjectKey === 'files';\n}\n\nexport function subjectForToolInput(\n toolName: string,\n input: unknown,\n subjectKey?: string,\n): string | undefined {\n if (!input || typeof input !== 'object') return undefined;\n const obj = input as Record<string, unknown>;\n\n if (subjectKey) {\n const value = obj[subjectKey];\n if (typeof value === 'string') {\n return isPathSubjectKey(subjectKey) ? normalizePathSubject(value) : escapeGlobSubject(value);\n }\n }\n\n if (toolName === 'bash' && typeof obj.command === 'string') {\n return escapeGlobSubject(obj.command);\n }\n if (typeof obj.path === 'string') {\n return normalizePathSubject(obj.path);\n }\n if (typeof obj.url === 'string') {\n return escapeGlobSubject(obj.url);\n }\n if (typeof obj.name === 'string') {\n return escapeGlobSubject(obj.name);\n }\n return undefined;\n}\n", "import { randomBytes } from 'node:crypto';\n\n/**\n * Crockford base32 alphabet (excludes I, L, O, U to avoid ambiguity).\n */\nconst ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\nconst ENCODING_LEN = ENCODING.length;\nconst TIME_LEN = 10;\nconst RANDOM_LEN = 16;\n\nfunction encodeTime(now: number, len: number): string {\n let mod: number;\n let str = '';\n for (let i = len - 1; i >= 0; i--) {\n mod = now % ENCODING_LEN;\n str = ENCODING[mod] + str;\n now = (now - mod) / ENCODING_LEN;\n }\n return str;\n}\n\nfunction encodeRandom(len: number): string {\n const bytes = randomBytes(len);\n let str = '';\n for (let i = 0; i < len; i++) {\n str += ENCODING[(bytes[i] as number) % ENCODING_LEN];\n }\n return str;\n}\n\n/**\n * Generate a ULID \u2014 a 26-char Crockford-base32 identifier whose first 10 chars\n * encode the millisecond timestamp (so IDs sort lexicographically by creation\n * time) followed by 16 chars of randomness. Zero runtime dependencies.\n *\n * The codebase convention is \"IDs are ULIDs\"; use this for any new store key.\n */\nexport function ulid(seedTime: number = Date.now()): string {\n return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN);\n}\n\n/** True for a well-formed 26-char Crockford-base32 ULID. */\nexport function isUlid(value: string): boolean {\n if (value.length !== TIME_LEN + RANDOM_LEN) return false;\n for (const ch of value) {\n if (!ENCODING.includes(ch)) return false;\n }\n return true;\n}\n", "import { createHash } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\n/**\n * Path layout. All developer-level state lives in ~/.wrongstack/.\n * Per-project state is keyed by the canonical project root under\n * ~/.wrongstack/projects/<slug>/. Linked Git worktrees resolve to their main\n * checkout so coordination and durable state are shared across worktrees.\n *\n * The ONLY thing inside the project tree is the optional\n * .wrongstack/AGENTS.md (committed) and .wrongstack/skills/ (committed).\n */\n\nexport interface WstackPaths {\n /** ~/.wrongstack \u2014 global root. */\n globalRoot: string;\n /** Absolute project root. */\n projectRoot: string;\n /** Home directory (~) \u2014 base for foreign tool state (~/.codex, ~/.agents, \u2026). */\n homeDir: string;\n /**\n * ~/.wrongstack \u2014 directory for user-global stateful config files\n * (mode.json, theme.json, \u2026). Currently an alias for `globalRoot`;\n * separate name lets us split out per-OS XDG_CONFIG_HOME later\n * without rewriting callers.\n */\n configDir: string;\n /** ~/.wrongstack/config.json \u2014 bootstrap config (activeProfile + version). */\n globalConfig: string;\n /**\n * ~/.wrongstack/profiles \u2014 directory containing per-profile configs.\n * Each profile has its own subdirectory with a config.json.\n */\n profilesDir: string;\n /**\n * Resolve the config path for a named profile.\n * Returns ~/.wrongstack/profiles/<name>/config.json.\n */\n profileConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/statusline.json */\n profileStatuslineConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/mode.json */\n profileModeConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/provider-status.json */\n profileProviderStatus: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/update-cache.json */\n profileUpdateCache: (name: string) => string;\n /** ~/.wrongstack/.key \u2014 32 random bytes, mode 0600, AES-GCM key for the secret vault. */\n secretsKey: string;\n /** ~/.wrongstack/memory.md \u2014 user-global memory. */\n globalMemory: string;\n /** ~/.wrongstack/skills \u2014 user-global skills. */\n globalSkills: string;\n /** ~/.claude/skills \u2014 user-global skills from foreign coding agents (Claude Code, Codex, \u2026). Read-only. */\n globalClaudeSkills: string;\n /** ~/.wrongstack/design-kits \u2014 user-global Design Studio kits. */\n globalDesignKits: string;\n /** ~/.wrongstack/prompts \u2014 user-global prompt library. */\n globalPrompts: string;\n /** ~/.wrongstack/instructions \u2014 user-global system instruction overrides. */\n globalInstructions: string;\n /** ~/.wrongstack/prompt-usage.json \u2014 per-slug insert counts (recent/popular). */\n promptUsage: string;\n /** ~/.wrongstack/cache \u2014 fetched data (models.dev, etc.). */\n cacheDir: string;\n /** ~/.wrongstack/cache/models.dev.json */\n modelsCache: string;\n /** ~/.wrongstack/cache/models-overlay.json \u2014 cached curated overlay. */\n modelsOverlayCache: string;\n /**\n * Per-project codebase symbol index (SQLite). Lives under the global project\n * dir \u2014 NOT inside the repo \u2014 so it never clutters the working tree or needs\n * gitignoring. `~/.wrongstack/projects/<hash>/codebase-index`.\n */\n projectCodebaseIndex: string;\n /** ~/.wrongstack/history \u2014 REPL line history. */\n historyFile: string;\n /** ~/.wrongstack/logs/wrongstack.log */\n logFile: string;\n /** ~/.wrongstack/projects/<hash> */\n projectDir: string;\n /** ~/.wrongstack/projects/<hash>/memory.md */\n projectMemory: string;\n /** ~/.wrongstack/projects/<hash>/sessions */\n projectSessions: string;\n /** ~/.wrongstack/projects/<hash>/trust.json */\n projectTrust: string;\n /** ~/.wrongstack/projects/<hash>/meta.json */\n projectMeta: string;\n /** ~/.wrongstack/projects/<hash>/config.local.json \u2014 optional override */\n projectLocalConfig: string;\n /** <project>/.wrongstack/config.json \u2014 per-project settings (safe fields only).\n * This lives inside the project root so it can be gitignored or shared. */\n inProjectConfig: string;\n /** <project>/.wrongstack/AGENTS.md \u2014 committed project memory. */\n inProjectAgentsFile: string;\n /** <project>/.wrongstack/skills \u2014 committed project skills. */\n inProjectSkills: string;\n /** <project>/.claude/skills \u2014 project skills authored for foreign coding agents (Claude Code, \u2026). Read-only. */\n inProjectClaudeSkills: string;\n /** <project>/.wrongstack/prompts \u2014 committed project prompt library. */\n inProjectPrompts: string;\n /** <project>/.wrongstack/instructions \u2014 committed project instruction overrides. */\n inProjectInstructions: string;\n /** <project>/.wrongstack/design-kits \u2014 committed project Design Studio kits. */\n inProjectDesignKits: string;\n /** <project>/.wrongstack/worktrees \u2014 git worktrees for per-phase isolation (gitignored). */\n inProjectWorktrees: string;\n /** Stable hash for the canonical project root (shared by linked Git worktrees). */\n projectHash: string;\n /** Human-readable canonical project slug, shared by linked Git worktrees. */\n projectSlug: string;\n /** ~/.wrongstack/projects/<hash>/goal.json \u2014 goal persistence */\n projectGoal: string;\n /** ~/.wrongstack/projects/<hash>/input-history.json \u2014 TUI prompt input history */\n projectInputHistory: string;\n /** ~/.wrongstack/projects/<hash>/specs \u2014 SDD spec files */\n projectSpecs: string;\n /** ~/.wrongstack/projects/<hash>/task-graphs \u2014 SDD task graphs */\n projectTaskGraphs: string;\n /** ~/.wrongstack/projects/<hash>/sdd-session.json \u2014 SDD session state */\n projectSddSession: string;\n /** ~/.wrongstack/projects/<hash>/plan.json \u2014 plan persistence */\n projectPlan: string;\n /** ~/.wrongstack/projects/<hash>/autophase \u2014 Goal phase-graph JSON files (dir name kept for backward compat) */\n projectAutophase: string;\n /** ~/.wrongstack/projects/<hash>/sdd-boards \u2014 live SDD board snapshots + JSONL event logs */\n projectSddBoards: string;\n /** ~/.wrongstack/sync.json \u2014 CloudSync configuration */\n syncConfig: string;\n /** ~/.wrongstack/config-history \u2014 timestamped backups on every config write */\n configHistoryDir: string;\n /** Function to get the status.json path for a project given its hash. */\n projectStatus: (projectHash: string) => string;\n}\n\n/**\n * Resolve the stable project identity root used by global WrongStack state.\n *\n * A linked Git worktree has its own checkout path and a `.git` *file* that\n * points into `<main>/.git/worktrees/<name>`. Its `commondir` points back to\n * the main checkout's `.git` directory. Treating the linked checkout path as\n * the project identity would split one repository into multiple session,\n * registry, and mailbox directories \u2014 agents in different worktrees would be\n * unable to see or message each other.\n *\n * Project-local paths still use the caller's actual checkout. Only global\n * state identity is canonicalized. Non-Git projects, normal checkouts, Git\n * submodules, and separate-git-dir layouts keep their existing identity.\n */\nexport function canonicalProjectRoot(absRoot: string): string {\n const checkoutRoot = path.resolve(absRoot);\n const dotGit = path.join(checkoutRoot, '.git');\n\n try {\n if (!fs.statSync(dotGit).isFile()) return checkoutRoot;\n\n const gitDirLine = fs.readFileSync(dotGit, 'utf8').trim();\n const match = /^gitdir:\\s*(.+)$/i.exec(gitDirLine);\n if (!match?.[1]) return checkoutRoot;\n\n const gitDir = path.resolve(checkoutRoot, match[1].trim());\n const commonDirFile = path.join(gitDir, 'commondir');\n if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;\n\n const commonDir = path.resolve(gitDir, fs.readFileSync(commonDirFile, 'utf8').trim());\n // Linked worktrees created by Git share the main checkout's `.git` dir.\n // A submodule or --separate-git-dir layout may point elsewhere; do not\n // guess a working-tree root for those shapes.\n if (path.basename(commonDir).toLowerCase() !== '.git') return checkoutRoot;\n return path.dirname(commonDir);\n } catch {\n // Missing/malformed administrative files must never make path resolution\n // fail. Falling back preserves the pre-canonicalization behavior.\n return checkoutRoot;\n }\n}\n\nexport function projectHash(absRoot: string): string {\n return createHash('sha256').update(canonicalProjectRoot(absRoot)).digest('hex').slice(0, 12);\n}\n\n/**\n * Human-readable project directory name: slugified folder name + short hash\n * suffix for uniqueness. e.g. `wrongstack-a1b2c3` instead of `3024e5e6fa58`.\n */\nexport function projectSlug(absRoot: string): string {\n const identityRoot = canonicalProjectRoot(absRoot);\n const base = slugify(path.basename(identityRoot));\n const hash = createHash('sha256').update(identityRoot).digest('hex').slice(0, 6);\n return `${base}-${hash}`;\n}\n\n/** Turn a folder name into a filesystem-safe lowercase slug. */\nfunction slugify(name: string): string {\n return (\n name\n .toLowerCase()\n // Collapse any run of non-alphanumeric chars into a single hyphen.\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40) || 'project'\n );\n}\n\nexport interface WstackPathOptions {\n userHome?: string | undefined;\n projectRoot: string;\n /** Override the global root (e.g. for tests). Default: `${userHome}/.wrongstack`. */\n globalRoot?: string | undefined;\n}\n\n/**\n * The global `~/.wrongstack` root, honoring the `WRONGSTACK_HOME` env\n * override. The override exists so tests (and sandboxed runs) can redirect\n * ALL global state \u2014 config, secrets, logs, projects/, mailboxes \u2014 away from\n * the real user home. Before it existed, `pnpm test` booted runtimes against\n * the real `~/.wrongstack`: it read the user's real config.json (starting a\n * second live Telegram poller), appended to the real wrongstack.log, and left\n * ~20k orphaned fixture dirs under projects/.\n *\n * Every code path that wants the global dir must come through here (or\n * through `resolveWstackPaths`) instead of `path.join(os.homedir(), '.wrongstack')`.\n */\nexport function wstackGlobalRoot(): string {\n const fromEnv = process.env['WRONGSTACK_HOME'];\n if (fromEnv && fromEnv.trim().length > 0) return path.resolve(fromEnv);\n return path.join(os.homedir(), '.wrongstack');\n}\n\nexport function resolveWstackPaths(opts: WstackPathOptions): WstackPaths {\n // Precedence: explicit globalRoot > explicit userHome (callers/tests that\n // pass one expect paths under it) > WRONGSTACK_HOME env > real home dir.\n const globalRoot =\n opts.globalRoot ?? (opts.userHome ? path.join(opts.userHome, '.wrongstack') : wstackGlobalRoot());\n // Home dir for FOREIGN tool state (Claude Code's ~/.claude). Independent of\n // WRONGSTACK_HOME, which redirects only WrongStack state: a real user's\n // Claude skills live in their real home, but tests pass `userHome` to keep\n // both `.wrongstack` and `.claude` under a temp dir.\n const homeDir = opts.userHome ?? os.homedir();\n const hash = projectHash(opts.projectRoot);\n const slug = projectSlug(opts.projectRoot);\n const projectDir = path.join(globalRoot, 'projects', slug);\n return {\n globalRoot,\n projectRoot: opts.projectRoot,\n homeDir,\n configDir: globalRoot,\n globalConfig: path.join(globalRoot, 'config.json'),\n profilesDir: path.join(globalRoot, 'profiles'),\n profileConfig: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'config.json');\n },\n profileStatuslineConfig: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'statusline.json');\n },\n profileModeConfig: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'mode.json');\n },\n profileProviderStatus: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'provider-status.json');\n },\n profileUpdateCache: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'update-cache.json');\n },\n secretsKey: path.join(globalRoot, '.key'),\n globalMemory: path.join(globalRoot, 'memory.md'),\n globalSkills: path.join(globalRoot, 'skills'),\n globalClaudeSkills: path.join(homeDir, '.claude', 'skills'),\n globalDesignKits: path.join(globalRoot, 'design-kits'),\n globalPrompts: path.join(globalRoot, 'prompts'),\n globalInstructions: path.join(globalRoot, 'instructions'),\n promptUsage: path.join(globalRoot, 'prompt-usage.json'),\n cacheDir: path.join(globalRoot, 'cache'),\n modelsCache: path.join(globalRoot, 'cache', 'models.dev.json'),\n modelsOverlayCache: path.join(globalRoot, 'cache', 'models-overlay.json'),\n historyFile: path.join(globalRoot, 'history'),\n logFile: path.join(globalRoot, 'logs', 'wrongstack.log'),\n projectDir,\n projectCodebaseIndex: path.join(projectDir, 'codebase-index'),\n projectMemory: path.join(projectDir, 'memory.md'),\n projectSessions: path.join(projectDir, 'sessions'),\n projectTrust: path.join(projectDir, 'trust.json'),\n projectMeta: path.join(projectDir, 'meta.json'),\n projectLocalConfig: path.join(projectDir, 'config.local.json'),\n inProjectConfig: path.join(opts.projectRoot, '.wrongstack', 'config.json'),\n inProjectAgentsFile: path.join(opts.projectRoot, '.wrongstack', 'AGENTS.md'),\n inProjectSkills: path.join(opts.projectRoot, '.wrongstack', 'skills'),\n inProjectClaudeSkills: path.join(opts.projectRoot, '.claude', 'skills'),\n inProjectPrompts: path.join(opts.projectRoot, '.wrongstack', 'prompts'),\n inProjectInstructions: path.join(opts.projectRoot, '.wrongstack', 'instructions'),\n inProjectDesignKits: path.join(opts.projectRoot, '.wrongstack', 'design-kits'),\n inProjectWorktrees: path.join(opts.projectRoot, '.wrongstack', 'worktrees'),\n projectHash: hash,\n projectSlug: slug,\n projectGoal: path.join(projectDir, 'goal.json'),\n projectInputHistory: path.join(projectDir, 'input-history.json'),\n projectSpecs: path.join(projectDir, 'specs'),\n projectTaskGraphs: path.join(projectDir, 'task-graphs'),\n projectSddSession: path.join(projectDir, 'sdd-session.json'),\n projectPlan: path.join(projectDir, 'plan.json'),\n projectAutophase: path.join(projectDir, 'autophase'),\n projectSddBoards: path.join(projectDir, 'sdd-boards'),\n syncConfig: path.join(globalRoot, 'sync.json'),\n configHistoryDir: path.join(globalRoot, 'config-history'),\n projectStatus: (projectHash: string) => path.join(globalRoot, 'projects', projectHash, 'status.json'),\n };\n}\n"],
5
- "mappings": "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",
6
- "names": ["stat", "resolve", "open", "fs", "path", "fs", "path", "path", "isAbsolute", "resolve", "walk", "stat", "path", "path", "resolve", "isRecord", "randomBytes", "createHash", "fs", "path", "slugify", "projectHash"]
3
+ "sources": ["../../src/utils/assert-never.ts", "../../src/utils/atomic-write.ts", "../../src/types/errors.ts", "../../src/utils/child-env.ts", "../../src/utils/term.ts", "../../src/utils/color.ts", "../../src/utils/config-backup.ts", "../../src/utils/connectivity.ts", "../../src/utils/cache-key.ts", "../../src/utils/config-json.ts", "../../src/utils/context-evidence.ts", "../../src/utils/error.ts", "../../src/utils/expect-defined.ts", "../../src/utils/message-invariants.ts", "../../src/core/agent-response.ts", "../../src/core/system-prompt-builder.ts", "../../src/utils/tool-wire-compact.ts", "../../src/utils/token-estimate.ts", "../../src/utils/context-breakdown.ts", "../../src/utils/deep-merge.ts", "../../src/utils/diff.ts", "../../src/utils/glob-expand.ts", "../../src/utils/glob-match.ts", "../../src/utils/incoming-images.ts", "../../src/utils/ip-guard.ts", "../../src/utils/json-repair.ts", "../../src/utils/json-schema-validate.ts", "../../src/utils/merge-custom-models.ts", "../../src/utils/merge-models-payload.ts", "../../src/utils/newline-normalize.ts", "../../src/utils/regex-guard.ts", "../../src/utils/safe-json.ts", "../../src/utils/session-scoped-path.ts", "../../src/utils/sleep.ts", "../../src/utils/slug.ts", "../../src/utils/string.ts", "../../src/utils/task-format.ts", "../../src/utils/todos-format.ts", "../../src/utils/tool-description-mode.ts", "../../src/utils/tool-output-serializer.ts", "../../src/utils/tool-result-render-mode.ts", "../../src/utils/tool-subject.ts", "../../src/utils/ulid.ts", "../../src/utils/wstack-paths.ts"],
4
+ "sourcesContent": ["/**\n * Exhaustiveness check for discriminated union switches.\n * Place in the `default` branch of a switch over a union type\n * to get a compile-time error when a new variant is added.\n *\n * @example\n * switch (block.type) {\n * case 'text': return renderText(block);\n * case 'tool_use': return renderToolUse(block);\n * default: return assertNever(block);\n * }\n */\nexport function assertNever(x: never, message?: string): never {\n const err = new Error(\n message ?? `Unhandled case: ${JSON.stringify(x)}`,\n );\n err.name = 'AssertNeverError';\n throw err;\n}\n", "import { randomBytes } from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport { watch as watchDir } from 'node:fs';\nimport type { FSWatcher } from 'node:fs';\nimport * as path from 'node:path';\nimport { FsError } from '../types/errors.js';\n\nexport interface AtomicWriteOptions {\n mode?: number | undefined;\n encoding?: BufferEncoding | undefined;\n}\n\nexport interface FileLockOptions {\n timeoutMs?: number | undefined;\n staleMs?: number | undefined;\n}\n\nexport async function atomicWrite(\n targetPath: string,\n content: string | Uint8Array,\n opts: AtomicWriteOptions = {},\n): Promise<void> {\n const dir = path.dirname(targetPath);\n await fs.mkdir(dir, { recursive: true });\n const tmp = path.join(dir, `.${path.basename(targetPath)}.${randomBytes(6).toString('hex')}.tmp`);\n\n // Write content to tmp first; 'wx' ensures exclusive creation (fails if\n // tmp already exists \u2014 extremely unlikely with 6-byte random suffix).\n try {\n if (typeof content === 'string') {\n await fs.writeFile(tmp, content, { flag: 'wx', encoding: opts.encoding ?? 'utf8' });\n } else {\n await fs.writeFile(tmp, content, { flag: 'wx' });\n }\n try {\n const fh = await fs.open(tmp, 'r+');\n try {\n await fh.sync();\n } finally {\n await fh.close();\n }\n } catch {\n // fsync best-effort\n }\n // Now safely read mode from target (if it exists) and apply to tmp before rename.\n // Prefer opts.mode for new files; for existing files preserve their mode.\n let mode: number | undefined;\n try {\n const stat = await fs.stat(targetPath);\n mode = stat.mode & 0o777;\n } catch {\n mode = opts.mode;\n }\n if (mode !== undefined) {\n await fs.chmod(tmp, mode);\n }\n await renameWithRetry(tmp, targetPath);\n // P3 #20 (before-release.md): on Windows, fs.rename (MoveFileExW) does\n // not preserve Unix permission bits \u2014 the chmod above applies to the tmp\n // file, but the rename may reset the destination's mode to the Windows\n // default. Re-apply the mode after rename on win32 so an edited file\n // keeps its executable bit (or any non-default permission). On POSIX,\n // rename preserves metadata so this is a no-op (chmod is idempotent and\n // cheap), but we gate it on win32 to avoid the extra stat+chmod on the\n // common path.\n if (mode !== undefined && process.platform === 'win32') {\n try {\n await fs.chmod(targetPath, mode);\n } catch {\n // Best-effort: a transient EPERM (antivirus lock) should not fail\n // the write \u2014 the content is already on disk.\n }\n }\n } catch (err) {\n try {\n await fs.unlink(tmp);\n } catch {\n // ignore cleanup error\n }\n throw err;\n }\n}\n\nexport async function ensureDir(dir: string): Promise<void> {\n await fs.mkdir(dir, { recursive: true });\n}\n\nexport async function withFileLock<T>(\n targetPath: string,\n fn: () => Promise<T>,\n opts: FileLockOptions = {},\n): Promise<T> {\n const dir = path.dirname(targetPath);\n await fs.mkdir(dir, { recursive: true });\n const lockPath = path.join(dir, `.${path.basename(targetPath)}.lock`);\n // A lock holder can be scheduled out for several seconds when the full test\n // suite (or a busy workstation) is spawning many child processes. Five\n // seconds was short enough to turn ordinary contention into a dropped\n // best-effort index write. Keep the wait bounded, but leave enough headroom\n // for the holder to resume and release before stale-lock recovery applies.\n const timeoutMs = opts.timeoutMs ?? 15_000;\n const staleMs = opts.staleMs ?? 30_000;\n const started = Date.now();\n let handle: fs.FileHandle | undefined;\n\n for (;;) {\n try {\n handle = await fs.open(lockPath, 'wx');\n await handle.writeFile(`${process.pid}:${Date.now()}`);\n break;\n } catch (err) {\n // If fs.open succeeded but handle.writeFile threw (e.g. ENOSPC, EIO),\n // `handle` owns an open exclusive lock file. Close the handle and remove\n // the orphan lock so the next iteration (or a peer) can acquire it\n // without timing out on the stale-lock window or dead-looping on EEXIST.\n if (handle) {\n await handle.close().catch(() => {});\n await fs.unlink(lockPath).catch(() => {});\n handle = undefined;\n }\n const code = (err as NodeJS.ErrnoException).code;\n // ENOENT means the directory was deleted (e.g. by concurrent cleanup).\n // Recreate it and retry acquiring the lock.\n if (code === 'ENOENT') {\n await fs.mkdir(dir, { recursive: true });\n continue;\n }\n if (code !== 'EEXIST' && code !== 'EPERM') throw err;\n try {\n const stat = await fs.stat(lockPath);\n if (Date.now() - stat.mtimeMs > staleMs) {\n await fs.unlink(lockPath);\n continue;\n }\n } catch {\n continue;\n }\n const elapsed = Date.now() - started;\n if (elapsed >= timeoutMs) {\n throw new FsError({\n message: `Timed out waiting for file lock: ${targetPath}`,\n code: 'FS_ATOMIC_WRITE_FAILED',\n path: targetPath,\n context: { timeoutMs },\n });\n }\n // Wait for the lock to be released, using a filesystem watcher for\n // nearly-instant wake-up instead of polling. The watcher is best-effort:\n // a safety timeout fires at most every 100ms so we don't busy-wait.\n await waitForLockRelease(lockPath, timeoutMs - elapsed);\n }\n }\n\n try {\n return await fn();\n } finally {\n try {\n await handle?.close();\n } catch {\n // ignore\n }\n try {\n await fs.unlink(lockPath);\n } catch {\n // ignore\n }\n }\n}\n\n/**\n * Watch a lock file's parent directory for the file being removed (unlinked),\n * which signals that the lock holder has released it. A safety timeout caps\n * the wait so the overall `withFileLock` timeout is always respected.\n *\n * Uses a bounded safety interval (up to 100ms) so even if `fs.watch` is\n * unavailable or misses the event, we never busy-wait at 25ms fixed polling.\n */\nasync function waitForLockRelease(lockPath: string, remainingMs: number): Promise<void> {\n const parentDir = path.dirname(lockPath);\n const lockName = path.basename(lockPath);\n const intervalMs = Math.min(remainingMs, 100);\n\n return new Promise<void>((resolve) => {\n let settled = false;\n let watcher: FSWatcher | null = null;\n\n // Safety timer \u2014 always fires, even if fs.watch is unavailable.\n const timer = setTimeout(() => {\n settled = true;\n watcher?.close();\n resolve();\n }, intervalMs);\n\n try {\n watcher = watchDir(parentDir, (eventType, filename) => {\n if (settled) return;\n // 'rename' fires on unlink on most platforms; 'change' is a\n // conservative fallback for environments that only emit 'change'.\n if (filename === lockName && (eventType === 'rename' || eventType === 'change')) {\n settled = true;\n clearTimeout(timer);\n watcher?.close();\n resolve();\n }\n });\n } catch {\n // fs.watch not supported (e.g. some container environments, network\n // filesystems). Clear the safety timer and fall back to a single\n // short delay \u2014 the caller's loop will retry on the next iteration.\n clearTimeout(timer);\n if (!settled) {\n settled = true;\n setTimeout(resolve, Math.min(remainingMs, 25));\n }\n return;\n }\n\n // Re-check lock existence after setting up the watch to close the race\n // where the lock was released between our last EEXIST check and now.\n fs.access(lockPath).then(\n () => {\n // Lock still exists \u2014 the watch (or safety timer) will resolve.\n },\n () => {\n // Lock was already released \u2014 respond immediately.\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n watcher?.close();\n resolve();\n }\n },\n );\n });\n}\n\n// On Windows, fs.rename over an existing file can fail with EPERM/EBUSY/EACCES\n// when antivirus, file indexers, editor file watchers, or a concurrent writer\n// briefly hold a handle on the destination. These are transient \u2014 retry with a\n// short backoff before giving up. POSIX renames are atomic and won't hit this.\nconst TRANSIENT_RENAME_CODES = new Set(['EPERM', 'EBUSY', 'EACCES', 'ENOTEMPTY']);\n\nasync function renameWithRetry(from: string, to: string): Promise<void> {\n if (process.platform !== 'win32') {\n await fs.rename(from, to);\n return;\n }\n const delays = [10, 25, 60, 120, 250];\n let lastErr: unknown;\n for (let i = 0; i <= delays.length; i++) {\n try {\n await fs.rename(from, to);\n return;\n } catch (err) {\n lastErr = err;\n const code = (err as NodeJS.ErrnoException)?.code;\n if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {\n throw err;\n }\n await new Promise((resolve) => setTimeout(resolve, delays[i]));\n }\n }\n throw lastErr;\n}\n", "import { toErrorMessage } from '../utils/index.js';\n\n/**\n * WrongStack error hierarchy.\n *\n * Every error thrown by the framework is a `WrongStackError` with a\n * machine-readable `code`, a `subsystem` tag, and a `severity` level.\n * This lets consumers (CLI, TUI, plugins, tests) branch on structured\n * data instead of parsing error messages.\n */\n\n// \u2500\u2500 Error codes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Machine-readable error codes as frozen constants.\n *\n * Use `ERROR_CODES.X` instead of raw string literals for:\n * - IDE autocomplete and compile-time validation\n * - Safe refactoring (rename updates all usages)\n * - Plugin extensibility (extend the object to add custom codes)\n *\n * The `ErrorCode` type is derived from this object, so adding a new\n * code here automatically updates the type without extra changes.\n */\nexport const ERROR_CODES = {\n // Provider\n PROVIDER_RATE_LIMITED: 'PROVIDER_RATE_LIMITED',\n PROVIDER_AUTH_FAILED: 'PROVIDER_AUTH_FAILED',\n PROVIDER_OVERLOADED: 'PROVIDER_OVERLOADED',\n PROVIDER_INVALID_REQUEST: 'PROVIDER_INVALID_REQUEST',\n PROVIDER_SERVER_ERROR: 'PROVIDER_SERVER_ERROR',\n PROVIDER_NETWORK_ERROR: 'PROVIDER_NETWORK_ERROR',\n PROVIDER_CONTEXT_OVERFLOW: 'PROVIDER_CONTEXT_OVERFLOW',\n // Tool\n TOOL_NOT_FOUND: 'TOOL_NOT_FOUND',\n TOOL_PERMISSION_DENIED: 'TOOL_PERMISSION_DENIED',\n TOOL_EXECUTION_FAILED: 'TOOL_EXECUTION_FAILED',\n TOOL_TIMEOUT: 'TOOL_TIMEOUT',\n TOOL_INPUT_INVALID: 'TOOL_INPUT_INVALID',\n // Config\n CONFIG_INVALID: 'CONFIG_INVALID',\n CONFIG_NOT_FOUND: 'CONFIG_NOT_FOUND',\n CONFIG_PARSE_FAILED: 'CONFIG_PARSE_FAILED',\n CONFIG_MIGRATION_NEEDED: 'CONFIG_MIGRATION_NEEDED',\n // Plugin\n PLUGIN_LOAD_FAILED: 'PLUGIN_LOAD_FAILED',\n PLUGIN_API_MISMATCH: 'PLUGIN_API_MISMATCH',\n PLUGIN_MISSING_DEPENDENCY: 'PLUGIN_MISSING_DEPENDENCY',\n // Agent\n AGENT_ITERATION_LIMIT: 'AGENT_ITERATION_LIMIT',\n AGENT_CONTEXT_OVERFLOW: 'AGENT_CONTEXT_OVERFLOW',\n AGENT_ABORTED: 'AGENT_ABORTED',\n AGENT_RUN_FAILED: 'AGENT_RUN_FAILED',\n // Session\n SESSION_NOT_FOUND: 'SESSION_NOT_FOUND',\n SESSION_CORRUPTED: 'SESSION_CORRUPTED',\n SESSION_WRITE_FAILED: 'SESSION_WRITE_FAILED',\n // Container / Registry\n CONTAINER_TOKEN_ALREADY_BOUND: 'CONTAINER_TOKEN_ALREADY_BOUND',\n CONTAINER_TOKEN_NOT_BOUND: 'CONTAINER_TOKEN_NOT_BOUND',\n CONTAINER_CIRCULAR_DEPENDENCY: 'CONTAINER_CIRCULAR_DEPENDENCY',\n REGISTRY_DUPLICATE: 'REGISTRY_DUPLICATE',\n REGISTRY_NOT_FOUND: 'REGISTRY_NOT_FOUND',\n REGISTRY_INVALID: 'REGISTRY_INVALID',\n // File system\n FS_READ_FAILED: 'FS_READ_FAILED',\n FS_WRITE_FAILED: 'FS_WRITE_FAILED',\n FS_MKDIR_FAILED: 'FS_MKDIR_FAILED',\n FS_DELETE_FAILED: 'FS_DELETE_FAILED',\n FS_ATOMIC_WRITE_FAILED: 'FS_ATOMIC_WRITE_FAILED',\n // SDD (Spec-Driven Development)\n SDD_VALIDATION_FAILED: 'SDD_VALIDATION_FAILED',\n SDD_PARSE_FAILED: 'SDD_PARSE_FAILED',\n SDD_INVALID_STATE: 'SDD_INVALID_STATE',\n SDD_NOT_READY: 'SDD_NOT_READY',\n // General\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n PARSE_FAILED: 'PARSE_FAILED',\n UNKNOWN: 'UNKNOWN',\n} as const;\n\n/**\n * Union type derived from `ERROR_CODES`. Using `typeof ERROR_CODES[keyof typeof ERROR_CODES]`\n * instead of a string literal union means TypeScript auto-updates the type whenever\n * a new code is added to `ERROR_CODES` \u2014 no need to keep two lists in sync.\n */\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\nexport type ErrorSubsystem =\n | 'provider'\n | 'tool'\n | 'config'\n | 'plugin'\n | 'agent'\n | 'session'\n | 'sdd'\n | 'container'\n | 'fs'\n | 'general';\nexport type ErrorSeverity = 'fatal' | 'error' | 'warning';\n\n// \u2500\u2500 Base error class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class WrongStackError extends Error {\n readonly code: ErrorCode;\n readonly subsystem: ErrorSubsystem;\n readonly severity: ErrorSeverity;\n readonly recoverable: boolean;\n readonly context?: Record<string, unknown> | undefined;\n\n constructor(opts: {\n message: string;\n code: ErrorCode;\n subsystem: ErrorSubsystem;\n severity?: ErrorSeverity | undefined;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super(opts.message, { cause: opts.cause });\n this.name = 'WrongStackError';\n this.code = opts.code;\n this.subsystem = opts.subsystem;\n this.severity = opts.severity ?? 'error';\n this.recoverable = opts.recoverable ?? false;\n this.context = opts.context;\n }\n\n /**\n * Render a one-line user-facing description.\n * Subclasses should override for domain-specific formatting.\n */\n describe(): string {\n const ctx = this.context ? ` ${formatContext(this.context)}` : '';\n return `${this.code}: ${this.message}${ctx}`;\n }\n}\n\nfunction formatContext(ctx: Record<string, unknown>): string {\n const parts = Object.entries(ctx)\n .filter(([, v]) => v !== undefined)\n .slice(0, 3)\n .map(([k, v]) => `${k}=${String(v)}`);\n return parts.length > 0 ? `[${parts.join(' ')}]` : '';\n}\n\n// \u2500\u2500 Specific error classes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Tool execution errors \u2014 thrown by ToolExecutor and individual tools.\n */\nexport class ToolError extends WrongStackError {\n readonly toolName: string;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n | 'TOOL_NOT_FOUND'\n | 'TOOL_PERMISSION_DENIED'\n | 'TOOL_EXECUTION_FAILED'\n | 'TOOL_TIMEOUT'\n | 'TOOL_INPUT_INVALID'\n >;\n toolName: string;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'tool',\n recoverable: opts.recoverable,\n context: { tool: opts.toolName, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ToolError';\n this.toolName = opts.toolName;\n }\n}\n\n/**\n * Config loading / validation errors.\n */\nexport class ConfigError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'CONFIG_INVALID' | 'CONFIG_NOT_FOUND' | 'CONFIG_PARSE_FAILED' | 'CONFIG_MIGRATION_NEEDED'\n >;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'config',\n severity: 'fatal',\n recoverable: false,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'ConfigError';\n }\n}\n\n/**\n * Plugin loading / lifecycle errors.\n */\nexport class PluginError extends WrongStackError {\n readonly pluginName: string;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'PLUGIN_LOAD_FAILED' | 'PLUGIN_API_MISMATCH' | 'PLUGIN_MISSING_DEPENDENCY'\n >;\n pluginName: string;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'plugin',\n severity: 'error',\n recoverable: opts.code === ERROR_CODES.PLUGIN_MISSING_DEPENDENCY,\n context: { plugin: opts.pluginName, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'PluginError';\n this.pluginName = opts.pluginName;\n }\n}\n\n/**\n * Agent runtime errors \u2014 thrown by Agent.run when a non-WrongStackError\n * escapes the inner loop, so callers always see a structured error.\n */\nexport class AgentError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'AGENT_ITERATION_LIMIT' | 'AGENT_CONTEXT_OVERFLOW' | 'AGENT_ABORTED' | 'AGENT_RUN_FAILED'\n >;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'agent',\n severity: opts.code === ERROR_CODES.AGENT_ABORTED ? 'warning' : 'error',\n recoverable: opts.recoverable ?? opts.code === ERROR_CODES.AGENT_ITERATION_LIMIT,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'AgentError';\n }\n}\n\n/**\n * Wrap an arbitrary thrown value into a `WrongStackError` so the caller\n * always gets a structured error. Pass-throughs WrongStackError instances\n * unchanged; raw `Error`s and primitives get an `AGENT_RUN_FAILED` wrapper\n * with the original preserved as `cause`.\n */\nexport function toWrongStackError(\n err: unknown,\n code: Extract<ErrorCode, 'AGENT_RUN_FAILED' | 'AGENT_ABORTED' | 'UNKNOWN'> = ERROR_CODES.AGENT_RUN_FAILED,\n): WrongStackError {\n if (err instanceof WrongStackError) return err;\n const message = toErrorMessage(err);\n return new AgentError({\n message,\n code: code === 'UNKNOWN' ? ERROR_CODES.AGENT_RUN_FAILED : code,\n cause: err,\n });\n}\n\n/**\n * Session storage errors.\n */\nexport class SessionError extends WrongStackError {\n readonly sessionId?: string | undefined;\n\n constructor(opts: {\n message: string;\n code: Extract<ErrorCode, 'SESSION_NOT_FOUND' | 'SESSION_CORRUPTED' | 'SESSION_WRITE_FAILED'>;\n sessionId?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'session',\n severity: opts.code === ERROR_CODES.SESSION_WRITE_FAILED ? 'error' : 'warning',\n recoverable: opts.code !== ERROR_CODES.SESSION_CORRUPTED,\n context: { sessionId: opts.sessionId, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'SessionError';\n this.sessionId = opts.sessionId;\n }\n}\n\n/**\n * SDD (Spec-Driven Development) errors \u2014 spec validation, parsing, and\n * state machine violations in the AISpecBuilder, TaskFlow, and TaskTracker.\n */\nexport class SddError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'SDD_VALIDATION_FAILED' | 'SDD_PARSE_FAILED' | 'SDD_INVALID_STATE' | 'SDD_NOT_READY'\n >;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'sdd',\n severity: opts.code === ERROR_CODES.SDD_PARSE_FAILED ? 'warning' : 'error',\n recoverable: opts.code === ERROR_CODES.SDD_NOT_READY,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'SddError';\n }\n}\n\n/**\n * File system operation errors.\n */\nexport class FsError extends WrongStackError {\n readonly path?: string | undefined;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'FS_READ_FAILED' | 'FS_WRITE_FAILED' | 'FS_MKDIR_FAILED' | 'FS_DELETE_FAILED' | 'FS_ATOMIC_WRITE_FAILED'\n >;\n path?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'fs',\n severity: 'error',\n recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,\n context: { path: opts.path, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'FsError';\n this.path = opts.path;\n }\n}\n\n/**\n * HTTP fetch error \u2014 thrown when a network request returns a non-OK status.\n * Carries the response status so {@link classifyToolError} can branch on it\n * (429 \u2192 transient, 404 \u2192 not_found, 401 \u2192 permission) without duck-typing\n * the error via `'response' in err`.\n *\n * P3 #18 (before-release.md): the previous `'response' in err` check caught\n * any Error with a `response` property, including custom errors, proxy\n * objects, or mocked errors in tests. `instanceof FetchError` is reliable.\n *\n * Tools and providers that make HTTP requests and need the executor to\n * classify their failures should throw `new FetchError({ status, message })`\n * instead of a bare `Error` with an ad-hoc `response` field.\n */\nexport class FetchError extends WrongStackError {\n readonly status: number;\n\n constructor(opts: {\n message: string;\n status: number;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n severity: 'error',\n recoverable: opts.status === 429 || opts.status >= 500,\n context: { status: opts.status, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'FetchError';\n this.status = opts.status;\n }\n}\n\n/**\n * Tool input validation error \u2014 thrown when a tool's input fails a validation\n * check that the JSON Schema cannot express (e.g. `old_string === new_string`\n * in edit, or a cross-field invariant). Use this instead of a bare\n * `throw new Error('...validation...')` so {@link classifyToolError} can\n * match on `instanceof` rather than a locale-dependent message substring.\n *\n * P2 #6 (before-release.md): the previous `err.message.includes('validation')`\n * check misclassified any error whose message happened to contain \"validation\"\n * (e.g. a third-party \"input validation timeout\") as a VALIDATION error.\n *\n * Named `ToolValidationError` (not `ValidationError`) to avoid colliding with\n * the existing `ValidationError` interface exported by json-schema-validate.ts\n * (a validation-result shape, not an Error subclass).\n */\nexport class ToolValidationError extends WrongStackError {\n constructor(opts: {\n message: string;\n /** Field path or tool name that failed validation, for diagnostics. */\n field?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n severity: 'error',\n recoverable: false,\n context: { field: opts.field, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ToolValidationError';\n }\n}\n\n/**\n * Response / payload parse error \u2014 thrown when an upstream HTTP response,\n * file, or data structure is well-formed at the transport layer (HTTP 200,\n * valid JSON) but is missing required fields or has an unexpected shape.\n *\n * Distinct from `ConfigError(CONFIG_PARSE_FAILED)` (which is specifically\n * for config-file parsing) and `FetchError` (which covers HTTP non-OK\n * responses). `ParseError` fills the gap: the request succeeded but the\n * response body couldn't be interpreted.\n *\n * Common sites: OAuth token responses missing `access_token`, device-code\n * responses missing `device_code`, registry responses with unexpected\n * schemas.\n */\nexport class ParseError extends WrongStackError {\n readonly source?: string | undefined;\n\n constructor(opts: {\n message: string;\n /**\n * What was being parsed \u2014 e.g. `'oauth-token-response'`,\n * `'device-code-response'`. Lets consumers distinguish parse failures\n * from different upstream APIs without parsing the message.\n */\n source?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.PARSE_FAILED,\n subsystem: 'general',\n severity: 'error',\n recoverable: false,\n context: { source: opts.source, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ParseError';\n this.source = opts.source;\n }\n}\n\n// \u2500\u2500 Type guards \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function isWrongStackError(err: unknown): err is WrongStackError {\n return err instanceof WrongStackError;\n}\n\nexport function isToolError(err: unknown): err is ToolError {\n return err instanceof ToolError;\n}\n\nexport function isConfigError(err: unknown): err is ConfigError {\n return err instanceof ConfigError;\n}\n\nexport function isPluginError(err: unknown): err is PluginError {\n return err instanceof PluginError;\n}\n\nexport function isSessionError(err: unknown): err is SessionError {\n return err instanceof SessionError;\n}\n\nexport function isAgentError(err: unknown): err is AgentError {\n return err instanceof AgentError;\n}\n\nexport function isFsError(err: unknown): err is FsError {\n return err instanceof FsError;\n}\n\nexport function isToolValidationError(err: unknown): err is ToolValidationError {\n return err instanceof ToolValidationError;\n}\n\nexport function isFetchError(err: unknown): err is FetchError {\n return err instanceof FetchError;\n}\n\nexport function isParseError(err: unknown): err is ParseError {\n return err instanceof ParseError;\n}\n\nexport function isSddError(err: unknown): err is SddError {\n return err instanceof SddError;\n}\n", "/**\n * Build a sanitized child-process environment.\n *\n * The bash/exec tools and MCP stdio transports execute LLM-generated or\n * configured commands. The parent process carries provider API keys\n * (ANTHROPIC_API_KEY, OPENAI_API_KEY, ...), VCS tokens (GITHUB_TOKEN),\n * and cloud credentials. Forwarding those to a child is an exfiltration\n * vector even with `permission: 'confirm'` \u2014 a compromised MCP server\n * or a cleverly composed shell pipeline can leak secrets.\n *\n * Strategy: copy a small, explicit allowlist of variables that real builds\n * need, then copy anything else that does NOT look secret-bearing. This\n * preserves user-friendly behavior (locale, terminal, npm config) while\n * blocking the obvious leak channels. Two value-side guards back up the\n * name-based filter:\n * - any value carrying an embedded URI credential (`scheme://user:pass@host`,\n * e.g. `DATABASE_URL`/`REDIS_URL`/`*_DSN`) is dropped (WS-01);\n * - `NODE_OPTIONS` is forwarded but with module-preload directives\n * (`--require`/`--import`/`--loader`) stripped so a parent-set value can't\n * inject code into node children (WS-02).\n *\n * Override with `WRONGSTACK_CHILD_ENV_PASSTHROUGH=1` to forward the full\n * parent environment unchanged (opt-in for advanced users who understand\n * the risk).\n */\n\nconst ALLOWED_KEYS = new Set<string>([\n 'PATH',\n 'HOME',\n 'USER',\n 'USERNAME',\n 'LOGNAME',\n 'SHELL',\n 'LANG',\n 'LC_ALL',\n 'LC_CTYPE',\n 'TERM',\n 'TZ',\n 'TMPDIR',\n 'TEMP',\n 'TMP',\n 'PWD',\n 'OLDPWD',\n 'COMSPEC',\n 'SYSTEMROOT',\n 'SYSTEMDRIVE',\n 'WINDIR',\n 'PROGRAMFILES',\n 'PROGRAMFILES(X86)',\n 'PROGRAMDATA',\n 'APPDATA',\n 'LOCALAPPDATA',\n 'USERPROFILE',\n 'PUBLIC',\n 'PATHEXT',\n]);\n\n// Substring match against env-var names (case-insensitive). Bias toward\n// false-positives \u2014 a missing var is recoverable, an exfiltrated key is not.\n// Only consulted for vars NOT on the curated allowlist; PWD/PASSWD-style\n// false positives there are avoided by checking allowlist first.\nconst SECRET_NAME_PARTS = [\n 'TOKEN',\n 'SECRET',\n 'PASSWORD',\n 'PASSWD',\n 'AUTH',\n 'CRED',\n 'BEARER',\n 'COOKIE',\n 'PRIVATE',\n];\n\nfunction looksSecret(name: string): boolean {\n const upper = name.toUpperCase();\n for (const p of SECRET_NAME_PARTS) {\n if (upper.includes(p)) return true;\n }\n // KEY is tricky \u2014 PUBLIC_KEY is fine to forward but most _KEY vars are\n // secrets. Require word boundary so KEYBOARD_LAYOUT etc. are not flagged.\n if (/(?:^|_)KEY(?:$|_|S$)/i.test(upper)) return true;\n if (/API[_-]?KEY/i.test(upper)) return true;\n if (/ACCESS[_-]?KEY/i.test(upper)) return true;\n if (/SESSION[_-]?ID/i.test(upper) === false && /SESSION/i.test(upper)) {\n // SESSION_ID is metadata (we set our own); other SESSION_* often holds\n // session cookies. Be conservative.\n return true;\n }\n return false;\n}\n\n/**\n * Value-side secret detection (WS-01). The name-based `looksSecret` filter\n * misses connection-string variables whose NAME is innocuous but whose VALUE\n * embeds a password \u2014 e.g. `DATABASE_URL=postgres://user:pass@host`,\n * `REDIS_URL=redis://:pass@host`, `MONGO_URI`, `AMQP_URL`, `*_DSN`. Forwarding\n * these to a child (bash/exec/MCP server) leaks the embedded credential.\n *\n * Matches a URI userinfo component that contains a password, i.e.\n * `scheme://[user]:<password>@host`. Deliberately precise: a credential-free\n * URL (`https://api.example.com`, `https://user@host` with no password) is NOT\n * matched, so non-secret `*_URL` knobs (registries, endpoints) still forward.\n */\nfunction valueHasEmbeddedCredential(value: string): boolean {\n // scheme:// then optional user, a ':' , a non-empty password, then '@'.\n // Userinfo chars stop at '/', whitespace, ':' (separator) and '@'.\n return /\\b[a-z][a-z0-9+.-]*:\\/\\/[^/\\s:@]*:[^/\\s@]+@/i.test(value);\n}\n\n/**\n * Code-injection directives that turn `NODE_OPTIONS` into an RCE channel by\n * preloading an arbitrary module into every node child process (WS-02).\n */\nconst NODE_OPTIONS_INJECTION_FLAG =\n /^(?:--require|-r|--import|--loader|--experimental-loader)$/;\nconst NODE_OPTIONS_INJECTION_FLAG_EQ =\n /^(?:--require|-r|--import|--loader|--experimental-loader)=/;\n\n/**\n * Strip module-preload directives from a `NODE_OPTIONS` value while preserving\n * benign flags (`--no-warnings`, `--max-old-space-size=\u2026`, etc.). Handles both\n * the `--require=./x.js` and space-separated `--require ./x.js` forms. Returns\n * the sanitized string (possibly empty).\n */\nexport function sanitizeNodeOptions(value: string): string {\n const tokens = value.split(/\\s+/).filter(Boolean);\n const kept: string[] = [];\n for (let i = 0; i < tokens.length; i++) {\n const tok = tokens[i] as string;\n if (NODE_OPTIONS_INJECTION_FLAG_EQ.test(tok)) continue; // --require=./x\n if (NODE_OPTIONS_INJECTION_FLAG.test(tok)) {\n i++; // also drop the following path token (--require ./x)\n continue;\n }\n kept.push(tok);\n }\n return kept.join(' ');\n}\n\nexport interface BuildChildEnvOptions {\n /** Session ID to inject as WRONGSTACK_SESSION_ID. */\n sessionId?: string | undefined;\n /** Additional env vars to merge (takes priority over filtered parent env). */\n extra?: NodeJS.ProcessEnv | undefined;\n}\n\n/**\n * Commit identity applied to every git-touching child process via the\n * `GIT_AUTHOR_*` / `GIT_COMMITTER_*` env vars. Env-var identity outranks\n * every `git config` layer (repo/global/system), so commits made by any\n * tool (git tool, bash/exec, worktree manager, plugins) carry this\n * name/email without touching the user's git config \u2014 hand-made commits\n * in a normal terminal are unaffected.\n */\nexport interface GitIdentity {\n name?: string | undefined;\n email?: string | undefined;\n}\n\nlet gitIdentity: GitIdentity | null = null;\n\n/**\n * Set (or clear with `null`) the git commit identity injected by\n * `buildChildEnv()`. Wired at boot from the user-level `config.git.identity`\n * (the config loader strips `git` from repo-committed in-project configs \u2014\n * identity spoofing must not be repo-controllable) and re-applied at runtime\n * by the `/gitid` slash command.\n */\nexport function configureChildEnvGitIdentity(identity: GitIdentity | null | undefined): void {\n const name = identity?.name?.trim();\n const email = identity?.email?.trim();\n gitIdentity = name || email ? { name: name || undefined, email: email || undefined } : null;\n}\n\n/** Current configured git identity, or null when none is set. */\nexport function getChildEnvGitIdentity(): Readonly<GitIdentity> | null {\n return gitIdentity;\n}\n\n/**\n * Build a filtered child-process environment suitable for bash, exec, and\n * MCP server subprocesses. Strips API keys, tokens, and other credentials\n * while preserving system/tooling variables.\n */\nexport function buildChildEnv(optsOrSessionId?: BuildChildEnvOptions | string): NodeJS.ProcessEnv {\n const opts: BuildChildEnvOptions =\n typeof optsOrSessionId === 'string'\n ? { sessionId: optsOrSessionId }\n : (optsOrSessionId ?? {});\n\n // WRONGSTACK_CHILD_ENV_PASSTHROUGH may NOT be set via config file.\n // It is a privileged override that opt-outs the entire credential filter\n // and must only be set by the operator's shell environment (real env var,\n // not something a config file injects into process.env). Config-file\n // sources do NOT go through process.env \u2014 only the actual shell environment\n // does \u2014 so checking Object.prototype.hasOwnProperty.call(process.env, ...)\n // is sufficient to exclude config-driven values.\n const hasOwn = Object.hasOwn(process.env, 'WRONGSTACK_CHILD_ENV_PASSTHROUGH');\n const legacyHasOwn = Object.hasOwn(process.env, 'WRONGSTACK_BASH_ENV_PASSTHROUGH');\n const passthrough = (hasOwn && process.env['WRONGSTACK_CHILD_ENV_PASSTHROUGH'] === '1')\n || (legacyHasOwn && process.env['WRONGSTACK_BASH_ENV_PASSTHROUGH'] === '1');\n if (passthrough && !process.env['CI']) {\n console.warn(\n '[agent] WARNING: WRONGSTACK_*_ENV_PASSTHROUGH=1 is active \u2014\\n' +\n ' all parent env vars (including API keys) forwarded to child processes.\\n' +\n ' Do not use on shared or multi-tenant systems.'\n );\n }\n const out: NodeJS.ProcessEnv = {};\n\n // The CLI entry defaults NODE_ENV=production (so React/Ink resolve their\n // production builds \u2014 see cli-main) and marks the injection with this\n // flag. The injected value must NOT reach children: NODE_ENV=production\n // makes `pnpm install` skip devDependencies and flips test-runner\n // behavior. Strip both vars whenever the flag says wrongstack set them \u2014\n // a NODE_ENV genuinely exported by the operator's shell (flag absent)\n // is forwarded unchanged. Applies in passthrough mode too: passthrough\n // means \"the operator's real environment\", which this value is not.\n const nodeEnvDefaulted = process.env['WRONGSTACK_NODE_ENV_DEFAULTED'] === '1';\n\n for (const [k, v] of Object.entries(process.env)) {\n if (v === undefined) continue;\n if (nodeEnvDefaulted && (k === 'NODE_ENV' || k === 'WRONGSTACK_NODE_ENV_DEFAULTED')) continue;\n if (passthrough) {\n out[k] = v;\n continue;\n }\n const upper = k.toUpperCase();\n // 0. Strip any value with an embedded URI credential (user:pass@host),\n // regardless of the variable name (WS-01). Applied before the allowlist\n // so even a \"system\" name carrying a connection string is caught.\n if (valueHasEmbeddedCredential(v)) continue;\n // 1. Forward names on the explicit allowlist \u2014 these are well-known\n // non-secret system variables (PATH, HOME, LANG, ...).\n if (ALLOWED_KEYS.has(upper)) {\n out[k] = v;\n continue;\n }\n // 2. Strip anything that looks like a secret.\n if (looksSecret(upper)) continue;\n // NODE_OPTIONS is forwarded (builds rely on flags like --no-warnings) but\n // module-preload directives (--require/--import/--loader) are stripped \u2014\n // they would let a parent-set NODE_OPTIONS inject code into every node\n // child (WS-02 defense-in-depth).\n if (upper === 'NODE_OPTIONS') {\n const sanitized = sanitizeNodeOptions(v);\n if (sanitized) out[k] = sanitized;\n continue;\n }\n // 3. Forward tooling-prefixed vars that builds commonly need, unless\n // they already failed the secret check above.\n if (\n upper.startsWith('NODE_') ||\n upper.startsWith('NPM_') ||\n upper.startsWith('PNPM_') ||\n upper.startsWith('YARN_') ||\n upper.startsWith('GIT_') ||\n upper.startsWith('CI') ||\n upper.startsWith('XDG_') ||\n // Our own non-secret knobs (WRONGSTACK_HOME, WRONGSTACK_SESSION_ID, \u2026).\n // Secrets never live in WRONGSTACK_* env vars (they're in the encrypted\n // vault). Forwarding keeps child wstack processes \u2014 e.g. ones spawned\n // by the test suite \u2014 inside the same redirected global root.\n upper.startsWith('WRONGSTACK_') ||\n upper === 'EDITOR' ||\n upper === 'VISUAL' ||\n upper === 'PAGER'\n ) {\n out[k] = v;\n }\n }\n\n // Configured commit identity. Applied in passthrough mode too \u2014 it is the\n // operator's explicit intent, not a parent-env leak. Placed BEFORE the\n // extras merge so a caller-provided GIT_* override still wins.\n if (gitIdentity) {\n if (gitIdentity.name) {\n out['GIT_AUTHOR_NAME'] = gitIdentity.name;\n out['GIT_COMMITTER_NAME'] = gitIdentity.name;\n }\n if (gitIdentity.email) {\n out['GIT_AUTHOR_EMAIL'] = gitIdentity.email;\n out['GIT_COMMITTER_EMAIL'] = gitIdentity.email;\n }\n }\n\n // Merge explicit extras AFTER filtering. Callers MUST treat `opts.extra`\n // as a small, user-authored allowlist (e.g. MCP server tokens, LSP env\n // overrides from config). Do NOT pass `process.env` or any object derived\n // from it \u2014 that would defeat the parent-env scrub above. The secret\n // filter is intentionally skipped here so legitimate secret-bearing\n // tokens the user explicitly configured can still reach the child.\n if (opts.extra) {\n Object.assign(out, opts.extra);\n }\n\n if (opts.sessionId) out['WRONGSTACK_SESSION_ID'] = opts.sessionId;\n return out;\n}\n", "/**\n * TTY detection helpers \u2014 the single source of truth for \"is this process\n * running against a real terminal?\". Replaces ad-hoc `process.stdin.isTTY`\n * / `process.stdout.isTTY` checks scattered across the codebase so that:\n *\n * 1. test code can mock a single module instead of stubbing `isTTY` on\n * every ReadStream/WriteStream the test happens to touch;\n * 2. a future TTY-detection source (an env var override, a Windows\n * ConPTY workaround, \u2026) lands in one place;\n * 3. `isInteractive()` encodes the rule the project already used inline\n * (\"both streams are TTYs AND we're not running under CI\") in one\n * testable helper instead of the same 3-condition check in two\n * different files.\n *\n * Scope: detection only. Raw-mode control (`setRawMode`), resize\n * subscriptions, and write-injection belong to a future, larger TTY\n * abstraction; this module is the smallest pull that gives us a\n * testable seam and dedups 20+ call sites.\n */\n\nconst hasStdout = (): boolean => typeof process !== 'undefined' && !!process.stdout;\nconst hasStdin = (): boolean => typeof process !== 'undefined' && !!process.stdin;\n\n/** True when `process.stdout` is attached to a terminal (not a pipe/file). */\nexport function isStdoutTTY(): boolean {\n return hasStdout() && Boolean(process.stdout.isTTY);\n}\n\n/** True when `process.stdin` is attached to a terminal (not a pipe/file). */\nexport function isStdinTTY(): boolean {\n return hasStdin() && Boolean(process.stdin.isTTY);\n}\n\n/**\n * True when the current process is an interactive session: both stdin and\n * stdout are TTYs. Callers that also need a \"not a single-shot invocation\"\n * or \"not under CI\" check should layer that on top \u2014 keeping this helper\n * minimal preserves the original inline checks it replaces.\n */\nexport function isInteractive(): boolean {\n return isStdinTTY() && isStdoutTTY();\n}\n\n/** Current terminal size in characters, with a 24\u00D780 fallback for non-TTYs. */\nexport function getTermSize(): { rows: number; cols: number } {\n if (!hasStdout()) return { rows: 24, cols: 80 };\n return {\n rows: process.stdout.rows ?? 24,\n cols: process.stdout.columns ?? 80,\n };\n}\n\n/**\n * Subscribe to terminal resize events. `cb` is called with the new size each\n * time the underlying stream emits `resize`. Returns a cleanup function the\n * caller MUST call on dispose to remove the listener \u2014 leaving a stale\n * `resize` listener on a disposed component leaks the closure (and the\n * component itself, transitively) until the process exits.\n *\n * The stream argument defaults to `process.stdout`. Pass an explicit\n * `NodeJS.WriteStream` when the caller already owns one (e.g. a status line\n * that targets an injected `out` for testability). For non-TTY streams no\n * listener is registered and the returned cleanup is a no-op.\n */\nexport function onResize(\n cb: (size: { rows: number; cols: number }) => void,\n stream: NodeJS.WriteStream = process.stdout,\n): () => void {\n if (!stream || typeof stream.on !== 'function') return () => {};\n const handler = (): void => {\n cb({\n rows: stream.rows ?? 24,\n cols: stream.columns ?? 80,\n });\n };\n stream.on('resize', handler);\n return () => {\n stream.off('resize', handler);\n };\n}\n\n/**\n * Toggle raw mode on a TTY stdin stream. Returns `true` when the toggle was\n * applied, `false` when the stream is null, not a TTY, or doesn't expose\n * `setRawMode` (pipes, file descriptors, Windows ConPTY edge cases). Callers\n * that need to restore the previous mode should snapshot `input.isRaw`\n * BEFORE the call and pass the value to a second call to flip back.\n *\n * Use this helper to drop the now-redundant\n * `if (input.isTTY) input.setRawMode(...)` ceremony at every call site.\n */\nexport function setRawMode(input: NodeJS.ReadStream, mode: boolean): boolean {\n if (input?.isTTY !== true) return false;\n if (typeof input.setRawMode !== 'function') return false;\n input.setRawMode(mode);\n return true;\n}\n\n/**\n * Bracket installed by the interactive input reader while a `readline`\n * prompt is on screen. Out-of-band terminal writes \u2014 logger WARN/INFO\n * lines, async activity from the Telegram bridge, etc. \u2014 go to the same\n * physical terminal as the half-typed prompt but readline has no idea they\n * happened, so it never repaints. The result is the classic corruption the\n * user sees: every async line strands the in-progress draft as a fresh\n * scrollback row (sometimes with its cursor underline).\n *\n * The guard closes that gap. `suspend()` wipes the draft row so the message\n * prints clean; `resume()` repaints the prompt + draft (cursor preserved).\n * When no prompt is active the guard is `null` and writes pass straight\n * through \u2014 so agent-turn output (spinner, renderer) is untouched.\n */\nexport interface OutputLineGuard {\n /** Clear the current input row right before an out-of-band write. */\n suspend(): void;\n /** Repaint the prompt + in-progress draft right after the write. */\n resume(): void;\n}\n\nlet activeOutputGuard: OutputLineGuard | null = null;\n\n/**\n * Register (or clear, with `null`) the guard that brackets out-of-band\n * writes. Installed by {@link writeOut}/{@link writeErr} consumers \u2014 in\n * practice the CLI's readline input reader \u2014 only while a prompt is live.\n * Idempotent; the most recent caller wins.\n */\nexport function setOutputLineGuard(guard: OutputLineGuard | null): void {\n activeOutputGuard = guard;\n}\n\n/**\n * Stream-agnostic write primitive. Returns `false` when the stream is\n * missing or doesn't expose `write` so callers can degrade silently under\n * hostile host environments (closed pipe, mock injects `null`, test\n * replaces the stream with a stub).\n *\n * When an {@link OutputLineGuard} is installed (a readline prompt is on\n * screen) the write is bracketed by `suspend()`/`resume()` so the user's\n * half-typed input survives the interruption instead of being stranded in\n * scrollback. The guard's own redraw uses raw stream writes \u2014 never\n * `writeOut`/`writeErr` \u2014 so there is no re-entrancy here.\n *\n * **Not exported in the public API.** Exposed only inside `term.ts` for\n * `writeOut` / `writeErr` to share a single implementation. If a caller\n * needs to write to an arbitrary stream, they should call `writeOut` (or\n * `writeErr`) with an explicit `stream` argument \u2014 the named functions\n * are the public surface so the \"this is the standard error stream\"\n * intent stays visible at every call site.\n */\nfunction writeTo(\n s: string,\n stream: NodeJS.WriteStream | undefined,\n): boolean {\n if (!stream || typeof stream.write !== 'function') return false;\n const guard = activeOutputGuard;\n if (!guard) {\n stream.write(s);\n return true;\n }\n // A prompt is live \u2014 wipe the draft row, emit the message, repaint.\n guard.suspend();\n stream.write(s);\n guard.resume();\n return true;\n}\n\n/**\n * Write `s` to `stream` (defaults to `process.stdout`). Returns `false`\n * when the stream is missing or doesn't expose `write` so callers can\n * degrade silently under hostile host environments (closed pipe, mock\n * injects `null`, test replaces the stream with a stub).\n *\n * Why a helper:\n * 1. **Single seam for output capture in tests** \u2014 stub `writeOut` once\n * and assert on what the rest of the codebase intended to print,\n * without spying on `process.stdout.write` (which is brittle and\n * leaks across parallel test files).\n * 2. **Stream swap without grep** \u2014 routing the CLI's output to a\n * logger or `out.log` becomes a one-line change at process boot.\n * 3. **Defensive default** \u2014 closes the \"what if `process.stdout` is\n * `null`\" gap that currently exists at ~50 call sites that just\n * call `process.stdout.write(s)` and crash on certain Windows\n * redirect invocations.\n *\n * Call-site migration is staged: this commit introduces the helper, a\n * follow-up commit replaces the 50+ `process.stdout.write(...)` sites\n * with `writeOut(...)`. Until that migration lands, both forms coexist\n * and `writeOut` is the preferred form for new code.\n */\nexport function writeOut(\n s: string,\n stream: NodeJS.WriteStream = process.stdout,\n): boolean {\n return writeTo(s, stream);\n}\n\n/**\n * Symmetric partner of `writeOut` for the standard error stream. Same shape,\n * same defensive contract, same single-seam-for-tests story \u2014 just defaults to\n * `process.stderr` instead of `process.stdout`.\n *\n * Use this in code paths that emit error/diagnostic/warning text. Keeping\n * these two helpers split (rather than a single `writeTo(s, stream)`) means\n * the call site reads as a clear intent signal: \"I am writing an error\" vs.\n * \"I am writing a result\" \u2014 which matters for callers that decide between\n * stdout/stderr routing (e.g. `--quiet` flags, log-level filtering,\n * structured-log rewriters that fork on stream).\n *\n * Stderr writes from the core logger (see `infrastructure/logger.ts`) and from\n * the TUI guard (see `tui/run-tui.ts`) used to call `process.stderr.write`\n * directly. Routing them through this helper lets tests stub the stream at\n * one boundary and lets future logging middleware (e.g. a JSON-line rewriter)\n * swap the destination for the entire process in one place.\n */\nexport function writeErr(\n s: string,\n stream: NodeJS.WriteStream = process.stderr,\n): boolean {\n return writeTo(s, stream);\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// TerminalCapability \u2014 startup capability profile\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Color depth the terminal can render.\n *\n * 0 = no color (dumb, redirected, `TERM=dumb`)\n * 1 = 16 colors (basic ANSI)\n * 2 = 256 colors (ANSI 256 + bright variants)\n * 3 = 16.7M / 24-bit truecolor\n */\nexport type ColorDepth = 0 | 1 | 2 | 3;\n\n/**\n * Mouse tracking protocol the terminal speaks.\n *\n * 'none' \u2014 no mouse reporting\n * 'x10' \u2014 basic button press/release, capped at 223 columns\n * 'urxvt'\u2014 URXVT extension, supports >223 cols\n * 'sgr' \u2014 SGR extended mode, modern standard, no column cap\n */\nexport type MouseProtocol = 'none' | 'x10' | 'urxvt' | 'sgr';\n\n/**\n * A snapshot of the terminal's capabilities and identity, computed once at\n * startup. Call {@link detectTerminal} once and pass the result through your\n * app \u2014 never re-detect mid-session.\n *\n * Query once \u2192 adapt once. Re-querying on every render is wrong because\n * `$TERM` is static for the process lifetime and stdout.isTTY can only go\n * from true\u2192false (never the reverse), so a mid-session change means the\n * process was started in a terminal and then something went wrong.\n */\nexport interface TerminalCapability {\n /** True when both stdin and stdout are attached to a terminal. */\n isRealTTY: boolean;\n /**\n * Whether the terminal speaks color.\n *\n * Uses the industry-standard precedence:\n * `FORCE_COLOR=0` \u2192 0 (disabled)\n * `FORCE_COLOR` \u2192 3 (force truecolor)\n * `NO_COLOR=\u2026` \u2192 0 (opted out)\n * `COLORTERM=truecolor|24bit` \u2192 3\n * `TERM=\u2026truecolor|24bit` \u2192 3 (some emulators advertise it)\n * `TERM=\u2026256color` \u2192 2\n * fallback \u2192 1\n *\n * Note: `TERM=dumb` always produces 0 even if `COLORTERM=truecolor` is set \u2014\n * `dumb` terminals are non-interactive by definition.\n */\n colorDepth: ColorDepth;\n /**\n * Whether `stdout` can be written to. Determined once at startup from\n * `stdout?.isTTY ?? false`. Even when `isRealTTY` is true, `stdout` may be\n * writable=false (e.g. the terminal was closed mid-session).\n */\n stdoutWritable: boolean;\n /**\n * Best mouse protocol the terminal speaks. Progressive enhancement:\n * attempt SGR first, fall back to URXVT, then X10, then 'none'.\n *\n * The caller (typically the App component) decides whether to enable\n * tracking at all \u2014 this only reports what the terminal CAN understand.\n */\n mouseProtocol: MouseProtocol;\n /**\n * Whether the terminal title can be set via OSC 0 / OSC 2.\n * True when stdout is a TTY and `$TERM` is not `dumb`.\n */\n canSetTitle: boolean;\n /**\n * Whether the terminal understands the tmux DCS passthrough prefix.\n * Detected by seeing `$TERM=tmux*`. When true, escape sequences should be\n * wrapped: `\\x1bPtmux;\\x1b${seq}\\x1b\\\\`.\n */\n isTmux: boolean;\n /**\n * Whether the terminal is on Windows using the legacy conhost backend\n * (cmd.exe, PowerShell non-ConPTY) rather than ConPTY (Windows Terminal,\n * VS Code Integrated Terminal). Used to apply Windows-specific raw-mode\n * handoff logic.\n *\n * Detected by: platform === 'win32' AND `!process.stdout.getColorDepth?.()`\n * (ConPTY exposes color depth; conhost does not). Also false on non-Windows.\n */\n isWindowsConhost: boolean;\n}\n\n/**\n * Parse `process.env.TERM` into a color-depth guess.\n *\n * We intentionally do NOT use the `supports-color` npm package here because\n * (a) it adds a dependency, (b) it resolves at module-import time, and (c) we\n * need to factor in `FORCE_COLOR` / `NO_COLOR` which may be set after import.\n * This pure function is trivial to test and predictable regardless of import\n * order.\n */\nfunction parseColorDepth(env: { FORCE_COLOR?: string; NO_COLOR?: string; COLORTERM?: string; TERM?: string; }): ColorDepth {\n // `FORCE_COLOR=0` is an explicit opt-out, even when `COLORTERM=truecolor`.\n if (env.FORCE_COLOR === '0') return 0;\n // `FORCE_COLOR` with no value or any truthy value forces truecolor.\n if (env.FORCE_COLOR !== undefined) return 3;\n // Explicit user opt-out.\n if (typeof env.NO_COLOR === 'string' && env.NO_COLOR !== '') return 0;\n // Explicit terminal advertisement.\n const colorterm = (env.COLORTERM ?? '').toLowerCase();\n if (colorterm === 'truecolor' || colorterm === '24bit') return 3;\n // TERM strings that advertise rich color.\n const term = (env.TERM ?? '').toLowerCase();\n if (term.includes('truecolor') || term.includes('24bit')) return 3;\n if (term.includes('256color')) return 2;\n // TERM=dumb = no interactive capability at all.\n if (term === 'dumb') return 0;\n // Default to 16-color \u2014 the safest floor. Modern terminals all override this.\n return 1;\n}\n\n/**\n * Detect the best mouse protocol the terminal speaks.\n *\n * Uses the `$TERM` string as a proxy since Node.js has no runtime query for\n * mouse capability (DECSET 1000/1002/1003 responses are not standardized for\n * programmatic use). The approximation is:\n * - `tmux` + `screen` = SGR (they proxy it)\n * - `xterm*`, `rxvt*`, `konsole`, `gnome*` = SGR (post-2013)\n * - `linux`, `vt100`, `dumb` = none\n * - Everything else = URXVT / X10 (try SGR, fall back gracefully in mouse.ts)\n *\n * This is advisory only. The App component gates mouse tracking behind an\n * explicit user/setting opt-in; false positives just mean the tracking enable\n * sequence is ignored harmlessly.\n */\nfunction parseMouseProtocol(term: string): MouseProtocol {\n const t = term.toLowerCase();\n // Terminals that reliably support SGR (1006) mode.\n if (\n t.startsWith('xterm') ||\n t.startsWith('tmux') ||\n t.startsWith('screen') ||\n t.includes('rxvt-unicode') ||\n t.includes('urxvt') ||\n t.includes('konsole') ||\n t.includes('gnome') ||\n t.includes('foot') ||\n t.includes('alacritty') ||\n t.includes('wezterm') ||\n t.includes('kitty') ||\n t.includes('vscode') ||\n t.includes('Apple_Terminal')\n ) {\n return 'sgr';\n }\n // Known mouse-capable but older terminals.\n if (t.startsWith('rxvt') || t.startsWith('linux') || t.includes('Eterm')) {\n return 'urxvt';\n }\n // vt100 / dumb \u2014 no mouse.\n if (t === 'vt100' || t === 'dumb') return 'none';\n // Default: assume SGR is safe to try; mouse.ts falls back silently.\n return 'sgr';\n}\n\n/**\n * Detect the terminal's capabilities and identity at startup.\n *\n * Call once, early in process boot, before any event-loop async has had a\n * chance to corrupt the read of `process.stdout.isTTY`. Store the result and\n * pass it through the app \u2014 do NOT call this on every render.\n *\n * All `env` lookups default gracefully to safe values when the env var is\n * absent or empty, so the return value is deterministic regardless of what\n * the host's environment looks like.\n */\nexport function detectTerminal(\n opts: {\n stdin?: NodeJS.ReadStream | null;\n stdout?: NodeJS.WriteStream | null;\n env?: typeof process.env;\n } = {},\n): TerminalCapability {\n const stdin = opts.stdin ?? process.stdin;\n const stdout = opts.stdout ?? process.stdout;\n const env = opts.env ?? process.env;\n\n const isRealTTY = (stdin?.isTTY ?? false) && (stdout?.isTTY ?? false);\n const stdoutWritable = isRealTTY && typeof stdout?.write === 'function';\n const term = env.TERM ?? '';\n const isTmux = term.toLowerCase().startsWith('tmux');\n const isWindowsConhost = isRealTTY\n && process.platform === 'win32'\n && typeof (stdout as NodeJS.WriteStream & { getColorDepth?: unknown }).getColorDepth !== 'function';\n\n return {\n isRealTTY,\n colorDepth: isRealTTY ? parseColorDepth(env) : 0,\n stdoutWritable,\n mouseProtocol: parseMouseProtocol(term),\n canSetTitle: isRealTTY && term !== 'dumb',\n isTmux,\n isWindowsConhost,\n };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// TerminalLifecycle \u2014 raw-mode lifecycle manager\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Lifecycle owner for the TTY stdin raw-mode state machine.\n *\n * Raw mode is the most dangerous primitive in TUI code. Getting it wrong leaves\n * the user's terminal in a broken state (line-buffered, no echo) that requires\n * closing and reopening the terminal to recover. This class enforces a strict\n * acquire \u2192 hold \u2192 release lifecycle:\n *\n * - `acquire()` may be called only once (idempotent \u2014 subsequent calls no-op).\n * - `release()` restores the previous mode exactly once.\n * - `release()` is called automatically on process `exit` and `beforeExit`.\n * - Any signal handler (SIGINT, SIGTERM, SIGHUP) that calls `release()` clears\n * its own registration so double-signals don't double-restore.\n *\n * Use this instead of calling `setRawMode()` directly at every call site. The\n * single instance lives at module scope in `run-tui.ts`; components that need\n * to signal \"TUI is shutting down\" call `terminalLifecycle.requestExit()` rather\n * than calling `release()` directly.\n */\nexport class TerminalLifecycle {\n private _active = false;\n\n /**\n * The stdin stream we took raw mode on. Set by `acquire()`; undefined before\n * first call. Used by `release()` and by the ConPTY race-closer in\n * `acquire()`.\n */\n private _stdin: NodeJS.ReadStream | undefined;\n\n /**\n * The `isRaw` snapshot captured before the first `acquire()` call, so\n * `release()` restores to the pre-TUI state rather than blindly disabling\n * raw mode. Null when `acquire()` has never been called.\n */\n private _wasRaw: boolean | null = null;\n\n /**\n * Whether stdin was paused before the TUI took ownership. Readline closes\n * its interface by pausing the shared process stream, so raw mode alone is\n * not enough to make Ink receive bytes during the boot-prompt -> TUI handoff.\n */\n private _wasPaused: boolean | null = null;\n\n /**\n * Request the process to exit. Set the flag, trigger any registered\n * `onRequestExit` callback (typically Ink's `unmount()`), and arm a\n * deadline timer. When the timer fires the process is hard-exited.\n *\n * Safe to call multiple times: subsequent calls no-op after the first.\n */\n requestExit: (exitCode?: number) => void;\n\n /**\n * Callback invoked when `requestExit` is called. Registered by the Ink\n * mount point so unmount() runs before the deadline timer fires.\n */\n onRequestExit: (() => void | Promise<void>) | null = null;\n\n constructor() {\n // Build the requestExit closure here so subclasses / tests can override\n // `requestExit` before calling `acquire()`.\n let exitCode = 0;\n let requested = false;\n this.requestExit = (code = 0) => {\n exitCode = code;\n if (requested) return;\n requested = true;\n // Let the Ink unmount handler run (if registered).\n try {\n this.onRequestExit?.();\n } catch {\n // unmount handler threw \u2014 proceed to hard exit.\n }\n // Hard-exit deadline. `unref()` so the timer doesn't keep the event\n // loop alive if everything else has settled.\n setTimeout(() => process.exit(exitCode), 5_000).unref();\n };\n }\n\n /** True while raw mode is held. */\n get active(): boolean {\n return this._active;\n }\n\n /**\n * Acquire raw mode on `stdin`. Idempotent \u2014 calling twice is safe; only the\n * first call has any effect.\n *\n * On Windows ConPTY, calls `setRawMode` twice: once immediately, then again\n * after the next event-loop tick. This closes the ConPTY race where\n * `readline`'s cleanup (registered before this class is constructed) can\n * restore the original cooked mode after we set raw mode but before Ink's\n * render loop takes over the stdin fd. The double-call ensures the last\n * writer wins.\n *\n * @param stdin - The stdin stream (defaults to `process.stdin`).\n * @returns `true` if raw mode was acquired; `false` if stdin is not a TTY\n * or already shut down.\n */\n acquire(stdin: NodeJS.ReadStream = process.stdin): boolean {\n if (this._active) return false;\n\n // Guard: must be a real TTY, must have setRawMode.\n if (stdin?.isTTY !== true) return false;\n if (typeof stdin.setRawMode !== 'function') return false;\n\n // Snapshot the pre-TUI raw state so release() restores correctly.\n this._wasRaw = stdin.isRaw ?? false;\n this._wasPaused = stdin.isPaused();\n this._stdin = stdin;\n\n stdin.setRawMode(true);\n // A preceding readline prompt normally leaves process.stdin paused.\n // Explicitly start the stream before Ink installs its input listener;\n // otherwise the UI can render perfectly while every key appears dead.\n stdin.resume();\n this._active = true;\n\n // Windows ConPTY double-acquire: schedule a second setRawMode on the next\n // tick so we win the race against readline's \"restore original mode\".\n // Safe on non-Windows (no-op: process.platform !== 'win32').\n if (process.platform === 'win32') {\n setImmediate(() => {\n if (this._active && this._stdin?.isTTY) {\n this._stdin.setRawMode?.(true);\n }\n });\n }\n\n return true;\n }\n\n /**\n * Release raw mode and restore the terminal to the state it was in before\n * `acquire()` was called. Idempotent \u2014 subsequent calls are no-ops.\n *\n * Call this on: SIGINT, SIGTERM, SIGHUP, SIGBREAK, and `process.exit`.\n *\n * \u26A0 **Windows caveat**: `setRawMode(false)` on ConPTY restores the original\n * console mode captured when the process started \u2014 not the mode captured by\n * `acquire()`. On ConPTY, the original mode was already raw (ConPTY starts\n * raw), so this call is typically a no-op on Windows Terminal. On conhost.exe\n * it may restore cooked mode, which is correct for that backend.\n */\n release(): void {\n if (!this._active) return;\n this._active = false;\n\n const stdin = this._stdin;\n if (stdin?.isTTY === true) {\n // Restore to pre-TUI state rather than blindly disabling raw mode.\n stdin.setRawMode?.(this._wasRaw ?? false);\n if (this._wasPaused) stdin.pause();\n }\n this._stdin = undefined;\n this._wasRaw = null;\n this._wasPaused = null;\n }\n\n /**\n * Synchronously reset all terminal state written by the TUI layer:\n * raw mode, SGR attributes, cursor visibility, and mouse tracking.\n *\n * Intended for the final `process.on('exit')` handler where async is\n * impossible. Uses `\\x1b[0m` (SGR reset), `\\x1b[?25h` (cursor show),\n * and `\\x1b[?9l` (mouse off) \u2014 all safe to emit even if the terminal does\n * not understand them (unknown CSI sequences are silently ignored).\n *\n * Safe to call multiple times from multiple exit paths because each write\n * is a constant-time synchronous stream write.\n *\n * @param stdout - Stream to reset (defaults to `process.stdout`).\n */\n reset(stdout: NodeJS.WriteStream = process.stdout): void {\n if (typeof stdout?.write !== 'function') return;\n // SGR reset \u2014 clears bold, color, underline, etc.\n stdout.write('\\x1b[0m');\n // Cursor show (in case Ink's unmount left it hidden).\n stdout.write('\\x1b[?25h');\n // Mouse tracking off (all three DECRST sequences \u2014 safe no-ops if never set).\n stdout.write('\\x1b[?1003l\\x1b[?1002l\\x1b[?1000l');\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// safeEscape \u2014 guarded escape-sequence emitter\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Result of emitting an escape sequence. Distinguishes the three distinct\n * failure modes so callers can degrade gracefully.\n */\nexport interface EscapeEmitResult {\n /** `true` when the sequence was written to the stream. */\n ok: boolean;\n /**\n * Human-readable reason when `ok` is `false`. One of:\n * `'not_a_tty'` \u2014 stdout is not a terminal (pipe / redirect / CI)\n * `'unwritable'` \u2014 stdout.write is missing or threw\n * `'empty'` \u2014 `sequence` was the empty string\n */\n reason: 'not_a_tty' | 'unwritable' | 'empty';\n}\n\n/**\n * A terminal escape sequence with its optional string terminator.\n *\n * OSC (title, color palette) sequences MUST be terminated with `BEL (\\x07)`\n * or `ST (\\x1b\\\\)` or the terminal stays in command mode and corrupts\n * subsequent output. CSI sequences (colors, cursor movement) are\n * self-terminating and do not need a terminator byte.\n */\nexport interface EscapeSequence {\n /** The raw sequence, e.g. `\\x1b[38;2;255;0;0m`. */\n raw: string;\n /**\n * Optional terminator byte required by OSC/DCS families.\n * `undefined` for CSI sequences; `\\x07` (BEL) for OSC; `\\x1b\\\\` (ST) for DCS.\n */\n terminator?: string;\n}\n\n/**\n * Shared OSC/DCS terminator bytes.\n *\n * BEL (`\\x07`) is understood by iTerm2, Konsole, mintty, foot, and\n * Windows Terminal. ST (`\\x1b\\\\`) is the strict DEC standard equivalent.\n * Prefer BEL for compatibility; fall back to ST if the terminal's response\n * to BEL is observed to be garbled.\n */\nexport const ESCAPE_TERMINATOR = Object.freeze({\n BEL: '\\x07', // OSC 0 / OSC 2 (title)\n ST: '\\x1b\\\\', // DCS / strict OSC\n});\n\n/**\n * Emit a terminal escape sequence safely.\n *\n * Rules enforced:\n * 1. Empty string \u2192 returns `ok: false, reason: 'empty'`, no write.\n * 2. Not a TTY \u2192 returns `ok: false, reason: 'not_a_tty'`, no write.\n * 3. Stream write throws \u2192 returns `ok: false, reason: 'unwritable'`.\n * 4. OSC/DCS sequences MUST have a terminator byte (guarded assertion).\n * A missing terminator on an OSC sequence corrupts output on terminals\n * that don't understand the sequence \u2014 this function refuses to emit it.\n *\n * @param sequence - The sequence to emit. CSI sequences need no terminator.\n * OSC/DCS sequences must carry `terminator: '\\x07'` or `'\\x1b\\\\'`.\n * @param stdout - Stream to write to (defaults to `process.stdout`).\n * @returns Result indicating success or the specific failure mode.\n */\nexport function safeEmit(\n sequence: EscapeSequence | string,\n stdout: NodeJS.WriteStream = process.stdout,\n): EscapeEmitResult {\n const raw = typeof sequence === 'string' ? sequence : sequence.raw;\n const term = typeof sequence === 'string' ? undefined : sequence.terminator;\n\n if (!raw) {\n return { ok: false, reason: 'empty' };\n }\n if (stdout?.isTTY !== true) {\n return { ok: false, reason: 'not_a_tty' };\n }\n\n // For OSC/DCS sequences (which include `[` or `]` in the body), verify a\n // terminator is present. CSI sequences start with `\\x1b[` and are\n // self-terminating; we treat any sequence that is not a known OSC/DCS\n // as CSI and allow through. OSC starts with `\\x1b]`; DCS with `\\x1bP`.\n if (raw.startsWith('\\x1b]') || raw.startsWith('\\x1bP')) {\n // Assertion: terminator must be provided. Belt-and-suspenders against\n // callers that forget to append `\\x07` \u2014 this prevents corrupted output.\n if (!term) {\n // Defensive: append BEL rather than crashing. This is the only case\n // where we mutate the sequence. The assert-then-fallback pattern is\n // intentional: we catch programmer error in development but degrade\n // safely in production rather than throwing.\n void term; // suppress unused-variable warning\n const safe = `${raw}\\x07`;\n try {\n stdout.write(safe);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' }; // reason field is ignored when ok=true\n }\n try {\n stdout.write(`${raw}${term}`);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' };\n }\n\n // CSI sequence \u2014 self-terminating.\n try {\n stdout.write(raw);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' };\n}\n\n/**\n * Build an OSC 0 / OSC 2 (window/tab title) sequence with a safe terminator.\n *\n * The terminator defaults to `BEL (\\x07)` for broad terminal compatibility.\n * Use `terminator: ESCAPE_TERMINATOR.ST` for stricter terminals that interpret\n * BEL as a beep.\n *\n * @param title - The title string. Embedded BEL bytes are stripped.\n * @param terminator - Defaults to `ESCAPE_TERMINATOR.BEL`.\n */\nexport function buildTitleSequence(\n title: string,\n terminator: string = ESCAPE_TERMINATOR.BEL,\n): EscapeSequence {\n return {\n raw: `\\x1b]0;${title.replace(/\\x07/g, '')}`,\n terminator,\n };\n}\n\n/**\n * Build an SGR (Select Graphic Rendition) color/style sequence.\n *\n * @param codes - SGR parameter list, e.g. `[31]` (red foreground),\n * `[1;32]` (bold green), `[38;2;255;128;0]` (truecolor orange).\n */\nexport function buildSgrSequence(...codes: number[]): EscapeSequence {\n return { raw: `\\x1b[${codes.join(';')}m` };\n}\n\n/**\n * Emit a title sequence. Convenience wrapper around `safeEmit` + `buildTitleSequence`.\n *\n * @param title - The title string.\n * @param stdout - Target stream.\n */\nexport function setTitle(\n title: string,\n stdout: NodeJS.WriteStream = process.stdout,\n): boolean {\n return safeEmit(buildTitleSequence(title), stdout).ok;\n}\n", "import { isStdoutTTY } from './term.js';\n\nconst isColorTty = (): boolean => {\n if (envFlag(process.env.NO_COLOR)) return false;\n if (envFlag(process.env.FORCE_COLOR)) return true;\n return isStdoutTTY();\n};\n\nfunction envFlag(value: string | undefined): boolean {\n if (value === undefined) return false;\n if (value.trim() === '') return false;\n return !/^(0|false|no|off)$/i.test(value.trim());\n}\n\nconst COLOR = isColorTty();\n\nconst wrap =\n (open: string, close: string) =>\n (s: string): string =>\n COLOR ? `\\x1b[${open}m${s}\\x1b[${close}m` : s;\n\nexport const color = {\n reset: wrap('0', '0'),\n bold: wrap('1', '22'),\n dim: wrap('2', '22'),\n italic: wrap('3', '23'),\n underline: wrap('4', '24'),\n red: wrap('31', '39'),\n green: wrap('32', '39'),\n yellow: wrap('33', '39'),\n blue: wrap('34', '39'),\n magenta: wrap('35', '39'),\n cyan: wrap('36', '39'),\n gray: wrap('90', '39'),\n amber: wrap('38;5;214', '39'),\n pink: wrap('38;5;205', '39'),\n bgRed: wrap('41', '49'),\n bgGreen: wrap('42', '49'),\n};\n\nexport function stripAnsi(s: string): string {\n return s.replace(/\\x1b\\[[0-9;]*[A-Za-z]/g, '');\n}\n", "import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * Minimal paths object needed for config backup \u2014 just the global root\n * from which we derive the backup history directory.\n */\nexport interface ConfigBackupPaths {\n globalRoot: string;\n}\n\n/**\n * Backup directory for config file history.\n * Every config write creates a timestamped snapshot here so the user can\n * recover from accidental changes. Backups are never cleaned up automatically.\n */\nexport function configHistoryDir(globalRoot: string): string {\n return path.join(globalRoot, 'config-history');\n}\n\n/**\n * Derive a human-readable slug for a config file path relative to the\n * ~/.wrongstack root. Examples:\n * config.json \u2192 config\n * profiles/default/config.json \u2192 profiles-default-config\n */\nfunction configSlug(absolutePath: string, globalRoot: string): string {\n const rel = path.relative(globalRoot, absolutePath);\n const normalized = rel.replace(/\\\\/g, '/').replace(/\\.json$/i, '');\n return normalized.replace(/\\//g, '-');\n}\n\n/**\n * Before overwriting a config file, save its current content to the\n * config-history directory with a timestamp. Best-effort: failures are\n * silently ignored so they never block the config write itself.\n */\nexport async function backupConfigFile(\n filePath: string,\n paths: ConfigBackupPaths,\n): Promise<void> {\n let currentContent: string;\n try {\n currentContent = await fs.readFile(filePath, 'utf8');\n if (!currentContent.trim()) return;\n } catch {\n return; // ENOENT or other error \u2014 no current content to back up\n }\n\n const now = new Date();\n const ts = now.toISOString()\n .replace(/[:.]/g, '-')\n .replace(/Z$/, '');\n const slug = configSlug(filePath, paths.globalRoot);\n const backupDir = configHistoryDir(paths.globalRoot);\n const backupFile = path.join(backupDir, `${slug}-${ts}.json`);\n\n try {\n await fs.mkdir(backupDir, { recursive: true });\n await fs.writeFile(backupFile, currentContent, { mode: 0o600, encoding: 'utf8' });\n } catch {\n // best-effort \u2014 never block the config write for a backup failure\n }\n}\n", "/**\n * Lightweight outbound internet connectivity check.\n *\n * Probes a reliable public endpoint to determine if the current machine\n * has working outbound internet access. Used during provider-error handling\n * to distinguish \"the provider's API is down\" from \"our internet is down\" \u2014\n * the two need different remediation (wait vs. fix local network).\n *\n * The result is cached for a short TTL so rapid successive checks don't\n * hammer the probe target.\n */\n\nconst DEFAULT_PROBE_URL = 'https://1.1.1.1';\nconst DEFAULT_TIMEOUT_MS = 5_000;\nconst DEFAULT_TTL_MS = 30_000;\n\ninterface ConnectivityCache {\n ok: boolean;\n at: number;\n}\n\nlet cached: ConnectivityCache | undefined;\n\n/**\n * Returns `true` when the probe endpoint responds successfully (any HTTP\n * status that isn't a server-side failure), `false` on network error or\n * timeout. The result is cached for `ttlMs` (default 30 s).\n *\n * Probing `1.1.1.1` (Cloudflare DNS) is intentional: it's a CDN-backed\n * static page served from virtually every PoP, so a failure almost certainly\n * means the local network is down rather than the probe target being\n * unreachable.\n */\nexport async function checkConnectivity(opts?: {\n /** Probe URL. Default: https://1.1.1.1 */\n probeUrl?: string;\n /** Per-request timeout in ms. Default: 5000 (5 s). */\n timeoutMs?: number;\n /** Cache TTL in ms. Default: 30000 (30 s). Set 0 to force a fresh probe. */\n ttlMs?: number;\n}): Promise<boolean> {\n const url = opts?.probeUrl ?? DEFAULT_PROBE_URL;\n const timeout = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const ttl = opts?.ttlMs ?? DEFAULT_TTL_MS;\n\n // Respect a non-expired cache entry\n if (cached && ttl > 0 && Date.now() - cached.at < ttl) {\n return cached.ok;\n }\n\n cached = { ok: await probe(url, timeout), at: Date.now() };\n return cached.ok;\n}\n\nasync function probe(url: string, timeoutMs: number): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n // The response body is irrelevant; any HTTP response (including\n // 4xx/5xx from the probe target) means the network path works \u2014\n // the server reached us back.\n await fetch(url, { method: 'HEAD', signal: controller.signal });\n return true;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Reset the cached connectivity result so the next call performs a fresh\n * probe. Useful during error recovery when the network state may have\n * changed.\n */\nexport function resetConnectivityCache(): void {\n cached = undefined;\n}\n", "import { createHash } from 'node:crypto';\nimport type { TextBlock } from '../types/blocks.js';\n\nconst keyCache = new WeakMap<readonly TextBlock[], string>();\n\n/**\n * Derive a stable, provider-agnostic cache-partition key from a frozen\n * system-prompt epoch. Requests that share the same stable prefix produce the\n * same key, so provider backends route them to the same automatic-cache\n * partition \u2014 this is what OpenAI's `prompt_cache_key` (and Gemini implicit\n * routing) needs to actually hit the cache on load-balanced deployments.\n *\n * Keyed off the volatile-free `ctx.systemPrompt` epoch array (the per-turn\n * ledger/next-steps blocks are appended AFTER this array, so they never enter\n * the key). Cached by array identity in a WeakMap \u2014 the sha-256 runs once per\n * epoch (a new array = a new epoch, e.g. on mode switch), not per request.\n *\n * Anthropic ignores this field (it uses `ttl` + `cache_control` markers), so\n * setting it is harmless there; only the wires that read `req.cache.key` act on\n * it, gated by their own capability flags.\n */\nexport function deriveCachePrefixKey(systemPrompt: readonly TextBlock[]): string {\n const cached = keyCache.get(systemPrompt);\n if (cached !== undefined) return cached;\n const h = createHash('sha256');\n for (const block of systemPrompt) h.update(block.text).update('\u0000');\n // 128 bits of hex is ample collision resistance for a routing key and keeps\n // the value short enough for provider length limits (OpenAI caps at 128 chars).\n const key = `ws-${h.digest('hex').slice(0, 32)}`;\n keyCache.set(systemPrompt, key);\n return key;\n}\n", "import * as fs from 'node:fs/promises';\nimport { atomicWrite } from './atomic-write.js';\n\nexport type JsonObject = Record<string, unknown>;\nexport type JsonPathSegment = string | number;\nexport type JsonPath = readonly JsonPathSegment[];\n\nexport async function readJsonObjectFile(filePath: string): Promise<JsonObject> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return isJsonObject(parsed) ? parsed : {};\n } catch {\n return {};\n }\n}\n\nexport async function jsonObjectFileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function writeJsonObjectFile(filePath: string, value: JsonObject): Promise<void> {\n await atomicWrite(filePath, JSON.stringify(value, null, 2), { mode: 0o600 });\n}\n\nexport async function updateJsonObjectFile(\n filePath: string,\n mutator: (config: JsonObject) => void | JsonObject | Promise<void | JsonObject>,\n): Promise<JsonObject> {\n const config = await readJsonObjectFile(filePath);\n const maybeNext = await mutator(config);\n const next = maybeNext && isJsonObject(maybeNext) ? maybeNext : config;\n await writeJsonObjectFile(filePath, next);\n return next;\n}\n\nexport function getJsonPath(root: unknown, path: JsonPath): unknown {\n let current = root;\n for (const segment of path) {\n if (typeof segment === 'number') {\n if (!Array.isArray(current)) return undefined;\n current = current[segment];\n continue;\n }\n if (!isJsonObject(current)) return undefined;\n current = current[segment];\n }\n return current;\n}\n\nexport function setJsonPath(root: JsonObject, path: JsonPath, value: unknown): JsonObject {\n if (path.length === 0) {\n if (!isJsonObject(value)) throw new Error('Root config value must be an object');\n return value;\n }\n const parent = ensureJsonParent(root, path);\n const leaf = lastPathSegment(path);\n if (typeof leaf === 'number') {\n if (!Array.isArray(parent)) throw new Error(`Cannot set numeric segment ${leaf} on non-array parent`);\n parent[leaf] = value;\n } else {\n if (!isJsonObject(parent)) throw new Error(`Cannot set property ${leaf} on non-object parent`);\n parent[leaf] = value;\n }\n return root;\n}\n\nexport function removeJsonPath(root: JsonObject, path: JsonPath): boolean {\n if (path.length === 0) return false;\n const parent = getJsonPath(root, path.slice(0, -1));\n const leaf = lastPathSegment(path);\n if (typeof leaf === 'number') {\n if (!Array.isArray(parent) || leaf < 0 || leaf >= parent.length) return false;\n parent.splice(leaf, 1);\n return true;\n }\n if (!isJsonObject(parent) || !(leaf in parent)) return false;\n delete parent[leaf];\n return true;\n}\n\nexport async function setJsonPathInFile(filePath: string, path: JsonPath, value: unknown): Promise<JsonObject> {\n return updateJsonObjectFile(filePath, (config) => setJsonPath(config, path, value));\n}\n\nexport async function removeJsonPathInFile(filePath: string, path: JsonPath): Promise<JsonObject> {\n return updateJsonObjectFile(filePath, (config) => {\n removeJsonPath(config, path);\n });\n}\n\nexport function isJsonObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction lastPathSegment(path: JsonPath): JsonPathSegment {\n const segment = path[path.length - 1];\n /* v8 ignore next -- defensive: callers guard path.length === 0 before here */\n if (segment === undefined) throw new Error('Invalid empty JSON path');\n return segment;\n}\n\nfunction ensureJsonParent(root: JsonObject, path: JsonPath): JsonObject | unknown[] {\n let current: JsonObject | unknown[] = root;\n for (let i = 0; i < path.length - 1; i += 1) {\n const segment = path[i];\n const nextSegment = path[i + 1];\n /* v8 ignore next -- defensive: sparse-array paths are never produced by callers */\n if (segment === undefined) throw new Error('Invalid empty JSON path segment');\n const nextContainer = typeof nextSegment === 'number' ? [] : {};\n\n if (typeof segment === 'number') {\n if (!Array.isArray(current)) throw new Error(`Cannot traverse numeric segment ${segment} on non-array parent`);\n if (!isJsonObject(current[segment]) && !Array.isArray(current[segment])) current[segment] = nextContainer;\n current = current[segment] as JsonObject | unknown[];\n } else {\n if (!isJsonObject(current)) throw new Error(`Cannot traverse property ${segment} on non-object parent`);\n if (!isJsonObject(current[segment]) && !Array.isArray(current[segment])) current[segment] = nextContainer;\n current = current[segment] as JsonObject | unknown[];\n }\n }\n return current;\n}\n", "import * as path from 'node:path';\nimport type { Context } from '../core/context.js';\nimport type { TextBlock } from '../types/blocks.js';\nimport type { CompactReport } from '../types/compactor.js';\nimport type {\n CompletedWorkEvidence,\n CompletedWorkSource,\n ContextEvidenceState,\n ToolOutputMetadata,\n} from '../types/context-evidence.js';\nimport type { Message } from '../types/messages.js';\n\nconst MAX_TOOL_CALLS = 80;\nconst MAX_FACTS = 40;\nconst MAX_ERRORS = 20;\nconst MAX_DIGEST_CHARS = 4_000;\n/** Cap for the per-iteration reference scan \u2014 see markAssistantReferencedEvidence. */\nconst RECENT_TOOL_CALL_SCAN_LIMIT = 20;\n/** Cap content fed to file/symbol regex extractors (first N chars). */\nconst EXTRACT_CONTENT_CAP_CHARS = 10_000;\n/** Only scan the last N lines for error patterns \u2014 errors surface at the bottom. */\nconst EXTRACT_ERROR_TAIL_LINES = 200;\n\nconst WRITE_TOOLS = new Set(['edit', 'write', 'replace', 'patch']);\nconst READ_TOOLS = new Set(['read', 'grep', 'glob', 'ls', 'tree']);\n\nexport function createContextEvidenceState(): ContextEvidenceState {\n return {\n sessionGoals: [],\n implicitFacts: [],\n activeErrors: [],\n toolCalls: [],\n fileGraph: {},\n repeatedReads: [],\n completedWork: [],\n updatedAt: Date.now(),\n };\n}\n\nexport interface RecordToolOutputEvidenceInput {\n toolUseId: string;\n toolName: string;\n input: unknown;\n content: string;\n ok: boolean;\n outputBytes?: number | undefined;\n outputTokens?: number | undefined;\n outputLines?: number | undefined;\n}\n\nexport function recordUserIntentEvidence(ctx: Context, text: string): void {\n const intent = normalizeWhitespace(text).slice(0, 700);\n if (!intent) return;\n const state = ensureEvidence(ctx);\n state.currentIntent = { text: intent, updatedAt: Date.now() };\n if (state.sessionGoals.length === 0 || isGoalish(intent)) {\n pushUniqueBounded(state.sessionGoals, intent, 8);\n }\n state.updatedAt = Date.now();\n}\n\nexport function recordToolOutputEvidence(\n ctx: Context,\n input: RecordToolOutputEvidenceInput,\n): ToolOutputMetadata {\n const state = ensureEvidence(ctx);\n // Cap content for regex extraction. File paths and symbol declarations\n // appear near the top of tool output (import blocks, function definitions),\n // so the first 10KB captures them. Without this cap, matchAll() runs over\n // the full output \u2014 e.g. a 50KB file read triggers ~100KB of regex scanning\n // across two patterns in extractSymbols plus the extractFiles pass.\n const scanContent = input.content.length > EXTRACT_CONTENT_CAP_CHARS\n ? input.content.slice(0, EXTRACT_CONTENT_CAP_CHARS)\n : input.content;\n const files = extractFiles(ctx, input.toolName, input.input, scanContent);\n const symbols = extractSymbols(scanContent, input.input);\n const commands = extractCommands(input.toolName, input.input);\n const errors = extractErrors(input.content);\n const summary = summarizeToolOutput(input.toolName, input.input, input.content, {\n files,\n symbols,\n errors,\n ok: input.ok,\n });\n\n const metadata: ToolOutputMetadata = {\n toolUseId: input.toolUseId,\n toolName: input.toolName,\n ok: input.ok,\n inputSummary: summarizeInput(input.input),\n summary,\n files,\n symbols,\n commands,\n errors,\n status: 'seen',\n referenceCount: 0,\n seenAt: Date.now(),\n outputBytes: input.outputBytes,\n outputTokens: input.outputTokens,\n outputLines: input.outputLines,\n };\n\n state.toolCalls.push(metadata);\n if (state.toolCalls.length > MAX_TOOL_CALLS) {\n state.toolCalls.splice(0, state.toolCalls.length - MAX_TOOL_CALLS);\n }\n\n updateFileGraph(state, metadata);\n updateRepeatedReadSignals(state, metadata);\n if (errors.length > 0) {\n for (const err of errors) pushUniqueBounded(state.activeErrors, err, MAX_ERRORS);\n }\n const fact = implicitFactFor(metadata);\n if (fact) pushUniqueBounded(state.implicitFacts, fact, MAX_FACTS);\n state.updatedAt = Date.now();\n return metadata;\n}\n\nexport function markAssistantReferencedEvidence(ctx: Context, text: string): void {\n const state = ensureEvidence(ctx);\n const haystack = text.toLowerCase();\n if (!haystack.trim()) return;\n\n // Only scan the most recent tool calls. The assistant almost always\n // references the files/symbols it just worked on \u2014 older entries are\n // rarely re-referenced. Scanning the full list (up to 80 entries) means\n // worst case: 80 \u00D7 (files + symbols) includes() calls per iteration,\n // each O(responseText.length), which degrades as the conversation grows.\n // The last 20 captures the realistic reference window at \u00BC the cost.\n const recent = state.toolCalls.length > RECENT_TOOL_CALL_SCAN_LIMIT\n ? state.toolCalls.slice(-RECENT_TOOL_CALL_SCAN_LIMIT)\n : state.toolCalls;\n for (const tool of recent) {\n if (!metadataReferencedByText(tool, haystack)) continue;\n tool.status = 'referenced';\n tool.referenceCount++;\n tool.referencedAt = Date.now();\n for (const file of tool.files) {\n const node = state.fileGraph[file];\n if (node) node.referenced = true;\n }\n }\n state.updatedAt = Date.now();\n}\n\nexport function buildContextEvidenceDigest(ctx: Context): string {\n const state = ensureEvidence(ctx);\n const lines: string[] = [];\n\n if (state.currentIntent?.text) {\n lines.push(`intent: ${state.currentIntent.text}`);\n }\n\n const goals = state.sessionGoals.slice(-3);\n if (goals.length > 0) {\n lines.push('session_goals:');\n for (const goal of goals) lines.push(`- ${goal}`);\n }\n\n const activeErrors = state.activeErrors.slice(-5);\n if (activeErrors.length > 0) {\n lines.push('active_errors:');\n for (const err of activeErrors) lines.push(`- ${err}`);\n }\n\n const files = Object.values(state.fileGraph)\n .sort((a, b) => (b.writes - a.writes) || (b.reads - a.reads) || a.path.localeCompare(b.path))\n .slice(0, 12);\n if (files.length > 0) {\n lines.push('dependency_graph:');\n for (const file of files) {\n const actions = [\n file.reads > 0 ? `read ${file.reads}x` : '',\n file.writes > 0 ? `write ${file.writes}x` : '',\n ].filter(Boolean).join(', ');\n const refs = file.referenced ? '; referenced by assistant' : '';\n const via = file.lastToolUseId ? `; last via ${file.lastToolUseId}` : '';\n lines.push(`- ${file.path} (${actions || 'seen'}${refs}${via})`);\n }\n }\n\n const referenced = state.toolCalls\n .filter((tool) => tool.status === 'referenced')\n .slice(-10);\n const recentSeen = state.toolCalls\n .filter((tool) => tool.status === 'seen')\n .slice(-5);\n const trail = [...referenced, ...recentSeen];\n if (trail.length > 0) {\n lines.push('tool_trail:');\n for (const tool of trail) {\n const size = tool.outputTokens ? `; ~${tool.outputTokens} tokens` : '';\n const filesText = tool.files.length > 0 ? `; files=${tool.files.slice(0, 4).join(', ')}` : '';\n const symbolsText = tool.symbols.length > 0 ? `; symbols=${tool.symbols.slice(0, 4).join(', ')}` : '';\n lines.push(\n `- ${tool.toolUseId} ${tool.toolName} ${tool.status}: ${tool.summary}${filesText}${symbolsText}${size}`,\n );\n }\n }\n\n const facts = state.implicitFacts.slice(-8);\n if (facts.length > 0) {\n lines.push('implicit_facts:');\n for (const fact of facts) lines.push(`- ${fact}`);\n }\n\n const digest = lines.join('\\n');\n if (digest.length <= MAX_DIGEST_CHARS) return digest;\n return `${digest.slice(0, MAX_DIGEST_CHARS)}... [+${digest.length - MAX_DIGEST_CHARS} chars]`;\n}\n\nexport function repeatedReadPressure(ctx: Context): number {\n return ensureEvidence(ctx).repeatedReads.reduce((max, item) => Math.max(max, item.count), 0);\n}\n\n/** Marker prefixing the forced evidence-floor system message (also its dedupe key). */\nconst CONTEXT_STATE_MARKER = '[context_state]';\n\n/**\n * Stable issue keys emitted by `checkCompactionQuality` and consumed by\n * `injectEvidenceFloor`. Using typed consts instead of raw string literals\n * ensures the producer/consumer contract is typechecker-enforced \u2014 any new\n * issue key must be added here and both sides will be updated together.\n */\nconst QUALITY_ISSUE = {\n missingIntent: 'missing intent anchor',\n missingPathTrail: 'missing tool/path trail',\n} as const;\n\n/**\n * Deterministic post-compaction sanity check. Cheap and local: records whether\n * the compacted context still carries an intent anchor and a tool/path trail.\n * Shared across all compactors so they report quality the same way (previously\n * only HybridCompactor did). Advisory on its own \u2014 pair with\n * `injectEvidenceFloor` to actually repair a flagged loss.\n */\nexport function checkCompactionQuality(\n ctx: Context,\n opts: {\n collapsedDigest?: string | undefined;\n evidenceDigest?: string | undefined;\n reduced: boolean;\n },\n): CompactReport['quality'] {\n const evidence = ctx.contextEvidence;\n const digest = `${opts.collapsedDigest ?? ''}\\n${opts.evidenceDigest ?? ''}`;\n const hasIntent = Boolean(\n evidence?.currentIntent?.text ||\n /\\b(intent|goal|session_goals|hedef|amac|istiyorum|gerekiyor)\\b/i.test(digest),\n );\n const hasPathTrail = Boolean(\n Object.keys(evidence?.fileGraph ?? {}).length > 0 ||\n (evidence?.toolCalls.length ?? 0) > 0 ||\n /\\b(dependency_graph|tool_trail|files=)\\b/i.test(digest),\n );\n const issues: string[] = [];\n if (opts.reduced && !hasIntent) issues.push(QUALITY_ISSUE.missingIntent);\n if (opts.reduced && !hasPathTrail) issues.push(QUALITY_ISSUE.missingPathTrail);\n return { ok: issues.length === 0, hasIntent, hasPathTrail, issues };\n}\n\n/**\n * Enforce an evidence floor: when `checkCompactionQuality` flags that\n * compaction dropped the intent anchor or tool/path trail, prepend a compact\n * `[context_state]` system message rebuilt from the live evidence state so the\n * session goal is never silently lost. Idempotent (won't double-inject) and a\n * no-op when quality is fine or there is no evidence to inject. Returns true\n * when it injected a block (so the caller re-estimates tokens).\n */\nexport function injectEvidenceFloor(\n ctx: Context,\n quality: CompactReport['quality'] | undefined,\n): boolean {\n if (!quality || quality.ok) return false;\n const needsRepair =\n quality.issues.includes(QUALITY_ISSUE.missingIntent) ||\n quality.issues.includes(QUALITY_ISSUE.missingPathTrail);\n if (!needsRepair) return false;\n\n const digest = buildContextEvidenceDigest(ctx);\n if (!digest.trim()) return false;\n\n const already = ctx.messages.some(\n (m) => typeof m.content === 'string' && m.content.startsWith(CONTEXT_STATE_MARKER),\n );\n if (already) return false;\n\n const block: Message = { role: 'system', content: `${CONTEXT_STATE_MARKER}\\n${digest}` };\n ctx.state.replaceMessages([block, ...ctx.messages]);\n return true;\n}\n\nfunction ensureEvidence(ctx: Context): ContextEvidenceState {\n if (!ctx.contextEvidence) {\n (ctx as never as { contextEvidence: ContextEvidenceState }).contextEvidence =\n createContextEvidenceState();\n }\n // States restored from sessions persisted before the completed-work\n // ledger existed lack the array \u2014 heal in place so recorders can push.\n ctx.contextEvidence.completedWork ??= [];\n return ctx.contextEvidence;\n}\n\n// \u2500\u2500 Completed-work ledger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Bound on retained ledger entries \u2014 oldest are dropped first. */\nconst MAX_COMPLETED_WORK = 50;\n/** How many (newest) entries render into the system-prompt block. */\nconst LEDGER_BLOCK_ITEMS = 20;\n\n/** Marker prefixing the ledger's system-prompt block (also used to find/replace it). */\nexport const COMPLETED_WORK_LEDGER_MARKER = '[completed_work_ledger]';\n\nexport interface RecordCompletedWorkInput {\n /** Stable dedupe key, e.g. `task:<id>` \u2014 re-completion updates in place. */\n key: string;\n source: CompletedWorkSource;\n summary: string;\n /** Optional pointer to proof (test run, commit hash, file path). */\n evidence?: string | undefined;\n /** Epoch ms; defaults to now. */\n completedAt?: number | undefined;\n}\n\n/**\n * Append one finished unit of work to the session ledger. The provider request\n * composer renders this state as a volatile tail block; the stable system-prompt\n * prefix is never mutated here.\n */\nexport function recordCompletedWorkEvidence(\n ctx: Context,\n input: RecordCompletedWorkInput,\n): CompletedWorkEvidence {\n const state = ensureEvidence(ctx);\n const entry: CompletedWorkEvidence = {\n key: input.key,\n source: input.source,\n summary: normalizeWhitespace(input.summary).slice(0, 300),\n completedAt: input.completedAt ?? Date.now(),\n ...(input.evidence !== undefined && { evidence: input.evidence }),\n };\n const existing = state.completedWork.findIndex((item) => item.key === entry.key);\n if (existing >= 0) state.completedWork.splice(existing, 1);\n state.completedWork.push(entry);\n if (state.completedWork.length > MAX_COMPLETED_WORK) {\n state.completedWork.splice(0, state.completedWork.length - MAX_COMPLETED_WORK);\n }\n state.updatedAt = Date.now();\n return entry;\n}\n\n/** Render the ledger's system-prompt block text (marker + newest entries). */\nexport function formatCompletedWorkLedger(items: readonly CompletedWorkEvidence[]): string {\n const lines = items\n .slice(-LEDGER_BLOCK_ITEMS)\n .map(\n (item) =>\n `- [${item.source}] ${item.summary}${item.evidence ? ` (evidence: ${item.evidence})` : ''}`,\n );\n return (\n `${COMPLETED_WORK_LEDGER_MARKER}\\n` +\n 'Work already completed this session \u2014 do not redo it; build on it:\\n' +\n lines.join('\\n')\n );\n}\n\n/** Build the current volatile completed-work block without mutating the prompt. */\nexport function buildCompletedWorkLedgerBlock(ctx: Context): TextBlock | undefined {\n const items = ensureEvidence(ctx).completedWork;\n if (items.length === 0) return undefined;\n return {\n type: 'text',\n text: formatCompletedWorkLedger(items),\n cache_control: { type: 'ephemeral' },\n };\n}\n\n/**\n * @deprecated Volatile state must be composed at request time. Kept as a\n * compatibility no-op for embedders importing the old helper.\n */\nexport function syncCompletedWorkLedgerBlock(_ctx: Context): void {\n // Intentionally empty. Mutating ctx.systemPrompt invalidates provider prefix caches.\n}\n\nfunction isGoalish(text: string): boolean {\n return /\\b(goal|objective|task|need|want|implement|fix|improve|refactor|add|remove|hedef|amac|istiyorum|gerekiyor|iyilestir|duzelt|ekle|kaldir)\\b/i.test(text);\n}\n\nfunction normalizeWhitespace(text: string): string {\n return text.replace(/\\s+/g, ' ').trim();\n}\n\nfunction pushUniqueBounded(list: string[], value: string, max: number): void {\n const normalized = normalizeWhitespace(value);\n /* v8 ignore next -- unreachable: every caller passes already-normalized non-empty text */\n if (!normalized) return;\n const existing = list.findIndex((item) => item.toLowerCase() === normalized.toLowerCase());\n if (existing >= 0) list.splice(existing, 1);\n list.push(normalized);\n if (list.length > max) list.splice(0, list.length - max);\n}\n\nfunction extractFiles(\n ctx: Context,\n toolName: string,\n input: unknown,\n content: string,\n): string[] {\n const out = new Set<string>();\n for (const value of inputPathValues(input)) addPath(ctx, out, value);\n\n if (toolName === 'grep' || toolName === 'glob' || toolName === 'bash') {\n const re = /(?:(?:[A-Za-z]:)?[./\\\\]?[\\w@.-]+(?:[\\\\/][\\w@(). -]+)+\\.[A-Za-z0-9]{1,12})/g;\n for (const match of content.matchAll(re)) addPath(ctx, out, match[0]);\n }\n\n return [...out].slice(0, 30);\n}\n\nfunction inputPathValues(input: unknown): string[] {\n const values: string[] = [];\n const visit = (value: unknown, key?: string): void => {\n if (typeof value === 'string') {\n if (key && /^(path|file|files|fromFile|toFile|dir|cwd)$/i.test(key)) values.push(value);\n return;\n }\n if (Array.isArray(value)) {\n for (const item of value) visit(item, key);\n return;\n }\n if (!value || typeof value !== 'object') return;\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) visit(v, k);\n };\n visit(input);\n return values;\n}\n\nfunction addPath(ctx: Context, out: Set<string>, raw: string): void {\n const clean = raw.trim().replace(/^[\"'`]+|[\"'`),;:]+$/g, '');\n if (!clean || clean.length > 260) return;\n let normalized = clean.replace(/\\\\/g, '/');\n try {\n const abs = path.isAbsolute(clean) ? path.resolve(clean) : null;\n if (abs) {\n const rel = path.relative(ctx.projectRoot, abs);\n if (!rel.startsWith('..') && !path.isAbsolute(rel)) {\n normalized = rel.replace(/\\\\/g, '/');\n }\n }\n } catch {\n // Keep the best-effort normalized string.\n }\n if (normalized.length > 0) out.add(normalized);\n}\n\nfunction extractSymbols(content: string, input: unknown): string[] {\n const out = new Set<string>();\n const patterns = [\n /\\b(?:function|class|interface|type|enum|const|let|var|def|fn|struct)\\s+([A-Za-z_$][\\w$]*)/g,\n /\\b(?:export\\s+)?(?:async\\s+)?function\\s+([A-Za-z_$][\\w$]*)/g,\n ];\n for (const re of patterns) {\n for (const match of content.matchAll(re)) {\n if (match[1]) out.add(match[1]);\n if (out.size >= 30) break;\n }\n }\n\n const pattern = input && typeof input === 'object'\n ? (input as Record<string, unknown>)['pattern']\n : undefined;\n if (typeof pattern === 'string' && /^[A-Za-z_$][\\w$]*$/.test(pattern)) {\n out.add(pattern);\n }\n\n return [...out].slice(0, 30);\n}\n\nfunction extractCommands(toolName: string, input: unknown): string[] {\n if (toolName !== 'bash' && toolName !== 'exec' && toolName !== 'shell') return [];\n if (!input || typeof input !== 'object') return [];\n const command = (input as Record<string, unknown>)['command'];\n if (typeof command !== 'string') return [];\n return [command.slice(0, 220)];\n}\n\nfunction extractErrors(content: string): string[] {\n const allLines = content.split(/\\r?\\n/);\n // Only scan the last N lines \u2014 errors and stack traces surface at the\n // bottom of tool output. Scanning all lines means one regex test per line,\n // so a 2000-line file read costs 2000 regex evaluations for no gain since\n // the interesting errors are always at the tail.\n const lines = allLines.length > EXTRACT_ERROR_TAIL_LINES\n ? allLines.slice(-EXTRACT_ERROR_TAIL_LINES)\n : allLines;\n const errors: string[] = [];\n for (const line of lines) {\n if (!/\\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm|typeerror|syntaxerror)\\b/i.test(line)) continue;\n errors.push(normalizeWhitespace(line).slice(0, 260));\n if (errors.length >= 5) break;\n }\n return errors;\n}\n\nfunction summarizeInput(input: unknown): string | undefined {\n if (!input || typeof input !== 'object') return undefined;\n const obj = input as Record<string, unknown>;\n const parts: string[] = [];\n for (const key of ['path', 'file', 'pattern', 'glob', 'command']) {\n const value = obj[key];\n if (typeof value === 'string') parts.push(`${key}=${value.slice(0, 160)}`);\n }\n return parts.length > 0 ? parts.join(', ') : undefined;\n}\n\nfunction summarizeToolOutput(\n toolName: string,\n input: unknown,\n content: string,\n opts: { files: string[]; symbols: string[]; errors: string[]; ok: boolean },\n): string {\n if (!opts.ok && opts.errors.length > 0) return opts.errors[0] ?? `${toolName} failed`;\n if (toolName === 'read' && opts.files[0]) return `read ${opts.files[0]}`;\n if (toolName === 'grep') {\n const pattern = input && typeof input === 'object'\n ? (input as Record<string, unknown>)['pattern']\n : undefined;\n return `searched ${typeof pattern === 'string' ? pattern : 'pattern'} (${opts.files.length} file hint(s))`;\n }\n if ((toolName === 'edit' || toolName === 'write') && opts.files[0]) {\n return `${toolName === 'write' ? 'wrote' : 'edited'} ${opts.files[0]}`;\n }\n const firstLine = normalizeWhitespace(content.split(/\\r?\\n/).find((line) => line.trim()) ?? '');\n return firstLine ? firstLine.slice(0, 220) : `${toolName} returned no text`;\n}\n\nfunction updateFileGraph(state: ContextEvidenceState, metadata: ToolOutputMetadata): void {\n const writes = WRITE_TOOLS.has(metadata.toolName) ? 1 : 0;\n const reads = writes === 0 && (READ_TOOLS.has(metadata.toolName) || metadata.files.length > 0)\n ? 1\n : 0;\n for (const file of metadata.files) {\n const existing = state.fileGraph[file] ?? {\n path: file,\n reads: 0,\n writes: 0,\n tools: [],\n referenced: false,\n };\n existing.reads += reads;\n existing.writes += writes;\n existing.lastToolUseId = metadata.toolUseId;\n pushUniqueBounded(existing.tools, `${metadata.toolName}#${metadata.toolUseId}`, 8);\n state.fileGraph[file] = existing;\n }\n}\n\nfunction updateRepeatedReadSignals(state: ContextEvidenceState, metadata: ToolOutputMetadata): void {\n if (metadata.toolName !== 'read' || metadata.files.length === 0) {\n state.lastReadPath = undefined;\n return;\n }\n const file = metadata.files[0] as string;\n if (state.lastReadPath === file) {\n const existing = state.repeatedReads.find((item) => item.file === file);\n if (existing) {\n existing.count++;\n existing.lastToolUseId = metadata.toolUseId;\n } else {\n state.repeatedReads.push({ file, count: 2, lastToolUseId: metadata.toolUseId });\n }\n if (state.repeatedReads.length > 10) state.repeatedReads.shift();\n }\n state.lastReadPath = file;\n}\n\nfunction implicitFactFor(metadata: ToolOutputMetadata): string | undefined {\n if (metadata.errors.length > 0) return `${metadata.toolName}#${metadata.toolUseId} exposed error: ${metadata.errors[0]}`;\n if (metadata.toolName === 'read' && metadata.files[0]) {\n const size = metadata.outputLines ? ` (${metadata.outputLines} line(s) returned)` : '';\n return `read ${metadata.files[0]}${size}`;\n }\n if ((metadata.toolName === 'edit' || metadata.toolName === 'write') && metadata.files[0]) {\n return `${metadata.toolName} changed ${metadata.files[0]}`;\n }\n if (metadata.status === 'referenced') return `${metadata.toolName}#${metadata.toolUseId} was referenced`;\n return undefined;\n}\n\nfunction metadataReferencedByText(metadata: ToolOutputMetadata, haystack: string): boolean {\n for (const file of metadata.files) {\n const f = file.toLowerCase();\n const base = path.basename(file).toLowerCase();\n if (f && haystack.includes(f)) return true;\n if (base && haystack.includes(base)) return true;\n }\n for (const symbol of metadata.symbols) {\n if (symbol.length >= 3 && haystack.includes(symbol.toLowerCase())) return true;\n }\n for (const err of metadata.errors) {\n const head = err.slice(0, 80).toLowerCase();\n if (head.length >= 12 && haystack.includes(head)) return true;\n }\n return false;\n}\n", "/**\n * Converts an unknown error value to a human-readable string.\n * Used in 40+ files across the codebase to normalize error messaging.\n */\nexport function toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n", "/** Assert a value is neither null nor undefined. Throws if it is.\n * Useful after optional chaining and indexed access when the\n * control flow guarantees the value exists but TypeScript can't\n * prove it (e.g. after a check on a related field). */\nexport function expectDefined<T>(value: T | null | undefined, label?: string): T {\n if (value === null || value === undefined) {\n const err = new Error(label ? `Expected ${label} to be defined` : 'Expected value to be defined');\n err.name = 'ExpectDefinedError';\n throw err;\n }\n return value;\n}\n", "import type { ContentBlock, ToolResultBlock, ToolUseBlock } from '../types/blocks.js';\nimport type { Message } from '../types/messages.js';\nimport { expectDefined } from './expect-defined.js';\nexport interface MessageRepairReport {\n changed: boolean;\n removedToolUses: string[];\n removedToolResults: string[];\n removedMessages: number;\n}\n\nexport interface MessageRepairResult {\n messages: Message[];\n report: MessageRepairReport;\n}\n\n/**\n * Repair provider-level tool-call adjacency invariants.\n *\n * Anthropic requires every assistant `tool_use` block to have a matching\n * `tool_result` block in the immediately following user message. Manual\n * context surgery (summary/prune) can cut through the middle of such an\n * exchange. This function removes only the now-orphaned protocol blocks,\n * preserving surrounding text/images/thinking blocks where possible.\n */\nexport function repairToolUseAdjacency(messages: Message[]): MessageRepairResult {\n const removedToolUses: string[] = [];\n const removedToolResults: string[] = [];\n let removedMessages = 0;\n let changed = false;\n const out: Message[] = [];\n\n for (let i = 0; i < messages.length; i++) {\n const original = expectDefined(messages[i]);\n let msg = original;\n\n if (hasToolUse(msg)) {\n const nextIds = toolResultIds(messages[i + 1]);\n const filtered = mapContent(msg, (blocks) => {\n const next: ContentBlock[] = [];\n for (const block of blocks) {\n if (block.type === 'tool_use' && !nextIds.has(block.id)) {\n removedToolUses.push(block.id);\n changed = true;\n continue;\n }\n next.push(block);\n }\n return next;\n });\n msg = filtered ?? msg;\n }\n\n if (hasToolResult(msg)) {\n const allowed = toolUseIds(out[out.length - 1]);\n const filtered = mapContent(msg, (blocks) => {\n const next: ContentBlock[] = [];\n for (const block of blocks) {\n if (block.type === 'tool_result' && !allowed.has(block.tool_use_id)) {\n removedToolResults.push(block.tool_use_id);\n changed = true;\n continue;\n }\n next.push(block);\n }\n return next;\n });\n msg = filtered ?? msg;\n }\n\n if (isEmptyMessage(msg)) {\n removedMessages++;\n changed = true;\n continue;\n }\n out.push(msg);\n }\n\n return {\n messages: changed ? out : messages,\n report: { changed, removedToolUses, removedToolResults, removedMessages },\n };\n}\n\nfunction hasToolUse(msg: Message | undefined): boolean {\n return contentBlocks(msg).some((b): b is ToolUseBlock => b.type === 'tool_use');\n}\n\nfunction hasToolResult(msg: Message | undefined): boolean {\n return contentBlocks(msg).some((b): b is ToolResultBlock => b.type === 'tool_result');\n}\n\nfunction toolUseIds(msg: Message | undefined): Set<string> {\n const ids = new Set<string>();\n if (msg?.role !== 'assistant') return ids;\n for (const block of contentBlocks(msg)) {\n if (block.type === 'tool_use') ids.add(block.id);\n }\n return ids;\n}\n\nfunction toolResultIds(msg: Message | undefined): Set<string> {\n const ids = new Set<string>();\n if (msg?.role !== 'user') return ids;\n for (const block of contentBlocks(msg)) {\n if (block.type === 'tool_result') ids.add(block.tool_use_id);\n }\n return ids;\n}\n\nfunction contentBlocks(msg: Message | undefined): ContentBlock[] {\n return msg && Array.isArray(msg.content) ? msg.content : [];\n}\n\nfunction mapContent(msg: Message, fn: (blocks: ContentBlock[]) => ContentBlock[]): Message | null {\n if (!Array.isArray(msg.content)) return msg;\n const next = fn(msg.content);\n if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {\n return msg;\n }\n return { ...msg, content: next };\n}\n\n/**\n * True when a message content payload carries meaningful information for\n * the provider: non-whitespace text, a tool call/result, thinking with\n * text or a signature, or any other block type.\n *\n * False for empty strings/arrays and for arrays whose only blocks are\n * empty or whitespace-only text \u2014 the shape persisted when a stream is\n * interrupted before the first meaningful delta (issue #271). Strict\n * providers reject such assistant turns, so repair and replay paths must\n * treat them as empty.\n */\nexport function hasMeaningfulContent(content: Message['content']): boolean {\n if (typeof content === 'string') return content.trim().length > 0;\n for (const block of content) {\n if (block.type === 'text') {\n if (block.text.trim().length > 0) return true;\n continue;\n }\n if (block.type === 'thinking') {\n // Signature-only thinking blocks are valid and required for replay;\n // blocks with neither text nor signature are provider-rejected noise.\n if (block.thinking.trim().length > 0 || block.signature) return true;\n continue;\n }\n // tool_use, tool_result, image, redacted_thinking, \u2026 \u2014 always meaningful.\n return true;\n }\n return false;\n}\n\nfunction isEmptyMessage(msg: Message): boolean {\n return !hasMeaningfulContent(msg.content);\n}\n", "/**\n * Response processing handler \u2014 extracted from Agent class.\n * Handles provider response pipeline, event emission, session\n * persistence, text rendering, and autonomous continuation parsing.\n */\n\nimport { isTextBlock, type TextBlock } from '../types/blocks.js';\nimport type { Request, Response } from '../types/provider.js';\nimport { deriveCachePrefixKey } from '../utils/cache-key.js';\nimport {\n buildCompletedWorkLedgerBlock,\n markAssistantReferencedEvidence,\n} from '../utils/context-evidence.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { hasMeaningfulContent, repairToolUseAdjacency } from '../utils/message-invariants.js';\nimport type { AgentInternals } from './agent-internals.js';\nimport type { Context, RunOptions } from './context.js';\nimport { type ContinueDirective, parseContinueDirective } from './continue-to-next-iteration.js';\n\ninterface ProcessResponseResult {\n finalText: string;\n aborted: boolean;\n done: boolean;\n directive?: ContinueDirective | undefined;\n}\n\nexport interface AgentResponseHandler {\n buildAndRunRequestPipeline(opts: RunOptions): Promise<Request>;\n processResponse(raw: Response, req: Request): Promise<ProcessResponseResult>;\n}\n\nconst MAX_TODO_SNAPSHOT_ITEMS = 10;\nconst MAX_TODO_SNAPSHOT_CONTENT = 180;\n\n/**\n * Build the leader-only, per-request decision gate for `<nextsteps>`.\n *\n * The base system prompt is intentionally frozen for provider caching, while\n * todos change during tool execution. Keeping this block volatile makes the\n * final-response contract follow the live todo state on every iteration.\n */\nexport function buildLiveNextStepsGateBlock(\n ctx: Pick<Context, 'agentId' | 'todos'>,\n): TextBlock | undefined {\n if (ctx.agentId !== 'leader') return undefined;\n\n const openTodos = ctx.todos.filter(\n (todo) => todo.status === 'pending' || todo.status === 'in_progress',\n );\n\n if (openTodos.length === 0) {\n return {\n type: 'text',\n text: [\n '[nextsteps_gate]',\n 'Authoritative live state for this request: open todos = 0.',\n 'On the final response, you MUST take exactly one branch:',\n '1. If at least one genuinely useful follow-on action exists, include a balanced <nextsteps> block containing 1-4 exact prompt messages that can be submitted back to you through the current TUI or WebUI input.',\n 'Every item must ask the agent to perform work. Never put a human-only chore or an instruction addressed to the user inside <nextsteps>; natural-language agent-directed imperatives are valid and need not be shell commands.',\n '2. If no useful follow-on action truly exists, omit <nextsteps> and explicitly tell the user in normal prose that no further steps are needed for this task.',\n 'Silently omitting both is invalid. Do not decide by chance, tone, or response length, and do not invent filler suggestions.',\n '[/nextsteps_gate]',\n ].join('\\n'),\n cache_control: { type: 'ephemeral' },\n };\n }\n\n const todoSnapshot = openTodos.slice(0, MAX_TODO_SNAPSHOT_ITEMS).map((todo) => {\n const normalized = todo.content.replace(/\\s+/g, ' ').trim();\n const content =\n normalized.length > MAX_TODO_SNAPSHOT_CONTENT\n ? `${normalized.slice(0, MAX_TODO_SNAPSHOT_CONTENT - 1)}\u2026`\n : normalized;\n return `- [${todo.status}] ${content}`;\n });\n const omitted = openTodos.length - todoSnapshot.length;\n if (omitted > 0) todoSnapshot.push(`- \u2026and ${omitted} more open todo(s)`);\n\n return {\n type: 'text',\n text: [\n '[nextsteps_gate]',\n `Authoritative live state for this request: open todos = ${openTodos.length}.`,\n 'You MUST omit <nextsteps> entirely while these todos remain open. Continue or finish the tracked work; do not propose unrelated follow-on work.',\n 'Open todo snapshot:',\n ...todoSnapshot,\n '[/nextsteps_gate]',\n ].join('\\n'),\n cache_control: { type: 'ephemeral' },\n };\n}\n\nexport function createAgentResponseHandler(a: AgentInternals): AgentResponseHandler {\n // Each assigned prompt array is one explicit cache epoch. Freeze it at the\n // first request boundary so turn-time code cannot silently invalidate the\n // provider prefix by pushing/replacing blocks in place. Lifecycle actions\n // such as a mode switch may assign a new array, which becomes a new epoch.\n const stabilizedPromptEpochs = new WeakSet<TextBlock[]>();\n\n function stabilizePromptEpoch(): void {\n const prompt = a.ctx.systemPrompt;\n if (stabilizedPromptEpochs.has(prompt)) return;\n for (const block of prompt) {\n if (block.cache_control) Object.freeze(block.cache_control);\n Object.freeze(block);\n }\n Object.freeze(prompt);\n stabilizedPromptEpochs.add(prompt);\n }\n\n async function buildAndRunRequestPipeline(opts: RunOptions): Promise<Request> {\n // Only scan for tool-use adjacency issues when tool content has been\n // added since the last scan. Pure text responses and iterations without\n // tool calls don't introduce new adjacency problems \u2014 skipping the O(n)\n // message-array walk saves ~1-3ms per iteration on large contexts.\n if (a.ctx.toolAdjacencyDirty) {\n const repaired = repairToolUseAdjacency(a.ctx.messages);\n a.ctx.toolAdjacencyDirty = false;\n if (repaired.report.changed) {\n a.ctx.state.replaceMessages(repaired.messages);\n a.events.emit('context.repaired', {\n sessionId: a.ctx.session.id,\n ctx: a.ctx,\n ...repaired.report,\n });\n a.logger.warn(\n `Repaired context tool adjacency: removed ${repaired.report.removedToolUses.length} tool_use block(s), ` +\n `${repaired.report.removedToolResults.length} tool_result block(s), ` +\n `${repaired.report.removedMessages} empty message(s)`,\n );\n }\n }\n stabilizePromptEpoch();\n const volatileLedger = buildCompletedWorkLedgerBlock(a.ctx);\n const liveNextStepsGate = buildLiveNextStepsGateBlock(a.ctx);\n const volatileBlocks = [volatileLedger, liveNextStepsGate].filter(\n (block): block is TextBlock => block !== undefined,\n );\n const system =\n volatileBlocks.length > 0 ? [...a.ctx.systemPrompt, ...volatileBlocks] : a.ctx.systemPrompt;\n const baseReq: Request = {\n model: opts.model ?? a.ctx.model,\n system,\n messages: a.ctx.messages,\n tools: a.tools.list(),\n // Default to the provider's model-native output ceiling so subagents\n // (Chimera, etc.) can run long reports up to the model's actual\n // limit. The provider adapter's `buildBody` substitutes its own\n // fallback (`ctx.capabilities.maxOutput ?? 8192`) when this is\n // absent \u2014 keeping the field optional at the wire layer is what\n // lets the catalog-driven ceiling reach the API untouched.\n maxTokens: a.ctx.provider.capabilities.maxOutput,\n // Provider-agnostic cache-partition key from the stable prompt epoch.\n // Wires that support prompt caching (OpenAI `prompt_cache_key`) read it;\n // the config `ttl` is merged over this by the ModelRuntime middleware.\n cache: { key: deriveCachePrefixKey(a.ctx.systemPrompt) },\n };\n return a.pipelines.request.run(baseReq);\n }\n\n async function processResponse(raw: Response, req: Request): Promise<ProcessResponseResult> {\n let res = raw;\n res = await a.pipelines.response.run(res);\n a.events.emit('provider.response', {\n sessionId: a.ctx.session.id,\n ctx: a.ctx,\n model: req.model,\n content: res.content,\n usage: res.usage,\n stopReason: res.stopReason,\n });\n a.ctx.tokenCounter.account(res.usage, req.model, a.ctx.provider.id);\n\n // Issue #271: never append or persist a semantically empty assistant\n // response (e.g. a stream interrupted before the first meaningful delta,\n // which the response builders represent as a single empty text block).\n // Strict providers reject empty assistant turns on the next request, and\n // once journaled, the malformed turn survived every repair path. Partial\n // text, tool calls, and thinking content remain meaningful and are kept.\n if (hasMeaningfulContent(res.content)) {\n a.ctx.state.appendMessage({ role: 'assistant', content: res.content });\n // If the assistant emitted tool_use blocks, mark the message adjacency\n // as potentially needing repair before the next provider request.\n if (!a.ctx.toolAdjacencyDirty) {\n for (const block of res.content) {\n if (block.type === 'tool_use') {\n a.ctx.toolAdjacencyDirty = true;\n break;\n }\n }\n }\n await a.ctx.session.append({\n type: 'llm_response',\n ts: new Date().toISOString(),\n content: res.content,\n stopReason: res.stopReason,\n usage: res.usage,\n });\n // Tool execution is a side-effect boundary: ensure the response containing\n // its tool_use blocks has reached the session writer before any tool runs.\n // FileSessionWriter keeps failed batches queued for retry; alternate\n // writers may reject, which is logged without masking the provider result.\n try {\n await a.ctx.flushConversationJournal();\n await a.ctx.session.flush();\n } catch (err) {\n (a.logger.debug ?? a.logger.warn)?.(`LLM response flush failed: ${toErrorMessage(err)}`);\n }\n } else {\n a.logger.warn('Empty assistant response \u2014 not appended to context or session', {\n model: req.model,\n stopReason: res.stopReason,\n aborted: a.ctx.signal.aborted,\n });\n }\n\n if (a.ctx.signal.aborted) {\n // M3: collect into an array and join at the end. `finalText += block.text`\n // is O(n\u00B2) on V8 for many concatenations because each `+=` may allocate\n // a new backing string. For a typical 4-block response this is moot,\n // but the streaming-text path concatenates the *full* response in chunks\n // \u2014 and long autonomous loops with verbose reasoning can hit dozens of\n // chunks, making the cost visible. `Array.push` + single `join('')` is\n // amortized O(n).\n const parts: string[] = [];\n for (const block of res.content) {\n if (isTextBlock(block)) parts.push(block.text);\n }\n return { finalText: parts.join(''), aborted: true, done: false };\n }\n\n const parts: string[] = [];\n const streamed = a.ctx.provider.capabilities.streaming;\n for (const block of res.content) {\n if (isTextBlock(block)) {\n const rendered = await a.pipelines.assistantOutput.run(block);\n parts.push(rendered.text);\n if (!streamed) a.renderer?.write(rendered);\n }\n }\n const finalText = parts.join('');\n markAssistantReferencedEvidence(a.ctx, finalText);\n\n let directive: ContinueDirective = 'none';\n if (finalText) {\n directive = parseContinueDirective(finalText);\n }\n\n return { finalText, aborted: false, done: false, directive };\n }\n\n return { buildAndRunRequestPipeline, processResponse };\n}\n", "import { spawn } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { MailboxAgentStatus } from '../coordination/mailbox-types.js';\nimport { SKILL_LIMITS } from '../skills/limits.js';\nimport type { TextBlock } from '../types/blocks.js';\nimport type { ConcreteTokenSavingTier, TokenSavingTier } from '../types/config.js';\nimport { resolveTokenSavingTier } from '../types/config.js';\nimport type { MemoryStore } from '../types/memory.js';\nimport type { ModeStore } from '../types/mode.js';\nimport type { SkillLoader } from '../types/skill.js';\nimport type {\n BuildContext,\n ModelCapabilities,\n SystemPromptBuilder,\n SystemPromptRegions,\n} from '../types/system-prompt.js';\nimport { flattenSystemPromptRegions } from '../types/system-prompt.js';\nimport type { SystemPromptContributor } from '../types/system-prompt-contributor.js';\nimport type { Tool } from '../types/tool.js';\nimport { buildChildEnv } from '../utils/child-env.js';\nimport {\n type InstructionBundle,\n type InstructionBundlePaths,\n loadInstructionBundle,\n mergeInstructionBundle,\n} from './instruction-bundle.js';\nimport { PROMPT as DEFAULT_PROMPT, LEADER_AFTER_TASK_PROMPT } from './modes/default.js';\n\nexport const LAYER_1_IDENTITY = DEFAULT_PROMPT;\n\n/**\n * The section of the system prompt a given TextBlock originated from. Used by\n * `getContextBreakdown()` to attribute real token counts per category in the\n * `/context` display.\n */\nexport type SystemBlockSource =\n | 'identity' // layer1 \u2014 instructions/system.md\n | 'tool-usage' // layer2 \u2014 tool prose summary\n | 'environment' // layer3 \u2014 OS/git/date/skills-in-scope\n | 'skills' // layer4 \u2014 Active Skills bodies (+ memory when injectMemory)\n | 'mode' // layer5 mode prompt + mode-skill hint\n | 'plan' // layer6 \u2014 active plan\n | 'leader-after-task' // leader-only after-task affordances\n | 'contributor' // plugin-contributed volatile blocks\n | 'ledger' // volatile completed-work ledger (request-time)\n | 'nextsteps'; // volatile next-steps gate (request-time)\n\n/**\n * Side-table mapping each system-prompt TextBlock to the section it came from.\n * Kept as a WeakMap rather than a field on TextBlock so the label never reaches\n * the wire: the provider adapters spread blocks verbatim (Anthropic non-ttl\n * `: b`, OpenAI `stripCacheControl` rest-spread), so any extra field would leak.\n * Block identities survive `flattenSystemPromptRegions` into `ctx.systemPrompt`,\n * so a later read of `ctx.systemPrompt` resolves the same entries.\n */\nexport const SYSTEM_BLOCK_SOURCE = new WeakMap<TextBlock, SystemBlockSource>();\n\n/** Tag a freshly-built block with its origin, returning the same reference. */\nfunction tagBlock(block: TextBlock, source: SystemBlockSource): TextBlock {\n SYSTEM_BLOCK_SOURCE.set(block, source);\n return block;\n}\n\nfunction shortSessionId(sessionId: string): string {\n const leaf = sessionId.split('/').pop() ?? sessionId;\n return leaf.length > 12 ? `${leaf.slice(0, 12)}\u2026` : leaf;\n}\n\n/** Canonical shell the `bash` tool targets \u2014 drives the Environment Shell line\n * and the syntax-guidance sub-block. */\ntype EffectiveShell = 'pwsh' | 'powershell' | 'cmd' | 'posix';\n\n/**\n * Derive the shell the `bash` tool will use from `os.platform()` + the pinned\n * `WRONGSTACK_SHELL` value (set at boot by `ensureSessionShell` in\n * @wrongstack/tools). On POSIX this is always `'posix'` and the caller shows the\n * raw `$SHELL`. On Windows with no pinned value (boot didn't run \u2014 tests /\n * embeddings) we report `'cmd'`, matching `bash.ts`'s default for\n * non-PowerShell-looking commands.\n */\nexport function effectiveShell(\n platform: NodeJS.Platform,\n wrongstackShell: string | undefined,\n): EffectiveShell {\n if (platform !== 'win32') return 'posix';\n const v = wrongstackShell?.trim().toLowerCase();\n if (v === 'powershell' || v === 'powershell.exe') return 'powershell';\n if (v === 'pwsh' || v === 'pwsh.exe') return 'pwsh';\n if (v === 'cmd' || v === 'cmd.exe') return 'cmd';\n return 'cmd';\n}\n\nconst SHELL_DISPLAY: Record<Exclude<EffectiveShell, 'posix'>, string> = {\n pwsh: 'pwsh (PowerShell 7+) \u2014 write PowerShell syntax, not bash',\n powershell: 'powershell (Windows PowerShell 5.1) \u2014 write PowerShell syntax, not bash',\n cmd: 'cmd.exe (Command Prompt) \u2014 write cmd syntax, not bash',\n};\n\n/**\n * Shell-specific syntax guidance for the Environment block. Returns `''` for\n * POSIX (the model writes bash natively, so no nudge is needed). `detail:\n * 'short'` is the light-tier one-liner; `'full'` is the complete cheat-sheet.\n * The `&&`/`||` note branches on the PowerShell edition (only pwsh 7 supports\n * them).\n */\nexport function shellGuidanceBlock(shell: EffectiveShell, detail: 'full' | 'short'): string {\n if (shell === 'posix') return '';\n if (shell === 'cmd') {\n if (detail === 'short') {\n return '- Shell syntax: cmd.exe \u2014 use `%VAR%`, `2>nul`, `dir`/`type`/`del`/`where` (NOT bash `$VAR`, `/dev/null`, `ls`/`cat`/`rm`).';\n }\n return [\n '## Shell \u2014 cmd.exe',\n 'The `bash` tool runs **cmd.exe** on this machine. Write cmd syntax, not bash/POSIX:',\n '- Env vars: `%NAME%` (NOT `$NAME`); set with `set NAME=value`.',\n '- Discard output: `2>nul` / `>nul` (NOT `2>/dev/null`).',\n '- No `ls`/`cat`/`rm`/`which`/`head` \u2014 use `dir`/`type`/`del`/`where` and `more`.',\n '- Chain with `&&` / `||` / `&`. Prefer the dedicated read/grep/glob tools over shell file ops.',\n ].join('\\n');\n }\n // pwsh or powershell\n if (detail === 'short') {\n return '- Shell syntax: PowerShell \u2014 use `$env:VAR`, `2>$null`, `Get-Content`/`Select-Object` (NOT bash `$VAR`, `/dev/null`, `cat`/`head`).';\n }\n const chain =\n shell === 'pwsh'\n ? '- Chain with `&&` / `||` (supported in PowerShell 7).'\n : '- `&&` / `||` are NOT available in Windows PowerShell 5.1 \u2014 separate commands with `;` (and check `$LASTEXITCODE`).';\n return [\n `## Shell \u2014 PowerShell${shell === 'pwsh' ? ' 7+ (pwsh)' : ' 5.1 (powershell)'}`,\n 'The `bash` tool runs **PowerShell** on this machine. Write PowerShell syntax, not bash/POSIX:',\n \"- Env vars: read `$env:NAME`, set `$env:NAME = 'value'` (NOT `$NAME`, `%NAME%`, or `export`).\",\n '- Discard output: `... 2>$null` or `$null = ...` (NOT `2>/dev/null`).',\n '- No bash builtins \u2014 use cmdlets: `head -n N`\u2192`Select-Object -First N`, `tail`\u2192`-Last N`, `cat`\u2192`Get-Content`, `which x`\u2192`Get-Command x`, `rm -rf p`\u2192`Remove-Item -Recurse -Force p`, `touch f`\u2192`New-Item -ItemType File f`. Prefer the grep/glob tools over `Select-String`.',\n '- Read a line window of a file: `Get-Content path | Select-Object -Skip N -First M` (the `sed -n` / `head|tail` equivalent).',\n '- Pipes work normally; `rg`/`git`/`node` and other native exes run as-is \u2014 only the *shell builtins* differ. (`rg --files src | rg pattern` is fine.)',\n '- Call exes whose path has spaces via the call operator: `& \"C:\\\\Program Files\\\\app.exe\" args`.',\n \"- Multi-line literals: single-quoted here-string `@'\u2026'@` with the closing `'@` at column 0.\",\n '- Non-interactive only: no `Read-Host`/`Get-Credential`/`pause`; add `-Confirm:$false` to destructive cmdlets.',\n chain,\n ].join('\\n');\n}\n\nexport interface DefaultSystemPromptBuilderOptions {\n memoryStore?: MemoryStore | undefined;\n /**\n * Inject a static \"# Relevant Memory\" section into the built prompt from\n * `memoryStore`. Default: true. Set to false when a dedicated per-turn memory\n * retriever (the Super Memory turn middleware) is the single injection\n * channel \u2014 this avoids double-injecting the same memories and keeps all\n * memory context flowing through one system.\n */\n injectMemory?: boolean | undefined;\n skillLoader?: SkillLoader | undefined;\n /**\n * How skill bodies reach the prompt. `'eager'` (default) injects every\n * discovered skill body; `'progressive'` injects only a name+trigger manifest\n * and relies on the agent calling the `skill` tool to load a body on demand\n * (the agentskills.io progressive-disclosure model).\n */\n skillMode?: 'eager' | 'progressive' | undefined;\n /**\n * In eager mode, cap the total chars of injected skill bodies (highest-priority\n * skills first); the rest become a load-on-demand manifest. Bounds prompt\n * cost when many skills are discovered. Default ~24k chars.\n */\n skillEagerMaxChars?: number | undefined;\n modeStore?: ModeStore | undefined;\n /** Pre-resolved active mode id \u2014 shown in environment block. */\n modeId?: string | undefined;\n /** Pre-resolved mode prompt \u2014 avoids redundant modeStore.getActiveMode() call. */\n modePrompt?: string | undefined;\n /** Model capabilities \u2014 object snapshot or lazy getter for live model switches. */\n modelCapabilities?: ModelCapabilities | (() => ModelCapabilities | undefined) | undefined;\n todayIso?: string | undefined;\n /**\n * Path to the session's plan JSON, or a getter that returns it. When\n * set, the builder reads the file on every `build()` call and injects\n * an \"Active plan\" block listing open items, so the LLM is anchored to\n * the strategic roadmap every turn \u2014 not just at resume. The block is\n * tagged `ephemeral` so a plan edit on turn N doesn't invalidate the\n * provider's prefix cache for earlier turns.\n *\n * The function form lets callers bind the builder before the session\n * id is known (e.g. DI containers that resolve the builder lazily) \u2014\n * the getter is called at build-time, after the session has been\n * created.\n */\n planPath?: string | (() => string | undefined);\n /**\n * System prompt contributors \u2014 called on every `build()` to inject\n * additional TextBlocks. Use `ExtensionRegistry.listSystemPromptContributors()`\n * or pass a plain array. Contributors are called in order; a throwing\n * contributor is caught and logged without aborting the build.\n */\n contributors?: readonly SystemPromptContributor[] | undefined;\n /**\n * Token-saving mode tier. Controls how aggressively the system prompt is\n * compacted: skill bodies are omitted/trimmed, tool hints are shortened,\n * and optional guidance sections (delegation, mailbox, context management)\n * use minimal versions to reduce per-request tokens.\n *\n * - 'off' \u2014 Full guidance (no reduction)\n * - 'minimal' \u2014 TIER1 tools, stripped guidance\n * - 'light' \u2014 TIER1 + memory tools, minimal patterns\n * - 'medium' \u2014 TIER1 + TIER2 tools, some guidance\n * - 'aggressive' \u2014 Maximum reduction before tools become unusable\n *\n * Boolean values are accepted for backward compatibility:\n * - `true` \u2192 'medium'\n * - `false` \u2192 'off'\n */\n tokenSavingMode?: TokenSavingTier | boolean | undefined;\n /**\n * File-backed instruction layers. Builtins are loaded first, then global\n * overrides, then project overrides, then explicit files. This lets durable\n * system instructions live outside TypeScript while keeping the builder API\n * stable for embedded runtimes.\n */\n instructionPaths?: InstructionBundlePaths | undefined;\n /**\n * Last-mile in-memory overrides, applied after instructionPaths. Useful for\n * tests, embedders, and plugin-provided prompt experiments.\n */\n instructionBundle?: InstructionBundle | undefined;\n}\n\nexport class DefaultSystemPromptBuilder implements SystemPromptBuilder {\n /**\n * Cached environment block, keyed by projectRoot. A single builder\n * instance is normally reused across turns of the same agent run, but\n * tests and library consumers may reuse it across runs with different\n * roots; keying the cache prevents leaking the first call's project\n * state into a later call against an unrelated project.\n */\n private envCacheByRoot = new Map<string, string>();\n private skillCache?: string | undefined;\n /** Cached full skill bodies (after frontmatter), built once per session. */\n private skillBodyCache?: string | undefined;\n /** Tools from last build \u2014 used for memory relevance scoring. */\n private _lastBuildTools?: Tool[] | undefined;\n /** Cached rendered online agents string, keyed by content fingerprint. */\n private _lastOnlineAgents?: { hash: string; text: string } | undefined;\n /** Cached full buildToolUsage output \u2014 keyed by tools array ref + agents fingerprint + tier. */\n private _toolsUsageCache?:\n | { toolsRef: readonly Tool[]; agentsHash: string; tier: string; text: string }\n | undefined;\n private _instructionBundle?: Promise<InstructionBundle> | undefined;\n constructor(private readonly opts: DefaultSystemPromptBuilderOptions = {}) {}\n\n /**\n * Normalizes `tokenSavingMode` to a boolean for backward-compatible boolean checks.\n * - `undefined` / `false` / `'off'` \u2192 false\n * - `true` / any tier string other than `'off'` \u2192 true\n *\n * Note: invalid tier strings (e.g. \"MINIMAL\") are coerced to 'off'\n * by `normalizeTokenSavingTier` via the `tier` getter, so isCompact\n * correctly returns false for them \u2014 preventing isCompact/tier\n * disagreement on bad input.\n */\n private get isCompact(): boolean {\n return this.tier !== 'off';\n }\n\n /** Exposes the effective (concrete) `TokenSavingTier` for tier-aware guidance\n * decisions. Invalid strings coerce to 'off'; the `'auto'` sentinel expands\n * from the model's context window (cache-safe \u2014 the window is stable per\n * session, so the resolved tier and therefore the prompt prefix stay stable).\n * See packages/core/src/types/config.ts. */\n private get tier(): ConcreteTokenSavingTier {\n return resolveTokenSavingTier(\n this.opts.tokenSavingMode,\n this.modelCapabilities()?.maxContextTokens,\n );\n }\n\n /**\n * Returns the max tool description length for the current tier.\n * Per the design doc: off=80, minimal=40, light=50, medium=60, aggressive=70.\n */\n private toolDescLimit(): number {\n switch (this.tier) {\n case 'minimal':\n return 30;\n case 'light':\n return 40;\n case 'medium':\n return 50;\n case 'aggressive':\n return 60;\n default:\n return 70;\n }\n }\n\n async build(ctx: BuildContext): Promise<TextBlock[]> {\n return flattenSystemPromptRegions(await this.buildRegions(ctx));\n }\n\n async buildRegions(ctx: BuildContext): Promise<SystemPromptRegions> {\n this._lastBuildTools = ctx.tools;\n // Pre-load skill entries so we can include them in the environment block\n // (which is cached). Skills are static per-session, so this is safe.\n if (this.opts.skillLoader && !this.skillCache) {\n try {\n const entries = await this.opts.skillLoader.listEntries();\n if (entries.length > 0) {\n const lines: string[] = [];\n for (const e of entries) {\n // Compact format: name + shortened trigger (full body in Active Skills)\n const shortTrigger = compactTrigger(e.trigger);\n lines.push(`- **${e.name}** (${shortTrigger})`);\n }\n this.skillCache = lines.join('\\n');\n }\n } catch {\n // skip\n }\n }\n\n const instructions = await this.instructions();\n const layer1 = instructions.system?.identity ?? LAYER_1_IDENTITY;\n const layer2 = await this.buildToolUsage(ctx.tools, ctx);\n const layer3 = await this.buildEnvironment(ctx);\n const layer3WithDir = `${layer3}\\n- Project root: ${ctx.projectRoot}`;\n const layer4 = await this.buildMemoryAndSkills();\n const layer5 = await this.buildMode();\n // Plans anchor the HOST agent across turns. Subagents run one\n // narrow task and shouldn't carry the host's strategic context \u2014\n // it just bloats their prompt and risks them mutating a plan\n // they weren't supposed to touch.\n const layer6 = ctx.subagent ? '' : await this.buildActivePlan();\n\n const core: TextBlock[] = [\n tagBlock({ type: 'text', text: layer1 }, 'identity'),\n tagBlock({ type: 'text', text: layer2 }, 'tool-usage'),\n ];\n const session: TextBlock[] = [\n tagBlock({ type: 'text', text: layer3WithDir }, 'environment'),\n ];\n const volatile: TextBlock[] = [];\n\n if (layer4.trim()) {\n session.push(\n tagBlock(\n { type: 'text', text: layer4, cache_control: { type: 'ephemeral' } },\n 'skills',\n ),\n );\n }\n\n if (layer5.trim()) {\n session.push(\n tagBlock(\n { type: 'text', text: layer5, cache_control: { type: 'ephemeral' } },\n 'mode',\n ),\n );\n }\n\n // Suggested skills for the active mode \u2014 helps the model know which\n // domain instructions to prioritize when multiple skills are loaded.\n if (this.opts.modeStore && this.opts.skillLoader) {\n try {\n const activeMode = await this.opts.modeStore.getActiveMode();\n if (activeMode?.suggestedSkills && activeMode.suggestedSkills.length > 0) {\n const skills = await this.opts.skillLoader.list();\n const loadedNames = new Set(skills.map((s) => s.name));\n const available = activeMode.suggestedSkills.filter((n) => loadedNames.has(n));\n if (available.length > 0) {\n session.push(\n tagBlock(\n {\n type: 'text',\n text: `Mode \"${activeMode.id}\" works best with these skills: ${available.join(', ')}. Their full instructions are in the Active Skills block above.`,\n cache_control: { type: 'ephemeral' },\n },\n 'mode',\n ),\n );\n }\n }\n } catch {\n // skip \u2014 non-critical hint\n }\n }\n\n if (layer6.trim()) {\n volatile.push(\n tagBlock(\n { type: 'text', text: layer6, cache_control: { type: 'ephemeral' } },\n 'plan',\n ),\n );\n }\n\n // System prompt contributors \u2014 plugins inject ephemeral context here.\n if (this.opts.contributors && this.opts.contributors.length > 0) {\n for (const c of this.opts.contributors) {\n try {\n const contributed = await c(ctx);\n for (const b of contributed) tagBlock(b, 'contributor');\n volatile.push(...contributed);\n } catch {\n // Contributor errors are swallowed \u2014 a bad plugin shouldn't\n // break the system prompt assembly.\n }\n }\n }\n\n // Leader-only after-task affordances (the `<nextsteps>` block + post-task\n // mailbox update). Host-only and appended last: subagents are headless\n // workers whose output is parsed (SDD spec/plan/task JSON) or rolled up by\n // the parent, so a `<nextsteps>` tag there is just noise that leaks into\n // specs/plans. Lives outside layer1 so the host keeps it in EVERY mode while\n // no subagent ever receives it.\n if (!ctx.subagent) {\n session.push(\n tagBlock(\n {\n type: 'text',\n text: instructions.system?.leaderAfterTask ?? LEADER_AFTER_TASK_PROMPT,\n },\n 'leader-after-task',\n ),\n );\n }\n\n return { core, session, volatile };\n }\n\n private async instructions(): Promise<InstructionBundle> {\n if (!this._instructionBundle) {\n this._instructionBundle = loadInstructionBundle(this.opts.instructionPaths).then((bundle) =>\n this.opts.instructionBundle\n ? mergeInstructionBundle(bundle, this.opts.instructionBundle)\n : bundle,\n );\n }\n return this._instructionBundle;\n }\n\n private instructionSection(\n bundle: InstructionBundle,\n key: string,\n vars: Record<string, string | number> = {},\n ): string {\n const template = bundle.sections?.[key];\n if (!template) return '';\n return template.replace(/\\{\\{\\s*([a-zA-Z0-9_.-]+)\\s*\\}\\}/g, (match, name: string) => {\n const value = vars[name];\n return value === undefined ? match : String(value);\n });\n }\n\n /**\n * Cached plan content keyed by (planPath, mtimeMs). The plan is read\n * once per system-prompt build; most turns don't change the plan, so\n * this avoids a blocking fs.readFile + JSON.parse on every iteration.\n * Cleared when the file's mtime changes (the `/plan` tool mutated it).\n */\n private _planCache?: { path: string; mtimeMs: number; text: string } | undefined;\n\n /**\n * Reads the session-scoped plan sidecar (when configured) and produces\n * a short \"Active plan\" block listing open items so the model is\n * anchored to the strategic roadmap every turn. Reads on every `build()`\n * so a plan edit (via `/plan` or the `plan` tool) reflects on the next\n * turn without restarting the session.\n */\n private async buildActivePlan(): Promise<string> {\n const planPath =\n typeof this.opts.planPath === 'function' ? this.opts.planPath() : this.opts.planPath;\n if (!planPath) return '';\n\n let raw: string;\n try {\n // Check mtime before reading \u2014 plans change at human pace (a few times\n // per session), not on every iteration. Stat is O(1) metadata; readFile\n // + JSON.parse is O(n) for the file content.\n const stat = await fs.stat(planPath);\n if (\n this._planCache &&\n this._planCache.path === planPath &&\n this._planCache.mtimeMs === stat.mtimeMs\n ) {\n return this._planCache.text;\n }\n raw = await fs.readFile(planPath, 'utf8');\n const text = this._formatPlan(raw);\n this._planCache = { path: planPath, mtimeMs: stat.mtimeMs, text };\n return text;\n } catch {\n // File missing, unreadable, or corrupt \u2014 clear cache and return empty.\n this._planCache = undefined;\n return '';\n }\n }\n\n private _formatPlan(raw: string): string {\n let parsed: {\n items?: Array<{ status?: string | undefined; title?: string | undefined }>;\n title?: string | undefined;\n };\n try {\n parsed = JSON.parse(raw);\n } catch {\n return '';\n }\n if (!Array.isArray(parsed.items) || parsed.items.length === 0) return '';\n const open = parsed.items.filter((i) => i?.status !== 'done');\n if (open.length === 0) return '';\n const lines = ['## Active plan'];\n if (parsed.title) lines.push(`*${parsed.title}*`, '');\n parsed.items.forEach((it, idx) => {\n const mark = it?.status === 'done' ? '[x]' : it?.status === 'in_progress' ? '[~]' : '[ ]';\n lines.push(`${idx + 1}. ${mark} ${it?.title ?? '(untitled)'}`);\n });\n lines.push(\n '',\n 'Use `/plan` (user) or the `plan` tool to update status as you progress. The roadmap survives session resume.',\n );\n return lines.join('\\n');\n }\n\n private async buildToolUsage(tools: Tool[], ctx: BuildContext): Promise<string> {\n if (tools.length === 0) return '## Tool usage\\n\\nNo tools registered.';\n const instructions = await this.instructions();\n\n // Cache: tools array is stable (same reference) until a registry mutation\n // thanks to B2 (ToolRegistry snapshot). Online agents are keyed by content\n // fingerprint \u2014 the mailbox rebuilds the array on every status check, so\n // reference equality would always miss. When all three keys match the\n // previous build, the full output is identical \u2014 return the cached\n // string. Including `tier` in the key ensures that mutating\n // `opts.tokenSavingMode` between builds (rare but supported via the\n // `private readonly opts` design) recomputes the prompt with the\n // new tier's truncation limits and tier-gated content.\n const agentsHash = this.agentsFingerprint(ctx.onlineAgents);\n const tier = this.tier;\n if (\n this._toolsUsageCache?.toolsRef === tools &&\n this._toolsUsageCache?.agentsHash === agentsHash &&\n this._toolsUsageCache?.tier === tier\n ) {\n return this._toolsUsageCache.text;\n }\n\n // Group tools by category for a cleaner listing when categories are used.\n const byCat = new Map<string, Tool[]>();\n const uncategorized: Tool[] = [];\n for (const t of tools) {\n if (t.category) {\n let group = byCat.get(t.category);\n if (!group) {\n group = [];\n byCat.set(t.category, group);\n }\n group.push(t);\n } else {\n uncategorized.push(t);\n }\n }\n\n const lines = ['## Tool usage'];\n const descLimit = this.toolDescLimit();\n\n // Categorized tools\n for (const [cat, catTools] of byCat) {\n lines.push(`\\n### ${cat}`);\n for (const t of catTools) {\n const hint = t.usageHint ?? t.description;\n // Trim to the tier-specific limit, preferring sentence boundaries.\n const desc =\n hint.length > descLimit\n ? hint.slice(0, hint.indexOf('.', 20) + 1 || descLimit) +\n (hint.length > descLimit ? '\u2026' : '')\n : hint.trim();\n lines.push(`- **${t.name}** \u2014 ${desc}`);\n const boundary = this.renderToolSelectionBoundary(t);\n if (boundary) lines.push(` ${boundary}`);\n }\n }\n\n // Uncategorized tools\n if (uncategorized.length > 0) {\n if (byCat.size > 0) lines.push('');\n for (const t of uncategorized) {\n const hint = t.usageHint ?? t.description;\n lines.push(`\\n### ${t.name}\\n${hint.trim()}`);\n const boundary = this.renderToolSelectionBoundary(t);\n if (boundary) lines.push(boundary);\n }\n }\n\n // Common tool chain patterns \u2014 teaches model how to compose tools effectively.\n // Skipped in minimal and aggressive tiers \u2014 model already knows these patterns\n // and aggressive users are under context pressure.\n if (this.tier !== 'minimal' && this.tier !== 'aggressive') {\n const commonPatterns = this.instructionSection(instructions, 'tool.common.patterns');\n if (commonPatterns) lines.push(commonPatterns);\n }\n\n // Delegation guidance \u2014 included when the `delegate` tool is present.\n // Without this block the model doesn't know that multi-agent work is\n // even an option, and `delegate` sits unused while the host agent\n // tries to do everything in one expensive context.\n // Tier behaviour:\n // - 'off' / 'medium' / 'aggressive' \u2192 full block\n // - 'light' \u2192 minimal one-liner\n // - 'minimal' \u2192 skipped\n const hasDelegate = tools.some((t) => t.name === 'delegate');\n if (hasDelegate) {\n const delegateTool = tools.find((t) => t.name === 'delegate');\n const enumValues = (() => {\n const role = (\n delegateTool?.inputSchema as\n | { properties?: { role?: { enum?: unknown | undefined } } }\n | undefined\n )?.properties?.role?.enum;\n return Array.isArray(role) ? (role.filter((r) => typeof r === 'string') as string[]) : [];\n })();\n const roleList = enumValues.length > 0 ? enumValues.join(', ') : '(no roster configured)';\n if (this.tier === 'minimal') {\n // Skip \u2014 don't emit any delegation guidance\n } else if (this.tier === 'light' || this.tier === 'medium' || this.tier === 'aggressive') {\n // Token-saving tiers get the compact one-liner instead of the full\n // multi-paragraph guidance. `aggressive` joins the compact group \u2014\n // a user under context pressure doesn't need a 600-token essay on\n // subagent scoping.\n const delegation = this.instructionSection(instructions, 'tool.delegation.compact', {\n roleList,\n });\n if (delegation) lines.push(delegation);\n } else {\n const delegation = this.instructionSection(instructions, 'tool.delegation.full', {\n roleList,\n });\n if (delegation) lines.push(delegation);\n }\n }\n\n // Mailbox guidance \u2014 included when any mailbox tool is present.\n // Tier behaviour:\n // - 'off' \u2192 full block\n // - every token-saving tier \u2192 compact project-wide contract\n //\n // Note: 'aggressive' was previously listed with 'off' for the\n // full block, but per the parallel-session decision (Option H,\n // `leader@1b68eb14`): at aggressive, the 400-token mailbox essay\n // is the largest single guidance section and users under context\n // pressure don't need it. The compact one-liner is enough.\n const hasMailbox = tools.some(\n (t) => t.name === 'mailbox' || t.name === 'mail_send' || t.name === 'mail_inbox',\n );\n if (hasMailbox) {\n // Build online-agent info \u2014 cached by a rendered-field fingerprint so\n // joins/leaves and live status/task/tool changes invalidate it.\n const onlineAgentsInfo = this.renderOnlineAgents(ctx.onlineAgents);\n const hasMailboxPowerTool = tools.some((t) => t.name === 'mailbox');\n const mailStatusCommand = tools.some((t) => t.name === 'fleet_status')\n ? '`fleet_status`'\n : hasMailboxPowerTool\n ? '`mailbox action=status` or `mailbox action=online`'\n : 'the online-agent list above';\n const mailInboxCommand = tools.some((t) => t.name === 'mail_inbox')\n ? '`mail_inbox`'\n : '`mailbox action=check`';\n const mailSendCommand = tools.some((t) => t.name === 'mail_send')\n ? '`mail_send`'\n : '`mailbox action=send`';\n const mailboxVars = {\n onlineAgentsInfo,\n mailStatusCommand,\n mailInboxCommand,\n mailSendCommand,\n };\n if (this.tier !== 'off') {\n // Minimal: keep just the header and agent count.\n // `aggressive` joins `light`/`medium` \u2014 the 400-token mailbox essay\n // is the largest single guidance section; users under context pressure\n // don't need it.\n const mailbox = this.instructionSection(\n instructions,\n 'tool.mailbox.compact',\n mailboxVars,\n );\n if (mailbox) lines.push(mailbox);\n } else {\n const mailbox = this.instructionSection(instructions, 'tool.mailbox.full', mailboxVars);\n if (mailbox) lines.push(mailbox);\n }\n }\n\n // Commit hygiene \u2014 shown whenever the structured `git` tool is available.\n // Other agents (or a separate wrongstack process, or a human) may be\n // editing the SAME working tree at the same time; a blanket commit captures\n // their half-done work and there is no clean way to undo a shared commit.\n const hasGitTool = tools.some((t) => t.name === 'git');\n if (hasGitTool && this.tier !== 'minimal' && this.tier !== 'light') {\n const commitHygiene = this.instructionSection(instructions, 'tool.commit.hygiene');\n if (commitHygiene) lines.push(commitHygiene);\n }\n\n // MCP lazy-loading guidance \u2014 shown whenever mcp_control is registered.\n // Tier behaviour:\n // - 'off' / 'medium' \u2192 full guidance block\n // - 'minimal' / 'light' / 'aggressive' \u2192 minimal one-liner\n //\n // Note: 'aggressive' was previously listed with 'off' for the\n // full block, but per the parallel-session decision (Option H):\n // at aggressive, the full MCP workflow (activate \u2192 use \u2192\n // deactivate) is documented elsewhere and the meta-tool\n // `mcp_use` is sufficient. The one-liner is enough.\n const hasMcpControl = tools.some((t) => t.name === 'mcp_control');\n const hasMcpUse = tools.some((t) => t.name === 'mcp_use');\n if (hasMcpControl) {\n if (this.tier === 'minimal' || this.tier === 'light' || this.tier === 'aggressive') {\n // Minimal one-liner \u2014 `aggressive` joins `minimal`/`light`. The full\n // MCP workflow (activate \u2192 use \u2192 deactivate) is documented elsewhere\n // and the meta-tool `mcp_use` is sufficient at any tier that has it.\n const mcp = this.instructionSection(\n instructions,\n hasMcpUse ? 'tool.mcp.compact.use' : 'tool.mcp.compact.control',\n );\n if (mcp) lines.push(mcp);\n } else {\n // Full block\n const mcp = this.instructionSection(\n instructions,\n hasMcpUse ? 'tool.mcp.full.use' : 'tool.mcp.full.control',\n );\n if (mcp) lines.push(mcp);\n }\n }\n\n // Context management guidance \u2014 shown when context_manager is registered.\n // Tier behaviour:\n // - 'off' / 'aggressive' \u2192 full block\n // - 'medium' \u2192 minimal one-liner\n // - 'minimal' / 'light' \u2192 skipped\n const hasContextManager = tools.some((t) => t.name === 'context_manager');\n if (hasContextManager) {\n if (this.tier === 'minimal' || this.tier === 'light') {\n // Skip\n } else if (this.tier === 'medium') {\n const contextManagement = this.instructionSection(\n instructions,\n 'tool.context.management.compact',\n );\n if (contextManagement) lines.push(contextManagement);\n } else {\n // Adaptive threshold based on model context window size.\n // Small context (<=32k) \u2192 trigger earlier; large context (>32k) \u2192 more relaxed.\n // Fallback to 0 when unknown \u2192 conservative compaction (50 % threshold).\n const maxCtx = this.modelCapabilities()?.maxContextTokens ?? 0;\n const threshold = maxCtx <= 32000 ? '50' : '70';\n const contextManagement = this.instructionSection(\n instructions,\n 'tool.context.management.full',\n { threshold },\n );\n if (contextManagement) lines.push(contextManagement);\n }\n }\n\n // Store cache \u2014 keyed by tools reference (B2 snapshot) + agents content\n // fingerprint + tier, so it auto-invalidates when tools change, agents\n // join/leave, or the token-saving tier changes.\n const text = lines.join('\\n');\n this._toolsUsageCache = { toolsRef: tools, agentsHash, tier, text };\n return text;\n }\n\n private renderToolSelectionBoundary(tool: Tool): string {\n const selection = tool.selection;\n if (!selection?.doNotUseWhen.trim()) return '';\n const alternatives = selection.useInstead?.filter(Boolean) ?? [];\n const instead = alternatives.length > 0 ? ` Use ${alternatives.map((name) => `\\`${name}\\``).join(' or ')} instead.` : '';\n return `Do not use when ${selection.doNotUseWhen.trim()}${instead}`;\n }\n\n /**\n * Cheap content fingerprint of the online agents array. The mailbox\n * rebuilds the array as a fresh object on every status check, so caching\n * by reference always misses \u2014 this lets the renderOnlineAgents and\n * buildToolUsage caches detect rendered identity/status/task/tool changes\n * instead.\n *\n * O(n) over every field rendered in the peer snapshot. This matters because\n * status/task/tool changes should invalidate the prompt just like joins and\n * leaves do. Uses FNV-1a over character codes; a collision would only leave a\n * stale cosmetic snapshot in the prompt.\n */\n private agentsFingerprint(agents: readonly MailboxAgentStatus[] | undefined): string {\n if (!agents || agents.length === 0) return '0';\n let h = 0x811c9dc5;\n for (const a of agents) {\n const fields = [\n a.agentId,\n a.name,\n a.source,\n a.sessionId,\n a.status,\n a.currentTask,\n a.currentTool,\n a.online ? '1' : '0',\n ];\n for (const field of fields) {\n const value = field ?? '';\n for (let i = 0; i < value.length; i++) {\n h ^= value.charCodeAt(i);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n h ^= 0xff;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n }\n return `${agents.length}:${h.toString(36)}`;\n }\n\n /**\n * Render the online agents list, cached by content fingerprint. The agents\n * list changes at join/leave pace (seconds to minutes), not every prompt\n * build turn (hundreds of ms). The fingerprint detects membership changes\n * without holding the array reference \u2014 the mailbox rebuilds the array as\n * a fresh object on every status check, so reference equality always misses.\n *\n * Tier behaviour:\n * - 'off' / 'medium' / 'aggressive' \u2192 full list with names, sessions, sources\n * - 'minimal' / 'light' \u2192 count only (no list)\n */\n private renderOnlineAgents(agents: readonly MailboxAgentStatus[] | undefined): string {\n if (!agents || agents.length === 0) return '';\n\n // Content fingerprint: detects membership changes without holding the\n // array reference, which is rebuilt as a fresh object on every status check.\n const hash = this.agentsFingerprint(agents);\n if (this._lastOnlineAgents?.hash === hash) {\n return this._lastOnlineAgents.text;\n }\n\n const totalCount = agents.length;\n // minimal / light tiers: count only, no list\n if (this.tier === 'minimal' || this.tier === 'light') {\n const text = ` (${totalCount} agent${totalCount !== 1 ? 's' : ''} online)`;\n this._lastOnlineAgents = { hash, text };\n return text;\n }\n\n const inlineData = (value: string, max = 120): string =>\n value.replace(/[`\\r\\n]+/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, max);\n const agentList = agents\n .map((a) => {\n const details = [\n `id: \\`${inlineData(a.agentId ?? a.name, 96)}\\``,\n `client: ${inlineData(a.source ?? 'unknown', 32)}`,\n a.status ? `status: ${inlineData(a.status, 32)}` : undefined,\n a.currentTask ? `task: \\`${inlineData(a.currentTask)}\\`` : undefined,\n a.currentTool ? `tool: \\`${inlineData(a.currentTool, 64)}\\`` : undefined,\n a.sessionId ? `session: ${shortSessionId(inlineData(a.sessionId, 96))}` : undefined,\n ].filter((part): part is string => part !== undefined);\n return `- **${inlineData(a.name, 96)}** \u2014 ${details.join('; ')}`;\n })\n .join('\\n');\n const text = `\\n\\n**Currently online (${totalCount} agent${totalCount !== 1 ? 's' : ''}):**\\n${agentList}`;\n this._lastOnlineAgents = { hash, text };\n return text;\n }\n\n private async buildEnvironment(ctx: BuildContext): Promise<string> {\n const modelCapabilities = this.modelCapabilities();\n const cacheKey = [\n ctx.projectRoot,\n ctx.provider ?? '',\n ctx.model ?? '',\n modelCapabilities?.maxContextTokens ?? 0,\n modelCapabilities?.supportsTools ? 1 : 0,\n modelCapabilities?.supportsVision ? 1 : 0,\n modelCapabilities?.supportsReasoning ? 1 : 0,\n ].join('\\0');\n const cached = this.envCacheByRoot.get(cacheKey);\n if (cached) return cached;\n const today = this.opts.todayIso ?? new Date().toISOString().slice(0, 10);\n const platform = `${os.platform()} ${os.release()}`;\n // The bash tool's effective shell, pinned at boot via WRONGSTACK_SHELL.\n // On POSIX we keep reporting the raw $SHELL; on Windows we report the\n // resolved shell + a \"write X syntax\" nudge, and append a syntax guidance\n // sub-block below so the model doesn't default to bash/POSIX idioms.\n const effShell = effectiveShell(os.platform(), process.env['WRONGSTACK_SHELL']);\n const shell =\n effShell === 'posix'\n ? (process.env.SHELL ?? process.env.ComSpec ?? 'unknown')\n : SHELL_DISPLAY[effShell];\n const node = process.version;\n const isGit = await this.dirExists(path.join(ctx.projectRoot, '.git'));\n // Fan out the per-root probes so the prompt build doesn't serialize\n // ~12 fs.access calls plus the git status spawn back-to-back. On a\n // cold cache (CI / first turn) this trims hundreds of ms.\n const [git, langs] = await Promise.all([\n isGit ? this.gitStatus(ctx.projectRoot) : Promise.resolve('not a git repo'),\n this.detectLanguages(ctx.projectRoot),\n ]);\n\n // Tier-aware environment block content.\n // - 'off': Full \u2014 all fields\n // - 'minimal': Compact single line \u2014 git + date only\n // - 'light': +platform\n // - 'medium': +languages\n // - 'aggressive': +capabilities (context window, provider/model)\n const tier = this.tier;\n const lines: string[] = ['## Environment'];\n\n if (tier === 'minimal') {\n // Single compact line\n lines.push(`- Git: ${git} | Date: ${today}`);\n } else {\n lines.push(`- Operating system: ${platform}`);\n if (tier !== 'light') {\n lines.push(`- Shell: ${shell}`);\n lines.push(`- Node.js: ${node}`);\n }\n // Languages appear in the full ('off') block and the richer trimming\n // tiers; only 'minimal' (single line) and 'light' (platform only) omit\n // them. 'off' is the most complete tier (no token saving), per the\n // toolDescLimit ordering off=80 > aggressive=70 > \u2026 > minimal=40.\n if (tier === 'off' || tier === 'medium' || tier === 'aggressive') {\n lines.push(`- Detected languages: ${langs}`);\n }\n lines.push(`- Git status: ${git}`);\n lines.push(`- Today's date: ${today}`);\n if (tier === 'aggressive') {\n if (ctx.provider || ctx.model) {\n lines.push(\n `- Running on: ${ctx.provider ?? '<unknown provider>'}/${ctx.model ?? '<unknown model>'}`,\n );\n }\n if (modelCapabilities) {\n lines.push(\n `- Context window: ${modelCapabilities.maxContextTokens.toLocaleString()} tokens max`,\n );\n }\n }\n if (tier !== 'aggressive' && modelCapabilities) {\n lines.push(\n `- Context window: ${modelCapabilities.maxContextTokens.toLocaleString()} tokens max`,\n );\n }\n if (tier !== 'aggressive' && (ctx.provider || ctx.model)) {\n lines.push(\n `- Running on: ${ctx.provider ?? '<unknown provider>'}/${ctx.model ?? '<unknown model>'}`,\n );\n }\n if (tier !== 'aggressive' && this.opts.modeId && this.opts.modeId !== 'default') {\n lines.push(`- Mode: ${this.opts.modeId}`);\n }\n }\n\n // Shell syntax guidance \u2014 only meaningful on Windows, where the model must\n // not fall back to bash/POSIX idioms. Tier-gated: full for off/medium/\n // aggressive, a one-liner for light, omitted for minimal. POSIX returns ''.\n if (effShell !== 'posix' && tier !== 'minimal') {\n const guide = shellGuidanceBlock(effShell, tier === 'light' ? 'short' : 'full');\n if (guide) lines.push('', guide);\n }\n\n if (this.skillCache) {\n lines.push(\n '',\n '## Skills in scope for this session',\n this.skillCache,\n '',\n this.opts.skillMode === 'progressive'\n ? 'Skill names and triggers are injected below; load full instructions deterministically with the `skill` tool before relying on one.'\n : this.isCompact\n ? 'Compact skill instructions are injected in the Active Skills block below (Overview + Rules only).'\n : 'Skill bodies are injected below up to the eager budget; overflow remains listed by name and trigger for deterministic loading with the `skill` tool.',\n );\n }\n const text = lines.join('\\n');\n this.envCacheByRoot.set(cacheKey, text);\n return text;\n }\n\n private modelCapabilities(): ModelCapabilities | undefined {\n const caps = this.opts.modelCapabilities;\n return typeof caps === 'function' ? caps() : caps;\n }\n\n private async buildMemoryAndSkills(): Promise<string> {\n const parts: string[] = [];\n // Memory injection count per tier: off=5, minimal=3, light=5, medium=5, aggressive=5\n const memoryCount = this.tier === 'minimal' || this.tier === 'light' ? 3 : 5;\n const compactMemory = this.tier === 'minimal'; // compact = text only, no badges/tags\n // When a per-turn memory retriever owns injection (Super Memory turn\n // middleware), skip the static prompt section so memory flows through a\n // single channel \u2014 no double injection.\n if (this.opts.memoryStore && this.opts.injectMemory !== false) {\n try {\n // Use relevance scoring when available, fall back to full dump.\n if (this.opts.memoryStore.scoreRelevant) {\n const toolNames = this._lastBuildTools?.map((t) => t.name) ?? [];\n const scored = await this.opts.memoryStore.scoreRelevant(\n {\n currentTask: '',\n toolNames,\n },\n 'project-memory',\n memoryCount,\n );\n if (scored.length > 0) {\n const lines: string[] = ['# Relevant Memory'];\n for (const e of scored) {\n if (compactMemory) {\n lines.push(`- ${e.text}`);\n } else {\n const badge = e.type ? `[\\`${e.type.replace('_', '-')}\\`] ` : '';\n const priorityMark =\n e.priority === 'critical' ? '\u26A1' : e.priority === 'high' ? '\u25B2' : '';\n lines.push(\n `- ${priorityMark}${badge}${e.text}${e.tags ? ` \\`#${e.tags.join(' #')}\\`` : ''}`,\n );\n }\n }\n parts.push(lines.join('\\n'));\n }\n } else {\n const mem = await this.opts.memoryStore.readAll();\n if (mem.trim()) parts.push(`# Project Memory\\n\\n${mem}`);\n }\n } catch {\n // skip\n }\n }\n // Skill bodies \u2014 load once and cache for the session lifetime.\n // Skills are listed by name+trigger in buildEnvironment (envCache);\n // here we inject the full body content so the model has the actual\n // domain instructions, not just a trigger hint.\n // In token-saving mode, skill bodies are compacted to save tokens:\n // only the Overview and Rules sections (~400 chars max per skill).\n if (this.opts.skillLoader) {\n if (this.opts.skillMode === 'progressive') {\n // Progressive disclosure \u2014 only the metadata manifest is injected; the\n // agent loads full bodies on demand via the `skill` tool.\n if (this.skillBodyCache === undefined) {\n await this.buildProgressiveSkillManifest();\n }\n } else if (this.isCompact) {\n // Compact mode \u2014 build once, cache\n if (this.skillBodyCache === undefined) {\n await this.buildCompactSkillBodies();\n }\n } else {\n // Full mode \u2014 build once, cache\n if (this.skillBodyCache === undefined) {\n await this.buildFullSkillBodies();\n }\n }\n }\n if (this.skillBodyCache) {\n parts.push(`# Active Skills\\n\\n${this.skillBodyCache}`);\n }\n return parts.join('\\n\\n');\n }\n\n /**\n * Build the progressive-disclosure manifest: list each skill's name + trigger\n * only and instruct the agent to call the `skill` tool to load a body. No\n * bodies are injected \u2014 the agent pulls them on demand (agentskills.io tier 2).\n */\n private async buildProgressiveSkillManifest(): Promise<void> {\n if (!this.opts.skillLoader) {\n this.skillBodyCache = '';\n return;\n }\n try {\n const entries = await this.opts.skillLoader.listEntries();\n if (entries.length === 0) {\n this.skillBodyCache = '';\n return;\n }\n const lines = [\n 'Call the `skill` tool to load a skill before relying on it.',\n '',\n '| Skill | Use when |',\n '|---|---|',\n ];\n for (const e of entries) {\n const trigger = (e.trigger ?? '').replace(/\\|/g, '\\\\|').replace(/\\n+/g, ' ').trim();\n lines.push(`| \\`${e.name}\\` | ${trigger} |`);\n }\n this.skillBodyCache = lines.join('\\n');\n } catch {\n this.skillBodyCache = '';\n }\n }\n\n /** Build full skill bodies (token-saving OFF), bounded by an overall budget. */\n private async buildFullSkillBodies(): Promise<void> {\n try {\n const skills = await this.opts.skillLoader!.list();\n if (skills.length === 0) {\n this.skillBodyCache = '';\n return;\n }\n // Overall budget: the loader returns skills highest-priority first, so the\n // most relevant (project, then user) skills get a full body; the rest are\n // listed as a manifest the agent loads on demand via the `skill` tool.\n // Without this, discovering many skills (foreign agents add a lot) would\n // bloat every prompt with every skill body.\n const budget = this.opts.skillEagerMaxChars ?? SKILL_LIMITS.EAGER_DEFAULT_MAX_CHARS;\n const bodies: string[] = [];\n const overflow: string[] = [];\n let used = 0;\n for (const s of skills) {\n try {\n const raw = await this.opts.skillLoader!.readBody(s.name);\n const trimmed = stripFrontmatter(raw).trim();\n if (!trimmed) continue;\n // Per-skill cap (I5 audit): a misconfigured multi-MB file can't bloat.\n const entry = `## Skill: ${s.name}\\n\\n${capSkillBody(trimmed)}`;\n if (used + entry.length <= budget) {\n bodies.push(entry);\n used += entry.length;\n } else {\n overflow.push(`- ${s.name}`);\n }\n } catch {\n // skip unreadable skill\n }\n }\n let out = bodies.join('\\n\\n---\\n\\n');\n if (overflow.length > 0) {\n const note =\n overflow.length === skills.length\n ? '## Available skills (load with the `skill` tool)'\n : '## Other available skills (not injected \u2014 load with the `skill` tool)';\n out += `${out ? '\\n\\n---\\n\\n' : ''}${note}\\n${overflow.join('\\n')}`;\n }\n this.skillBodyCache = out;\n } catch {\n this.skillBodyCache = '';\n }\n }\n\n /**\n * Build compact skill bodies for token-saving mode.\n * Uses `readSaveBody` from the skill loader which tries `SKILL.save.md`\n * first, then falls back to auto-compaction.\n */\n private async buildCompactSkillBodies(): Promise<void> {\n if (!this.opts.skillLoader) {\n this.skillBodyCache = '';\n return;\n }\n try {\n const skills = await this.opts.skillLoader.list();\n if (skills.length > 0) {\n const bodies: string[] = [];\n for (const s of skills) {\n try {\n const saveBody = await this.opts.skillLoader.readSaveBody(s.name);\n const clean = stripFrontmatter(saveBody);\n if (clean.trim()) {\n bodies.push(`## Skill: ${s.name}\\n\\n${clean.trim()}`);\n }\n } catch {\n // skip unreadable skill\n }\n }\n this.skillBodyCache = bodies.length > 0 ? bodies.join('\\n\\n---\\n\\n') : '';\n } else {\n this.skillBodyCache = '';\n }\n } catch {\n this.skillBodyCache = '';\n }\n }\n\n private async buildMode(): Promise<string> {\n // Use pre-resolved modePrompt if available (avoids redundant async call).\n if (this.opts.modePrompt) return this.opts.modePrompt;\n if (!this.opts.modeStore) return '';\n const mode = await this.opts.modeStore.getActiveMode();\n if (!mode?.prompt) return '';\n return mode.prompt;\n }\n\n private async dirExists(p: string): Promise<boolean> {\n try {\n const stat = await fs.stat(p);\n return stat.isDirectory();\n } catch {\n return false;\n }\n }\n\n private async gitStatus(root: string): Promise<string> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (s: string): void => {\n if (settled) return;\n settled = true;\n resolve(s);\n };\n let proc: ReturnType<typeof spawn> | undefined;\n // 2 s ceiling: a hung git status (corrupt index, .git/index.lock\n // held by another process, network FS hiccup) must not stall the\n // whole prompt build for a turn.\n const timer = setTimeout(() => {\n proc?.kill('SIGKILL');\n finish('git timeout');\n }, 2000);\n try {\n proc = spawn('git', ['status', '--porcelain=v1', '--branch'], {\n cwd: root,\n env: buildChildEnv(),\n stdio: ['ignore', 'pipe', 'ignore'],\n windowsHide: true,\n });\n let buf = '';\n proc.stdout?.on('data', (c) => {\n buf += c.toString();\n });\n proc.on('error', () => {\n clearTimeout(timer);\n finish('git error');\n });\n proc.on('close', () => {\n clearTimeout(timer);\n const lines = buf.split('\\n').filter(Boolean);\n const branchLine = lines[0] ?? '';\n const branchMatch = branchLine.match(/## ([^\\s.]+)/);\n const branch = branchMatch?.[1] ?? 'detached';\n const dirty = lines.slice(1);\n const staged = dirty.filter((l) => /^[MARCD]/.test(l)).length;\n const modified = dirty.length - staged;\n finish(`branch=${branch}, ${modified} modified, ${staged} staged`);\n });\n } catch {\n clearTimeout(timer);\n finish('git unavailable');\n }\n });\n }\n\n private async detectLanguages(root: string): Promise<string> {\n const checks: Array<[string, string]> = [\n ['package.json', 'JavaScript/TypeScript'],\n ['tsconfig.json', 'TypeScript'],\n ['go.mod', 'Go'],\n ['Cargo.toml', 'Rust'],\n ['pyproject.toml', 'Python'],\n ['requirements.txt', 'Python'],\n ['Gemfile', 'Ruby'],\n ['pom.xml', 'Java'],\n ['build.gradle', 'Java/Kotlin'],\n ['composer.json', 'PHP'],\n ['mix.exs', 'Elixir'],\n ];\n // Fan out the marker probes. Sequential await on 11 fs.access calls\n // adds latency on cold cache for no reason \u2014 each probe is independent.\n const hits = await Promise.all(\n checks.map(async ([marker, lang]) => {\n try {\n await fs.access(path.join(root, marker));\n return lang;\n } catch {\n return null;\n }\n }),\n );\n const langs = new Set(hits.filter((l): l is string => l !== null));\n return langs.size === 0 ? 'unknown' : Array.from(langs).join(', ');\n }\n}\n\n/** Strip YAML frontmatter from a SKILL.md file, returning only the body. */\nfunction stripFrontmatter(raw: string): string {\n if (!raw.startsWith('---')) return raw;\n const end = raw.indexOf('\\n---', 4);\n if (end === -1) return raw;\n // Skip past the closing `---` and the following newline\n let body = raw.slice(end + 4);\n if (body.startsWith('\\n')) body = body.slice(1);\n return body;\n}\n\n/**\n * Maximum number of characters of a skill body to inject into the\n * prompt when building the full (token-saving OFF) Active Skills block.\n *\n * Real-world SKILL.md files are <5 KB; 16 KB is generous headroom.\n * Without a cap, a misconfigured multi-MB skill file can bloat the\n * prompt by tens of thousands of tokens. This cap matches the\n * bash `MAX_OUTPUT` (32 KB) at a smaller scale and keeps the full\n * path comparable in size to the compact path's natural output.\n *\n * I5 audit (Sprint 3). See\n * `packages/core/tests/core/system-prompt-builder-i-skills.test.ts`.\n */\n/**\n * Cap a skill body at `SKILL_LIMITS.MAX_SKILL_BODY_CHARS`, truncating at a\n * paragraph boundary when possible to preserve readability. Appends an\n * ellipsis marker when truncated so the model can detect the cap.\n */\nfunction capSkillBody(body: string): string {\n const max = SKILL_LIMITS.MAX_SKILL_BODY_CHARS;\n if (body.length <= max) return body;\n // Try to cut at the last paragraph break (`\\n\\n`) within the\n // budget so the truncated body ends cleanly. Fall back to a\n // hard cut if no paragraph break exists.\n const budget = max - 1; // reserve 1 char for ellipsis\n const cut = body.lastIndexOf('\\n\\n', budget);\n const truncated = cut > budget / 2 ? body.slice(0, cut) : body.slice(0, budget);\n return truncated + '\u2026';\n}\n\n/**\n * Compact a skill trigger description into a short label.\n * \"Use this skill when scanning source code for bugs...\"\n * \u2192 \"scanning source code for bugs, anti-patterns, code smells\"\n */\nfunction compactTrigger(trigger: string): string {\n // Strip common prefixes\n let s = trigger\n .replace(/^Use this skill when /i, '')\n .replace(/^Use this skill for /i, '')\n .replace(/^Use when /i, '')\n .replace(/\\.$/, '');\n // Truncate to ~72 chars at a word boundary\n if (s.length > 72) {\n const cut = s.lastIndexOf(' ', 68);\n s = cut > 50 ? s.slice(0, cut) + '\u2026' : s.slice(0, 68) + '\u2026';\n }\n return s;\n}\n", "import type { JSONSchema } from '../types/tool.js';\n\nexport interface ToolWireDefinitionLike {\n name: string;\n description?: string | undefined;\n inputSchema: unknown;\n}\n\nexport interface CompactToolDefinitionForWireOptions {\n /** Top-level tool description budget. */\n descriptionMaxChars?: number | undefined;\n /** Per-JSON-Schema `description` annotation budget. */\n schemaDescriptionMaxChars?: number | undefined;\n}\n\nexport interface CompactWireToolDefinition {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nconst TOOL_DESCRIPTION_MAX_CHARS = 400;\nconst SCHEMA_DESCRIPTION_MAX_CHARS = 120;\n\nconst compactCache = new WeakMap<object, CompactWireToolDefinition>();\n\n/**\n * Return the provider-wire version of a tool definition.\n *\n * Tool schemas remain structurally intact: validation keywords, property\n * names, required fields, enum values, and nested shapes are preserved. The\n * only reduction is on human prose annotations (`description`), which are the\n * largest repeated cost in provider tool declarations.\n */\nexport function compactToolDefinitionForWire(\n tool: ToolWireDefinitionLike,\n opts: CompactToolDefinitionForWireOptions = {},\n): CompactWireToolDefinition {\n const useDefaultOptions =\n opts.descriptionMaxChars === undefined && opts.schemaDescriptionMaxChars === undefined;\n if (useDefaultOptions && typeof tool === 'object' && tool !== null) {\n const cached = compactCache.get(tool);\n if (cached) return cached;\n }\n\n const compact: CompactWireToolDefinition = {\n name: tool.name,\n description: compactDescription(\n tool.description ?? '',\n opts.descriptionMaxChars ?? TOOL_DESCRIPTION_MAX_CHARS,\n ),\n inputSchema: normalizeTopLevelToolSchema(\n compactSchemaDescriptions(\n tool.inputSchema,\n opts.schemaDescriptionMaxChars ?? SCHEMA_DESCRIPTION_MAX_CHARS,\n ),\n ),\n };\n\n if (useDefaultOptions && typeof tool === 'object' && tool !== null) {\n compactCache.set(tool, compact);\n }\n return compact;\n}\n\n/**\n * Tool inputs are always JSON objects, but dynamically supplied MCP/plugin\n * schemas sometimes express that object as a top-level oneOf/anyOf/allOf.\n * Anthropic-family endpoints reject those combinators at the input_schema\n * root (including when reached through an OpenAI-compatible gateway such as\n * OmniRoute). Keep nested combinators intact and flatten only the root.\n * Runtime validation still uses the tool's original schema.\n */\nexport function normalizeTopLevelToolSchema(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n const combinators = (['oneOf', 'anyOf', 'allOf'] as const)\n .map((keyword) => ({ keyword, branches: schema[keyword] }))\n .filter((entry): entry is { keyword: 'oneOf' | 'anyOf' | 'allOf'; branches: unknown[] } =>\n Array.isArray(entry.branches),\n );\n if (combinators.length === 0) return schema;\n\n const out: Record<string, unknown> = { ...schema, type: 'object' };\n delete out['oneOf'];\n delete out['anyOf'];\n delete out['allOf'];\n\n const properties: Record<string, unknown> = isRecord(schema['properties'])\n ? { ...schema['properties'] }\n : {};\n let required = stringSet(schema['required']);\n\n for (const { keyword, branches } of combinators) {\n const objectBranches = branches.filter(isRecord);\n for (const branch of objectBranches) {\n if (isRecord(branch['properties'])) Object.assign(properties, branch['properties']);\n }\n\n const branchRequired = objectBranches.map((branch) => stringSet(branch['required']));\n if (keyword === 'allOf') {\n for (const fields of branchRequired) for (const field of fields) required.add(field);\n } else if (branchRequired.length > 0) {\n const common = new Set(\n [...branchRequired[0]!].filter((field) =>\n branchRequired.slice(1).every((fields) => fields.has(field)),\n ),\n );\n required = new Set([...required, ...common]);\n }\n }\n\n out['properties'] = properties;\n if (required.size > 0) out['required'] = [...required];\n else delete out['required'];\n return out;\n}\n\nfunction stringSet(value: unknown): Set<string> {\n return new Set(\n Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [],\n );\n}\n\nexport function compactSchemaDescriptions(\n schema: unknown,\n maxDescriptionChars = SCHEMA_DESCRIPTION_MAX_CHARS,\n): Record<string, unknown> {\n const compact = compactSchemaNode(schema, maxDescriptionChars);\n return isRecord(compact) ? compact : { type: 'object', properties: {} };\n}\n\nfunction compactSchemaNode(node: unknown, maxDescriptionChars: number): unknown {\n if (Array.isArray(node)) {\n return node.map((item) => compactSchemaNode(item, maxDescriptionChars));\n }\n if (!isRecord(node)) return node;\n\n const out: JSONSchema = {};\n for (const [key, value] of Object.entries(node)) {\n if (key === 'description' && typeof value === 'string') {\n out[key] = compactDescription(value, maxDescriptionChars);\n } else {\n out[key] = compactSchemaNode(value, maxDescriptionChars);\n }\n }\n return out;\n}\n\nexport function compactDescription(text: string, maxChars: number): string {\n const normalized = text.replace(/\\s+/g, ' ').trim();\n if (normalized.length <= maxChars) return normalized;\n if (maxChars <= 20) return normalized.slice(0, maxChars);\n\n const hardLimit = maxChars - 12;\n const boundary = findSemanticBoundary(normalized, hardLimit);\n const head = normalized.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd();\n return `${head} ...`;\n}\n\nexport function findSemanticBoundary(text: string, limit: number): number {\n const punctuation = Math.max(\n text.lastIndexOf('. ', limit),\n text.lastIndexOf('; ', limit),\n text.lastIndexOf(': ', limit),\n );\n if (punctuation >= Math.floor(limit * 0.45)) return punctuation + 1;\n\n const comma = text.lastIndexOf(', ', limit);\n if (comma >= Math.floor(limit * 0.6)) return comma + 1;\n\n const space = text.lastIndexOf(' ', limit);\n return space >= Math.floor(limit * 0.6) ? space : limit;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n", "import type { Message } from '../types/messages.js';\nimport { compactToolDefinitionForWire } from './tool-wire-compact.js';\n\n/**\n * Shared token estimation with JSON.stringify caching.\n * Avoids repeated stringification of tool input objects.\n *\n * ## Calibration\n *\n * `estimateRequestTokens` uses a fixed 3.5 chars/token heuristic \u2014 a\n * conservative overestimate that prevents underestimation but reduces\n * accuracy. After each API call, call `recordActualUsage()` with the\n * provider-authoritative effective prompt tokens (`input + cacheRead +\n * cacheWrite` after adapter normalization). The module maintains a rolling\n * average of `actual / estimated` ratio (EWM, \u03B1=0.3) and applies it to\n * subsequent calls via `estimateRequestTokensCalibrated`.\n *\n * Calibration is per-module (shared across all callers), which is\n * sufficient: the chars/token ratio is a property of the tokenizer,\n * not the model. Uncalibrated calls (before any samples, or when\n * `recordActualUsage` is not called) fall back to the uncalibrated\n * estimate so nothing breaks.\n */\n\nconst RoughTokenEstimate = (text: string, charsPerToken = 3.5): number =>\n Math.max(1, Math.ceil(text.length / charsPerToken));\n\n/** Calibration state: actual/estimated ratio via exponential weighted moving average. */\ninterface CalState {\n ratio: number; // current calibration multiplier (actual / estimated)\n count: number; // number of samples recorded\n prevEst: number; // estimated tokens from the most recent estimateRequestTokens call\n}\n\n/** EWM \u03B1 \u2014 higher = faster adaptation, more volatile. */\nconst CAL_ALPHA = 0.3;\n\n/**\n * Calibration is keyed so that, in a multi-agent / model-switching process,\n * each (provider, model) tokenizer gets its own ratio instead of all of them\n * collapsing onto one shared number. Callers that don't pass a key use the\n * shared `__global__` bucket \u2014 that preserves the original single-session\n * behavior and keeps all existing call sites working unchanged.\n */\nconst CALIBRATION_GLOBAL_KEY = '__global__';\nconst _cals = new Map<string, CalState>();\n\nfunction calState(key: string): CalState {\n let state = _cals.get(key);\n if (!state) {\n state = { ratio: 1.0, count: 0, prevEst: 0 };\n _cals.set(key, state);\n }\n return state;\n}\n\nconst MIN_SAMPLES_FOR_CALIBRATION = 3;\n\n/**\n * Fallback chars/token ratios per model family for providers that don't return\n * usage data. Used when `recordActualUsage` receives zero/negative tokens and\n * we have enough samples to trust the fallback. Keys are lowercase prefixes.\n */\nconst MODEL_FAMILY_RATIO: Record<string, number> = {\n // Anthropic: ~3.8-4.0 chars/token depending on model\n claude: 3.8,\n // OpenAI: ~4.0 chars/token\n 'gpt-4': 4.0,\n 'gpt-3.5': 4.0,\n // Google: ~3.5 chars/token\n gemini: 3.5,\n // DeepSeek: ~3.5 chars/token\n deepseek: 3.5,\n};\n\n/**\n * Cache of computed estimates keyed by the stringified input \u2014 not the\n * input object itself. Previously the cache was keyed by the input object\n * via WeakMap, but JSON.stringify() produces a new object reference each\n * call so the cache never hit. Now we use a Map with string keys so that\n * repeated stringifications of the same structure share a single entry.\n */\nconst ESTIMATE_CACHE = new Map<string, number>();\n/** Insertion-order queue for O(1) LRU eviction: shift from front on overcapacity. */\nconst _estimateCacheOrder: string[] = [];\n\nconst ESTIMATE_CACHE_MAX_SIZE = 50_000;\n\nfunction getCachedEstimate(key: string, compute: (key: string) => number): number {\n const existing = ESTIMATE_CACHE.get(key);\n if (existing !== undefined) return existing;\n if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {\n // Evict oldest half \u2014 O(1) per eviction (array shift + Map.delete) instead\n // of O(n) iteration over all 50 000 keys in the Map.\n while (ESTIMATE_CACHE.size > Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) {\n const oldest = _estimateCacheOrder.shift();\n if (oldest !== undefined) ESTIMATE_CACHE.delete(oldest);\n }\n }\n const estimate = compute(key);\n ESTIMATE_CACHE.set(key, estimate);\n _estimateCacheOrder.push(key);\n return estimate;\n}\n\n/**\n * Estimate tokens for a tool_use block input.\n * Caches the stringified result keyed by the stable string representation\n * to avoid repeated JSON.stringify calls during context window checks.\n */\nexport function estimateToolInputTokens(input: unknown): number {\n if (typeof input === 'string') return RoughTokenEstimate(input);\n if (input === null || typeof input !== 'object') {\n return RoughTokenEstimate(String(input));\n }\n // JSON.stringify is called once to form the cache key; RoughTokenEstimate\n // is deferred only on cache miss (compute callback), not wrapped unnecessarily.\n return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));\n}\n\n/**\n * Estimate tokens for a tool_result content.\n */\nexport function estimateToolResultTokens(content: string | unknown): number {\n if (typeof content === 'string') return RoughTokenEstimate(content);\n return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));\n}\n\n/**\n * Estimate tokens for a text block.\n */\nexport function estimateTextTokens(text: string): number {\n return RoughTokenEstimate(text);\n}\n\n/**\n * Compute and cache the token estimate for a single message. This is the\n * canonical per-message estimator \u2014 called once by ConversationState on\n * append/replace so the O(n\u00B7m) content-block walk happens at mutation time,\n * not on every context-pressure check.\n */\nexport function computeMessageTokens(msg: Message): number {\n if (typeof msg.content === 'string') return estimateTextTokens(msg.content);\n let total = 0;\n for (const b of msg.content) {\n if (b.type === 'text') total += estimateTextTokens(b.text);\n else if (b.type === 'tool_use') total += estimateToolInputTokens(b.input);\n else if (b.type === 'tool_result') total += estimateToolResultTokens(b.content);\n else total += RoughTokenEstimate(JSON.stringify(b));\n }\n return total;\n}\n\n/**\n * Estimate tokens for an array of messages (text + tool I/O), using the shared\n * 3.5 chars/token basis. This is the single canonical message-array estimator \u2014\n * compactors, the context_manager tool, and the `/context` display all route\n * through it so the number a user sees matches the number compaction decides on.\n *\n * When a message carries a pre-computed `_estTokens` field (set by\n * ConversationState on append/replace), it is used directly instead of\n * re-walking the content blocks \u2014 turning the O(n\u00B7m) scan into an O(n)\n * sum for fully-cached arrays.\n */\nexport function estimateMessageTokens(messages: readonly Message[]): number {\n let total = 0;\n for (const m of messages) {\n if (typeof m._estTokens === 'number' && m._estTokens > 0) {\n total += m._estTokens;\n continue;\n }\n total += computeMessageTokens(m);\n }\n return total;\n}\n\n/**\n * Real-usage-anchored input-token count. Given the provider's authoritative\n * prompt-token count from the last response (`anchorTokens`, a REAL number) and\n * the `messages.length` of the request that produced it (`anchorMsgCount`),\n * returns `anchorTokens + estimate(messages appended since)` \u2014 so everything up\n * to the last turn is exact and only the newest, not-yet-sent messages are\n * estimated. This is deliberately NOT calibrated: the base is already real, and\n * on the next response the whole thing re-anchors to the new real count.\n *\n * Returns `null` when there is no usable anchor (no response yet, or the\n * message array shrank below the anchor \u2014 e.g. after compaction \u2014 in which case\n * the caller falls back to a full estimate until the next response re-anchors).\n */\nexport function realAnchoredInputTokens(\n messages: readonly Message[],\n anchorTokens: number | undefined,\n anchorMsgCount: number | undefined,\n): number | null {\n if (typeof anchorTokens !== 'number' || anchorTokens <= 0) return null;\n if (typeof anchorMsgCount !== 'number' || anchorMsgCount < 0) return null;\n if (messages.length < anchorMsgCount) return null;\n const delta = anchorMsgCount === messages.length ? 0 : estimateMessageTokens(messages.slice(anchorMsgCount));\n return anchorTokens + delta;\n}\n\n/**\n * Rough estimate of tokens in a tool definition (name + description + schema).\n * Accounts for the JSON-serialized inputSchema which is sent to the API\n * but NOT included in roughEstimate(content).\n */\nexport function estimateToolDefTokens(tool: {\n name: string;\n description?: string | undefined;\n inputSchema: unknown;\n}): number {\n // Fast path: pre-computed by ToolRegistry at registration time.\n const cached = (tool as { _estDefTokens?: number | undefined })._estDefTokens;\n if (typeof cached === 'number' && cached > 0) return cached;\n\n const compact = compactToolDefinitionForWire(tool);\n return (\n RoughTokenEstimate(tool.name) +\n RoughTokenEstimate(compact.description) +\n RoughTokenEstimate(JSON.stringify(compact.inputSchema))\n );\n}\n\n/**\n * Estimate the total API request token count: system prompt + tool definitions\n * + conversation messages. Use this for context-window bar calculations\n * instead of roughEstimate (which only counts messages).\n *\n * The overhead ratio (overhead / messages) varies by conversation length:\n * - Short conversations (< 10 messages): ~30-50% overhead (large system+tools)\n * - Medium (10-50 messages): ~15-30%\n * - Long (> 50 messages): ~5-15%\n *\n * Returns { messages, systemPrompt, tools, total } for debugging display.\n */\nexport interface RequestTokenBreakdown {\n messages: number;\n systemPrompt: number;\n tools: number;\n total: number;\n}\n\nexport function estimateRequestTokens(\n messages: unknown,\n systemPrompt: unknown,\n tools: { name: string; description?: string | undefined; inputSchema: unknown }[],\n calibrationKey: string = CALIBRATION_GLOBAL_KEY,\n): RequestTokenBreakdown {\n // Messages: apply the same logic as roughEstimate\n let messagesTokens = 0;\n if (typeof messages === 'string') {\n messagesTokens = RoughTokenEstimate(messages);\n } else if (Array.isArray(messages)) {\n for (const m of messages) {\n if (typeof m === 'object' && m !== null && 'content' in m) {\n // Fast path: pre-computed per-message token estimate (set by\n // ConversationState on append/replace). Skips the O(m) content-block\n // walk entirely for cached messages.\n const cached = (m as { _estTokens?: number | undefined })._estTokens;\n if (typeof cached === 'number' && cached > 0) {\n messagesTokens += cached;\n continue;\n }\n const content = (m as { content: unknown }).content;\n if (typeof content === 'string') {\n messagesTokens += RoughTokenEstimate(content);\n } else if (Array.isArray(content)) {\n for (const b of content) {\n if (typeof b === 'object' && b !== null) {\n if ((b as { type?: string | undefined }).type === 'text') {\n messagesTokens += RoughTokenEstimate((b as { text: string }).text);\n } else {\n messagesTokens += RoughTokenEstimate(JSON.stringify(b));\n }\n }\n }\n }\n }\n }\n }\n\n // System prompt\n let systemTokens = 0;\n if (typeof systemPrompt === 'string') {\n systemTokens = RoughTokenEstimate(systemPrompt);\n } else if (Array.isArray(systemPrompt)) {\n for (const b of systemPrompt) {\n if (\n typeof b === 'object' &&\n b !== null &&\n (b as { type?: string | undefined }).type === 'text'\n ) {\n systemTokens += RoughTokenEstimate((b as { text: string }).text);\n }\n }\n }\n\n // Tool definitions\n let toolsTokens = 0;\n for (const t of tools) {\n toolsTokens += estimateToolDefTokens(t);\n }\n\n const total = messagesTokens + systemTokens + toolsTokens;\n\n // Record the raw estimate for calibration: the next recordActualUsage()\n // call will pair this against the actual API usage so the rolling ratio\n // stays in sync with the real chars/token ratio of the content.\n calState(calibrationKey).prevEst = total;\n\n return {\n messages: messagesTokens,\n systemPrompt: systemTokens,\n tools: toolsTokens,\n total,\n };\n}\n\n/**\n * Record the actual API input token count after a provider call so\n * `estimateRequestTokensCalibrated` can self-correct on subsequent calls.\n *\n * Prefer passing `estimatedInputTokens` explicitly (the calibrated pre-flight\n * estimate from the middleware) \u2014 this avoids race conditions when other code\n * also calls `estimateRequestTokens` between the pre-flight and this call\n * (e.g. audit logging in agent.ts).\n *\n * When `estimatedInputTokens` is omitted, falls back to the keyed bucket's\n * `prevEst` for backward compatibility with callers that don't have the\n * pre-flight value. `calibrationKey` selects the per-(provider,model) bucket\n * (defaults to the shared global bucket).\n */\nexport function recordActualUsage(\n actualInputTokens: number,\n estimatedInputTokens?: number,\n calibrationKey: string = CALIBRATION_GLOBAL_KEY,\n): void {\n if (actualInputTokens <= 0) return;\n const cal = calState(calibrationKey);\n const est = estimatedInputTokens ?? cal.prevEst;\n if (est <= 0) return;\n\n const sampleRatio = actualInputTokens / est;\n if (cal.count === 0) {\n cal.ratio = sampleRatio;\n } else {\n // EWM: new = \u03B1 * sample + (1-\u03B1) * old \u2192 \u03B1=0.3 = fast initial converge\n cal.ratio = CAL_ALPHA * sampleRatio + (1 - CAL_ALPHA) * cal.ratio;\n }\n // Sanity bound: keep the rolling ratio within [0.5, 1.5] so a sequence\n // of bad samples can't blow up the calibration for everyone.\n cal.ratio = Math.min(1.5, Math.max(0.5, cal.ratio));\n cal.count++;\n}\n\n/**\n * Returns the current calibration state for a bucket. Exposed for debugging\n * and tests \u2014 not needed by normal callers.\n */\nexport function getCalibrationState(calibrationKey: string = CALIBRATION_GLOBAL_KEY): {\n ratio: number;\n count: number;\n calibrated: boolean;\n} {\n const cal = calState(calibrationKey);\n return {\n ratio: cal.ratio,\n count: cal.count,\n calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION,\n };\n}\n\n/**\n * Like `estimateRequestTokens` but applies the rolling calibration factor\n * so context pressure readings converge on reality within a few iterations.\n *\n * Before any `recordActualUsage` samples are collected, returns the same\n * result as `estimateRequestTokens` (ratio = 1.0, no distortion).\n * After `MIN_SAMPLES_FOR_CALIBRATION` samples, applies the calibrated\n * multiplier capped to the range [0.5, 1.5] as a sanity bound.\n */\nexport function estimateRequestTokensCalibrated(\n messages: unknown,\n systemPrompt: unknown,\n tools: { name: string; description?: string | undefined; inputSchema: unknown }[],\n calibrationKey: string = CALIBRATION_GLOBAL_KEY,\n): RequestTokenBreakdown {\n const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);\n const cal = calState(calibrationKey);\n\n if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {\n const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));\n return {\n messages: Math.round(result.messages * safeRatio),\n systemPrompt: Math.round(result.systemPrompt * safeRatio),\n tools: Math.round(result.tools * safeRatio),\n total: Math.round(result.total * safeRatio),\n };\n }\n\n // No calibration samples yet \u2014 fall back to model-family ratio if available,\n // otherwise use the uncalibrated estimate (ratio = 1.0).\n const fallbackRatio = getModelFamilyRatio(calibrationKey);\n if (fallbackRatio !== null) {\n return {\n messages: Math.round(result.messages * fallbackRatio),\n systemPrompt: Math.round(result.systemPrompt * fallbackRatio),\n tools: Math.round(result.tools * fallbackRatio),\n total: Math.round(result.total * fallbackRatio),\n };\n }\n\n return result;\n}\n\n/** Per-block sample cap for the density scan \u2014 bounds work on giant blocks. */\nconst DENSITY_SAMPLE_PER_BLOCK = 4_096;\n/** Hard cap on total sampled chars so the density scan stays cheap. */\nconst DENSITY_SAMPLE_TOTAL_CAP = 2_000_000;\n\n/**\n * Estimate a **token-density multiplier** for content that the flat 3.5\n * chars/token basis under-counts. The basis is tuned for ASCII English\n * (~4 chars/token); CJK, and other high-codepoint scripts tokenize at ~1.5-2\n * chars/token, so a message that is mostly CJK carries up to ~2.3\u00D7 the tokens\n * the flat basis predicts. Long unbroken ASCII runs (base64, minified blobs)\n * pack slightly denser too. This scans a bounded sample and returns a\n * multiplier in [1, 2.5] \u2014 always \u2265 1, so it can only push the estimate UP,\n * never down. Used only by the send-time overflow guard, never for display.\n */\nfunction textDensityMultiplier(messages: readonly Message[]): number {\n let sampled = 0;\n let nonAscii = 0;\n let maxRun = 0;\n const consider = (s: string): void => {\n const n = Math.min(s.length, DENSITY_SAMPLE_PER_BLOCK);\n let run = 0;\n for (let i = 0; i < n; i++) {\n const c = s.charCodeAt(i);\n if (c > 127) nonAscii++;\n if (c === 32 || c === 9 || c === 10 || c === 13) {\n if (run > maxRun) maxRun = run;\n run = 0;\n } else {\n run++;\n }\n }\n if (run > maxRun) maxRun = run;\n sampled += n;\n };\n\n for (const m of messages) {\n if (typeof m.content === 'string') {\n consider(m.content);\n } else if (Array.isArray(m.content)) {\n for (const b of m.content) {\n if (b.type === 'text') consider(b.text);\n else if (b.type === 'tool_result' && typeof b.content === 'string') consider(b.content);\n else if (b.type === 'thinking') consider(b.thinking);\n }\n }\n if (sampled >= DENSITY_SAMPLE_TOTAL_CAP) break;\n }\n\n if (sampled === 0) return 1;\n const nonAsciiRatio = nonAscii / sampled;\n // 3.5 chars/token at 0% non-ASCII \u2192 1.5 at 100% (heavy CJK).\n let charsPerToken = 3.5 - 2.0 * nonAsciiRatio;\n // A very long unbroken ASCII run (base64/minified) packs a little denser.\n if (maxRun > 2_000 && nonAsciiRatio < 0.1) charsPerToken = Math.min(charsPerToken, 3.0);\n const multiplier = 3.5 / Math.max(1.4, charsPerToken);\n return Math.min(2.5, Math.max(1, multiplier));\n}\n\n/**\n * Never-undercount upper bound for the request token total, for the **send\n * guard** only. Takes the flat estimate and scales it up by the greater of the\n * content-density multiplier and the calibration ceiling, so the guarded value\n * satisfies `real \u2264 upperBound`. The context bar and `/context` keep using the\n * calibrated estimate \u2014 this deliberately over-counts, which is only ever safe\n * for the \"must this be trimmed before sending?\" decision.\n */\nexport function estimateRequestTokensUpperBound(\n messages: unknown,\n systemPrompt: unknown,\n tools: { name: string; description?: string | undefined; inputSchema: unknown }[],\n calibrationKey: string = CALIBRATION_GLOBAL_KEY,\n): RequestTokenBreakdown {\n const base = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);\n const density = Array.isArray(messages)\n ? textDensityMultiplier(messages as readonly Message[])\n : 1;\n const cal = calState(calibrationKey);\n const calCeiling =\n cal.count >= MIN_SAMPLES_FOR_CALIBRATION ? Math.min(1.5, Math.max(1, cal.ratio)) : 1;\n const mult = Math.max(density, calCeiling);\n if (mult <= 1) return base;\n return {\n messages: Math.ceil(base.messages * mult),\n systemPrompt: Math.ceil(base.systemPrompt * mult),\n tools: Math.ceil(base.tools * mult),\n total: Math.ceil(base.total * mult),\n };\n}\n\n/** Look up the fallback chars/token ratio for a calibration key (e.g. \"provider/model\"). */\nfunction getModelFamilyRatio(calibrationKey: string): number | null {\n const lower = calibrationKey.toLowerCase();\n for (const [family, ratio] of Object.entries(MODEL_FAMILY_RATIO)) {\n if (lower.includes(family)) return ratio / 3.5; // MODEL_FAMILY_RATIO is chars/token, we need multiplier\n }\n return null;\n}\n\n/**\n * Resets calibration state. Primarily for tests that run in the same\n * process and need a clean slate between suites. With no argument it clears\n * every bucket (including the global one); pass a key to reset just that bucket.\n */\nexport function resetCalibration(calibrationKey?: string): void {\n if (calibrationKey === undefined) {\n _cals.clear();\n return;\n }\n _cals.delete(calibrationKey);\n}\n", "import { buildLiveNextStepsGateBlock } from '../core/agent-response.js';\nimport {\n SYSTEM_BLOCK_SOURCE,\n type SystemBlockSource,\n} from '../core/system-prompt-builder.js';\nimport type { Context } from '../core/context.js';\nimport type { TextBlock } from '../types/blocks.js';\nimport type { Tool } from '../types/tool.js';\nimport { buildCompletedWorkLedgerBlock } from './context-evidence.js';\nimport {\n estimateTextTokens,\n estimateToolDefTokens,\n estimateToolInputTokens,\n estimateToolResultTokens,\n} from './token-estimate.js';\n\n/**\n * Real, per-category token accounting for the live context window \u2014 the data\n * behind an honest `/context` display. Every number is measured from the actual\n * assembled inputs (`ctx.systemPrompt`, `ctx.tools`, `ctx.messages`) using the\n * same 3.5-chars/token estimator the compactor and live bar use, so the total\n * here reconciles with the context-fill bar. It replaces the hardcoded fake\n * percentages the TUI dashboard used to show.\n */\nexport interface ContextBreakdown {\n system: {\n total: number;\n /** Tokens attributed to each system-prompt section (see SystemBlockSource). */\n bySource: Record<SystemBlockSource | 'other', number>;\n };\n tools: {\n total: number;\n builtin: number;\n mcp: number;\n /** Number of tool definitions in the request. */\n count: number;\n /** MCP tool tokens grouped by originating server. */\n mcpByServer: Record<string, number>;\n };\n history: {\n total: number;\n /** User/assistant text + tool_use inputs + thinking. */\n text: number;\n /** tool_result output content. */\n toolResults: number;\n messageCount: number;\n };\n volatile: {\n /** Completed-work ledger, appended to `system` at request time. */\n ledger: number;\n /** Next-steps gate, appended to `system` at request time. */\n nextsteps: number;\n total: number;\n };\n /** system.total + tools.total + history.total + volatile.total. */\n total: number;\n effectiveMaxContext: number;\n /** total / effectiveMaxContext \u2014 may exceed 1 before compaction fires. */\n usedPct: number;\n /**\n * Non-fatal build warnings encountered during breakdown computation.\n * Empty when everything succeeded; populated when volatile blocks\n * (ledger, nextsteps) threw during construction and were silently omitted.\n */\n warnings: string[];\n}\n\nconst SYSTEM_BLOCK_SOURCES: readonly (SystemBlockSource | 'other')[] = [\n 'identity',\n 'tool-usage',\n 'environment',\n 'skills',\n 'mode',\n 'plan',\n 'leader-after-task',\n 'contributor',\n 'ledger',\n 'nextsteps',\n 'other',\n];\n\nfunction emptyBySource(): Record<SystemBlockSource | 'other', number> {\n const out = {} as Record<SystemBlockSource | 'other', number>;\n for (const key of SYSTEM_BLOCK_SOURCES) out[key] = 0;\n return out;\n}\n\n/** An MCP-proxied tool, identified by capability first then the `mcp__` prefix. */\nfunction isMcpTool(tool: Tool): boolean {\n return (tool.capabilities?.includes('mcp.proxy') ?? false) || tool.name.startsWith('mcp__');\n}\n\n/** Server segment of a `mcp__<server>__<tool>` name, or a generic bucket. */\nfunction mcpServerOf(tool: Tool): string {\n const parts = tool.name.split('__');\n return parts.length >= 3 && parts[0] === 'mcp' ? (parts[1] ?? 'mcp') : 'mcp';\n}\n\nfunction safeBuild(\n fn: () => TextBlock | undefined,\n warn: (e: unknown) => void = () => {},\n): TextBlock | undefined {\n try {\n return fn();\n } catch (e) {\n warn(e);\n return undefined;\n }\n}\n\n/**\n * Mirror the denominator the agent loop (`currentMaxContext`) and the\n * auto-compaction middleware use: an explicit `effectiveMaxContext` override\n * wins, then the provider window, then a safe default. Inline-replicated here\n * to keep this module free of an `execution/`/`core/` runtime dependency for\n * the denominator (the builders it already imports are the only exception).\n */\nfunction resolveEffectiveMaxContext(ctx: Context): number {\n const metaLimit = ctx.meta?.['effectiveMaxContext'];\n const providerMax = ctx.provider.capabilities.maxContext;\n return typeof metaLimit === 'number' && metaLimit > 0\n ? metaLimit\n : typeof providerMax === 'number' && providerMax > 0\n ? providerMax\n : 200_000;\n}\n\n/**\n * Compute the real per-category token breakdown of a live context. Safe to call\n * interactively (e.g. from `/context`): it walks the full message array once, so\n * it is O(messages\u00B7blocks), not the O(1) cached path the per-turn bar uses.\n */\nexport function getContextBreakdown(ctx: Context): ContextBreakdown {\n // --- System prompt: attributed per section via the builder's WeakMap tag ---\n const bySource = emptyBySource();\n let systemTotal = 0;\n for (const block of ctx.systemPrompt) {\n const tokens = estimateTextTokens(block.text);\n systemTotal += tokens;\n bySource[SYSTEM_BLOCK_SOURCE.get(block) ?? 'other'] += tokens;\n }\n\n // --- Tool definitions: builtin vs MCP (grouped by server) ---\n let toolsBuiltin = 0;\n let toolsMcp = 0;\n const mcpByServer: Record<string, number> = {};\n for (const tool of ctx.tools) {\n const tokens = estimateToolDefTokens(tool);\n if (isMcpTool(tool)) {\n toolsMcp += tokens;\n const server = mcpServerOf(tool);\n mcpByServer[server] = (mcpByServer[server] ?? 0) + tokens;\n } else {\n toolsBuiltin += tokens;\n }\n }\n\n // --- Conversation history: text/tool_use/thinking vs tool_result output ---\n let histText = 0;\n let histToolResults = 0;\n for (const msg of ctx.messages) {\n if (typeof msg.content === 'string') {\n histText += estimateTextTokens(msg.content);\n continue;\n }\n for (const b of msg.content) {\n switch (b.type) {\n case 'text':\n histText += estimateTextTokens(b.text);\n break;\n case 'tool_use':\n histText += estimateToolInputTokens(b.input);\n break;\n case 'tool_result':\n histToolResults += estimateToolResultTokens(b.content);\n break;\n case 'thinking':\n histText += estimateTextTokens(b.thinking);\n break;\n default:\n histText += estimateTextTokens(JSON.stringify(b));\n }\n }\n }\n\n // --- Volatile per-turn blocks: re-derived (they live in the request, not\n // in ctx.systemPrompt) and tagged by construction. ---\n const warnings: string[] = [];\n const warn = (e: unknown) => {\n const msg = e instanceof Error ? e.message : String(e);\n warnings.push(msg);\n };\n const ledgerBlock = safeBuild(() => buildCompletedWorkLedgerBlock(ctx), warn);\n const nextstepsBlock = safeBuild(() => buildLiveNextStepsGateBlock(ctx), warn);\n const ledger = ledgerBlock ? estimateTextTokens(ledgerBlock.text) : 0;\n const nextsteps = nextstepsBlock ? estimateTextTokens(nextstepsBlock.text) : 0;\n\n const toolsTotal = toolsBuiltin + toolsMcp;\n const historyTotal = histText + histToolResults;\n const volatileTotal = ledger + nextsteps;\n const total = systemTotal + toolsTotal + historyTotal + volatileTotal;\n const effectiveMaxContext = resolveEffectiveMaxContext(ctx);\n\n return {\n system: { total: systemTotal, bySource },\n tools: {\n total: toolsTotal,\n builtin: toolsBuiltin,\n mcp: toolsMcp,\n count: ctx.tools.length,\n mcpByServer,\n },\n history: {\n total: historyTotal,\n text: histText,\n toolResults: histToolResults,\n messageCount: ctx.messages.length,\n },\n volatile: { ledger, nextsteps, total: volatileTotal },\n total,\n effectiveMaxContext,\n usedPct: effectiveMaxContext > 0 ? total / effectiveMaxContext : 0,\n warnings,\n };\n}\n", "/**\n * Deep merge utility \u2014 safely merges nested objects with configurable\n * conflict resolution, array merging, and prototype-pollution guarding.\n *\n * Used by:\n * - config-loader (config layer merging with primitive-array concatenation)\n * - secret-vault (config patching)\n * - json-path (json_merge tool with prefer-base / prefer-patch semantics)\n *\n * @module utils/deep-merge\n */\n\n// ---------------------------------------------------------------------------\n// Prototype-pollution guard \u2014 shared set of forbidden __proto__ keys\n// ---------------------------------------------------------------------------\n\nexport const FORBIDDEN_PROTO_KEYS = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n '__defineGetter__',\n '__defineSetter__',\n '__lookupGetter__',\n '__lookupSetter__',\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** True when every element is a primitive or null (no nested objects/arrays). */\nexport function isPrimitiveArray(a: unknown[]): boolean {\n return a.every((v) => v === null || (typeof v !== 'object' && typeof v !== 'function'));\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface DeepMergeOptions {\n /**\n * Which side wins on collision for scalars and arrays.\n *\n * - `'prefer-patch'` (default): patch value replaces base value.\n * - `'prefer-base'`: base value is kept, patch value is ignored.\n */\n conflictResolution?: 'prefer-base' | 'prefer-patch';\n\n /**\n * How to handle array values.\n *\n * - `'replace'` (default): patch array replaces base array entirely.\n * - `'concat-primitives'`: when both values are primitive arrays,\n * they are concatenated and deduped (via Set). Non-primitive\n * arrays still replace the base wholesale.\n */\n arrayMode?: 'replace' | 'concat-primitives';\n\n /**\n * Skip prototype-pollution keys (`__proto__`, `constructor`, etc.).\n * Enabled by default. Only disable when you control both inputs\n * and the keyset (e.g. when merging trusted JSON schemas).\n */\n protectProto?: boolean;\n\n /**\n * Optional callback fired when a non-primitive (object) array is\n * replaced wholesale (only relevant with `arrayMode: 'concat-primitives'`).\n * Receives the key name, existing array length, and patch array length.\n * Used by config-loader for debug logging.\n */\n onNonPrimitiveArrayReplace?: (\n key: string,\n existingLen: number,\n patchLen: number,\n ) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively merge `patch` into `base`, returning a new object.\n *\n * - Nested plain objects are merged recursively.\n * - Arrays are handled per `options.arrayMode`.\n * - Scalar collisions are resolved per `options.conflictResolution`.\n * - `null` and non-object values in `patch` replace the base value\n * (unless `conflictResolution` is `'prefer-base'`).\n * - Keys in `base` that are absent from `patch` are preserved.\n * - `FORBIDDEN_PROTO_KEYS` are skipped in the patch (unless\n * `options.protectProto` is set to `false`).\n *\n * The function is generic over `T extends Record<string, unknown>` for\n * callers that pass typed config objects, but the runtime signature\n * also accepts `unknown` inputs (used by the json-path plugin).\n */\nexport function deepMerge<T extends Record<string, unknown>>(\n base: T,\n patch: Record<string, unknown>,\n options?: DeepMergeOptions,\n): T;\n\nexport function deepMerge(\n base: unknown,\n patch: unknown,\n options?: DeepMergeOptions,\n): unknown;\n\nexport function deepMerge(\n base: unknown,\n patch: unknown,\n options: DeepMergeOptions = {},\n): unknown {\n const {\n conflictResolution = 'prefer-patch',\n arrayMode = 'replace',\n protectProto = true,\n onNonPrimitiveArrayReplace,\n } = options;\n\n // Non-object / null handling \u2014 delegate to conflict resolution.\n if (typeof base !== 'object' || base === null) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n if (typeof patch !== 'object' || patch === null) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // Arrays \u2014 handled *before* the object merge so array-of-objects\n // aren't accidentally treated as plain records.\n if (Array.isArray(base) && Array.isArray(patch)) {\n if (\n arrayMode === 'concat-primitives' &&\n isPrimitiveArray(base) &&\n isPrimitiveArray(patch)\n ) {\n return [...new Set([...base, ...patch])];\n }\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // If only one side is an array, treat as scalar collision.\n if (Array.isArray(base) || Array.isArray(patch)) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // Plain object merge.\n const baseObj = base as Record<string, unknown>;\n const patchObj = patch as Record<string, unknown>;\n const out: Record<string, unknown> = { ...baseObj };\n\n for (const [k, v] of Object.entries(patchObj)) {\n if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;\n\n const existing = out[k];\n if (\n v !== null &&\n typeof v === 'object' &&\n !Array.isArray(v) &&\n existing !== null &&\n typeof existing === 'object' &&\n !Array.isArray(existing)\n ) {\n // Recursive merge for nested plain objects.\n out[k] = deepMerge(existing, v, options);\n } else if (Array.isArray(v) && Array.isArray(existing)) {\n // Delegate to top-level array handling so arrayMode\n // (e.g. 'concat-primitives') applies to nested arrays too.\n // Fire debug hook when a non-primitive array replaces an existing\n // array (for non-primitive arrays, concat-primitives is a no-op and\n // the result is always a wholesale replacement).\n if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {\n onNonPrimitiveArrayReplace(k, existing.length, v.length);\n }\n out[k] = deepMerge(existing, v, options);\n } else if (v !== undefined) {\n // Fire debug hook when a non-primitive (object) array replaces an\n // existing value in concat-primitives mode.\n if (\n onNonPrimitiveArrayReplace &&\n Array.isArray(v) &&\n !isPrimitiveArray(v)\n ) {\n const existingLen = Array.isArray(existing) ? existing.length : 0;\n onNonPrimitiveArrayReplace(k, existingLen, v.length);\n }\n out[k] = v;\n }\n // When v === undefined, leave the existing value untouched\n // (this matches config-loader's behaviour: undefined in patch\n // means \"don't change this key\").\n }\n\n return out;\n}\n", "/**\n * Myers diff with unified-format output. No external dependencies.\n * Operates on arrays of lines (newline-terminated or stripped).\n */\n\ninterface Edit {\n op: 'equal' | 'insert' | 'delete';\n a: number;\n b: number;\n line: string;\n}\n\nfunction myersDiff(a: string[], b: string[]): Edit[] {\n const N = a.length;\n const M = b.length;\n const max = N + M;\n if (max === 0) return [];\n\n const v = new Map<number, number>();\n v.set(1, 0);\n const trace: Map<number, number>[] = [];\n\n for (let d = 0; d <= max; d++) {\n const snapshot = new Map(v);\n trace.push(snapshot);\n for (let k = -d; k <= d; k += 2) {\n const left = v.get(k - 1) ?? -1;\n const right = v.get(k + 1) ?? -1;\n let x: number;\n if (k === -d || (k !== d && left < right)) {\n x = right;\n } else {\n x = left + 1;\n }\n let y = x - k;\n while (x < N && y < M && a[x] === b[y]) {\n x++;\n y++;\n }\n v.set(k, x);\n if (x >= N && y >= M) {\n return backtrack(trace, a, b, N, M, d);\n }\n }\n }\n return [];\n}\n\nfunction backtrack(\n trace: Map<number, number>[],\n a: string[],\n b: string[],\n N: number,\n M: number,\n finalD: number,\n): Edit[] {\n const edits: Edit[] = [];\n let x = N;\n let y = M;\n for (let d = finalD; d > 0; d--) {\n const v = trace[d];\n if (!v) break;\n const k = x - y;\n const left = v.get(k - 1) ?? -1;\n const right = v.get(k + 1) ?? -1;\n let prevK: number;\n if (k === -d || (k !== d && left < right)) {\n prevK = k + 1;\n } else {\n prevK = k - 1;\n }\n const prevX = v.get(prevK) ?? 0;\n const prevY = prevX - prevK;\n while (x > prevX && y > prevY) {\n edits.push({ op: 'equal', a: x - 1, b: y - 1, line: a[x - 1] ?? '' });\n x--;\n y--;\n }\n if (d > 0) {\n if (x === prevX) {\n edits.push({ op: 'insert', a: x, b: y - 1, line: b[y - 1] ?? '' });\n } else {\n edits.push({ op: 'delete', a: x - 1, b: y, line: a[x - 1] ?? '' });\n }\n x = prevX;\n y = prevY;\n }\n }\n while (x > 0 && y > 0) {\n edits.push({ op: 'equal', a: x - 1, b: y - 1, line: a[x - 1] ?? '' });\n x--;\n y--;\n }\n return edits.reverse();\n}\n\nexport interface UnifiedDiffOptions {\n context?: number | undefined;\n fromFile?: string | undefined;\n toFile?: string | undefined;\n}\n\nexport function unifiedDiff(\n oldText: string,\n newText: string,\n opts: UnifiedDiffOptions = {},\n): string {\n const context = opts.context ?? 3;\n const a = oldText.split('\\n');\n const b = newText.split('\\n');\n // Handle trailing newline: split adds an empty string we don't want to diff\n if (a[a.length - 1] === '') a.pop();\n if (b[b.length - 1] === '') b.pop();\n const edits = myersDiff(a, b);\n if (edits.every((e) => e.op === 'equal')) return '';\n\n const hunks: { aStart: number; bStart: number; lines: string[] }[] = [];\n let i = 0;\n while (i < edits.length) {\n while (i < edits.length && edits[i]?.op === 'equal') i++;\n if (i >= edits.length) break;\n const hunkStart = Math.max(0, i - context);\n const lines: string[] = [];\n let aStart = (edits[hunkStart]?.a ?? 0) + 1;\n let bStart = (edits[hunkStart]?.b ?? 0) + 1;\n let aCount = 0;\n let bCount = 0;\n let cursor = hunkStart;\n let trailing = 0;\n while (cursor < edits.length) {\n const e = edits[cursor];\n if (!e) break;\n if (e.op === 'equal') {\n trailing++;\n if (trailing > context * 2) break;\n } else {\n trailing = 0;\n }\n if (e.op === 'equal') {\n lines.push(` ${e.line}`);\n aCount++;\n bCount++;\n } else if (e.op === 'delete') {\n lines.push(`-${e.line}`);\n aCount++;\n } else {\n lines.push(`+${e.line}`);\n bCount++;\n }\n cursor++;\n }\n // Trim trailing context lines beyond `context`\n while (lines.length > 0 && lines[lines.length - 1]?.startsWith(' ') && trailing > context) {\n lines.pop();\n aCount--;\n bCount--;\n trailing--;\n }\n if (aCount === 0) aStart = 0;\n if (bCount === 0) bStart = 0;\n hunks.push({ aStart, bStart, lines });\n i = cursor;\n }\n if (hunks.length === 0) return '';\n\n let out = '';\n out += `--- ${opts.fromFile ?? 'a'}\\n`;\n out += `+++ ${opts.toFile ?? 'b'}\\n`;\n for (const h of hunks) {\n let aCount = 0;\n let bCount = 0;\n for (const l of h.lines) {\n if (l.startsWith(' ')) {\n aCount++;\n bCount++;\n } else if (l.startsWith('-')) aCount++;\n else if (l.startsWith('+')) bCount++;\n }\n out += `@@ -${h.aStart},${aCount} +${h.bStart},${bCount} @@\\n`;\n out += `${h.lines.join('\\n')}\\n`;\n }\n return out;\n}\n", "import { expectDefined } from './expect-defined.js';\n/**\n * Glob pattern \u2192 concrete file path expansion.\n *\n * Supports: *, **, ?, [...]\n * Does NOT support brace expansion {a,b}.\n *\n * Returns the input as-is if it contains no glob metacharacters.\n * On Windows, both / and \\ are accepted as path separators.\n */\n\nimport * as fsp from 'node:fs/promises';\nimport { isAbsolute, resolve } from 'node:path';\nconst GLOB_CHARS = new Set(['*', '?', '[']);\nconst IS_WINDOWS = process.platform === 'win32';\nconst SEP = IS_WINDOWS ? '\\\\' : '/';\n\nfunction isGlob(p: string): boolean {\n for (const c of p) {\n if (GLOB_CHARS.has(c)) return true;\n }\n return false;\n}\n\nfunction globToRegex(pat: string): RegExp {\n let i = 0;\n let re = '^';\n while (i < pat.length) {\n const c = expectDefined(pat[i]);\n if (c === '*') {\n if (pat[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pat[i] === '/') i++;\n } else {\n re += '[^/\\\\\\\\]*';\n i++;\n }\n } else if (c === '?') {\n re += '[^/\\\\\\\\]';\n i++;\n } else if (c === '[') {\n let cls = '[';\n i++;\n if (pat[i] === '!' || pat[i] === '^') {\n cls += '^';\n i++;\n }\n while (i < pat.length && pat[i] !== ']') {\n const ch = pat[i] ?? '';\n if (ch === '\\\\') cls += '\\\\\\\\';\n else if (ch === ']' || ch === '^') cls += `\\\\${ch}`;\n else cls += ch;\n i++;\n }\n cls += ']';\n re += cls;\n i++;\n } else {\n re += c.replace(/[.+^${}()|\\\\]/g, '\\\\$&');\n i++;\n }\n }\n return new RegExp(re + '$');\n}\n\nfunction baseDir(pat: string): string {\n // Deepest literal directory prefix: cut at the last separator BEFORE the\n // first glob char. Scanning from the end instead finds separators inside\n // glob segments \u2014 '**/*.ts' would yield base '**' on POSIX (native sep '/').\n let firstGlob = pat.length;\n for (let i = 0; i < pat.length; i++) {\n if (GLOB_CHARS.has(expectDefined(pat[i]))) {\n firstGlob = i;\n break;\n }\n }\n const cut = Math.max(\n pat.lastIndexOf(SEP, firstGlob - 1),\n pat.lastIndexOf('/', firstGlob - 1),\n );\n return cut < 0 ? '.' : pat.slice(0, cut);\n}\n\n/**\n * Resolve `pattern` to the set of concrete file paths it matches.\n * Literal paths (no glob chars) are returned as-is.\n *\n * @example\n * await expandGlob('src/**\\/*.ts') // \u2192 ['src/a.ts', 'src/b/c.ts', ...]\n * await expandGlob('foo.txt') // \u2192 ['foo.txt']\n */\nexport async function expandGlob(pattern: string): Promise<string[]> {\n if (!isGlob(pattern)) return [pattern];\n\n const results = new Set<string>();\n const abs = isAbsolute(pattern);\n const base = abs ? baseDir(pattern) : baseDir(pattern);\n const relPat = base === '.' ? pattern : pattern.slice(base.length + 1);\n\n async function walk(dir: string, pat: string): Promise<void> {\n let entries: string[];\n try {\n entries = await fsp.readdir(dir);\n } catch {\n return;\n }\n\n const firstGlob = pat.search(/[*?[[]/);\n\n if (firstGlob < 0) {\n const re = globToRegex(pat);\n for (const e of entries) {\n if (re.test(e)) {\n const full = `${dir}${SEP}${e}`;\n results.add(abs ? resolve(full) : full);\n }\n }\n return;\n }\n\n const before = pat.slice(0, firstGlob);\n const rest = pat.slice(firstGlob);\n\n if (before.endsWith('**')) {\n // Match at current dir then recurse into subdirs\n await walk(dir, rest);\n for (const e of entries) {\n const full = `${dir}${SEP}${e}`;\n try {\n const stat = await fsp.stat(full);\n if (stat.isDirectory()) await walk(full, rest);\n } catch {\n /* skip inaccessible */\n }\n }\n } else if (before === '') {\n // Pattern starts with a glob char \u2014 match files in current dir only\n const re = globToRegex(rest);\n for (const e of entries) {\n if (re.test(e)) {\n const full = `${dir}${SEP}${e}`;\n results.add(abs ? resolve(full) : full);\n }\n }\n } else {\n // Literal segment(s) before the glob \u2014 descend into matching subdir\n const seg = before.replace(/[*?[\\]]/g, '').replace(/\\/$/, '');\n if (entries.includes(seg)) {\n const full = `${dir}${SEP}${seg}`;\n try {\n const stat = await fsp.stat(full);\n if (stat.isDirectory()) await walk(full, rest);\n } catch {\n /* skip */\n }\n }\n }\n }\n\n await walk(base === '.' ? '.' : base, relPat);\n return [...results];\n}\n", "import { expectDefined } from './expect-defined.js';\n/**\n * Minimal glob matcher for trust patterns.\n * Supports: *, **, ?, character classes [abc], [a-z], negation [!...] or [^...].\n *\n * Compiled regexes are cached so repeated calls with the same pattern\n * avoid recompilation overhead.\n */\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.+^${}()|\\\\]/g, '\\\\$&');\n}\n\n// Module-level cache to avoid recompiling the same pattern on every call.\n// LRU-ish eviction keeps unbounded growth in check for long-running processes.\nconst COMPILED_GLOB_CACHE = new Map<string, RegExp>();\nconst CACHE_MAX_SIZE = 2000;\n\n// Matches nothing \u2014 `[^\\s\\S]` can never be satisfied. Used as the cached\n// result for patterns that fail to compile (e.g. an over-long auto-trusted\n// command) so one bad trust entry degrades to \"no match\" instead of throwing.\nconst NEVER_MATCH = /[^\\s\\S]/;\n\nfunction getCachedGlob(pattern: string): RegExp {\n const cached = COMPILED_GLOB_CACHE.get(pattern);\n if (cached) return cached;\n if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {\n // Evict oldest 25% when at capacity\n const keys = [...COMPILED_GLOB_CACHE.keys()];\n for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {\n COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));\n }\n }\n let re: RegExp;\n try {\n re = compileGlob(pattern);\n } catch {\n // A pathological trust pattern (over MAX_GLOB_PATTERN_LEN \u2014 e.g. a long\n // one-liner auto-trusted in YOLO/Auto mode) must NOT throw out of every\n // subsequent permission check and break unrelated commands like `true`\n // or `ls` (#20). Cache a never-matching regex so the bad entry is inert.\n re = NEVER_MATCH;\n }\n COMPILED_GLOB_CACHE.set(pattern, re);\n return re;\n}\n\n// Cap glob pattern length to prevent excessively long compiled regexes.\nconst MAX_GLOB_PATTERN_LEN = 1024;\n\nexport function compileGlob(pattern: string): RegExp {\n if (pattern.length > MAX_GLOB_PATTERN_LEN) {\n throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);\n }\n let i = 0;\n let re = '^';\n while (i < pattern.length) {\n const c = pattern[i];\n if (c === '*') {\n if (pattern[i + 1] === '*') {\n // ** matches any number of chars including /\n re += '.*';\n i += 2;\n // Skip trailing slash so '**/x' matches 'x'\n if (pattern[i] === '/') i++;\n } else {\n // single * matches any chars except /\n re += '[^/]*';\n i++;\n }\n } else if (c === '?') {\n re += '[^/]';\n i++;\n } else if (c === '[') {\n let cls = '[';\n i++;\n if (pattern[i] === '!' || pattern[i] === '^') {\n cls += '^';\n i++;\n }\n while (i < pattern.length && pattern[i] !== ']') {\n const ch = pattern[i] ?? '';\n // Inside a regex class, only `]`, `\\`, and `^`/`-` at boundaries need\n // escaping. We've already consumed the leading `^`; the rest are\n // literal. Escape `\\` defensively and pass the rest through verbatim\n // so ranges like `a-z` continue to work.\n if (ch === '\\\\') {\n cls += '\\\\\\\\';\n } else if (ch === ']' || ch === '^') {\n cls += `\\\\${ch}`;\n } else {\n cls += ch;\n }\n i++;\n }\n cls += ']';\n re += cls;\n i++; // skip closing ]\n } else {\n re += escapeRegex(c ?? '');\n i++;\n }\n }\n re += '$';\n return new RegExp(re);\n}\n\nexport function matchGlob(pattern: string, input: string): boolean {\n return getCachedGlob(pattern).test(input);\n}\n\nexport function matchAny(patterns: string[], input: string): boolean {\n return patterns.some((p) => matchGlob(p, input));\n}\n", "import type { ContentBlock, ImageBlock } from '../types/blocks.js';\n\n/**\n * Wire shape for one image attached to a WebUI `user_message`. `data` may be\n * a bare base64 string or a full `data:` URL (the client normally strips the\n * prefix, but legacy senders shipped the whole URL).\n */\nexport interface IncomingImagePayload {\n data: string;\n mediaType?: string | undefined;\n /** Original filename, when the image came from a file picker or drop. */\n name?: string | undefined;\n}\n\nexport const MAX_INCOMING_IMAGES = 8;\n\n/**\n * Decoded-byte cap per image. The WebUI client downscales before sending, so\n * anything larger than this is either a bypassed client or an abuse attempt.\n * Kept under the servers' WS maxPayload once base64 overhead (~4/3) and the\n * surrounding JSON envelope are added.\n */\nexport const MAX_INCOMING_IMAGE_BYTES = 8 * 1024 * 1024;\n\n/** Media types every supported vision wire accepts (Anthropic passthrough,\n * OpenAI data-URLs, Gemini inlineData). */\nconst ALLOWED_IMAGE_MEDIA_TYPES = new Set<string>([\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/gif',\n]);\n\n/** Validation failure on user-supplied image payloads. The message is safe to\n * echo back to the client verbatim. */\nexport class IncomingImageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IncomingImageError';\n }\n}\n\nconst DATA_URL_RE = /^data:([a-z0-9.+-]+\\/[a-z0-9.+-]+)?(?:;[a-z0-9-]+=[^;,]*)*(;base64)?,/i;\n\nfunction splitDataUrl(data: string): { base64: string; mediaType?: string | undefined } {\n const match = DATA_URL_RE.exec(data);\n if (!match) return { base64: data.trim() };\n return {\n base64: data.slice(match[0].length).trim(),\n mediaType: match[1]?.toLowerCase(),\n };\n}\n\n/**\n * Validate and normalize the `images` field of a `user_message` payload into\n * canonical {@link ImageBlock}s. Accepts the legacy single `imageBase64`\n * field (a data-URL) as a trailing entry so old clients keep working.\n *\n * Throws {@link IncomingImageError} on count/size/media-type violations.\n */\nexport function parseIncomingImages(\n images?: readonly IncomingImagePayload[] | undefined,\n legacyImageBase64?: string | undefined,\n): ImageBlock[] {\n const raw: IncomingImagePayload[] = [...(images ?? [])];\n if (legacyImageBase64) raw.push({ data: legacyImageBase64 });\n if (raw.length === 0) return [];\n if (raw.length > MAX_INCOMING_IMAGES) {\n throw new IncomingImageError(\n `Too many images: ${raw.length} (max ${MAX_INCOMING_IMAGES} per message).`,\n );\n }\n\n return raw.map((img, i) => {\n const { base64, mediaType: fromUrl } = splitDataUrl(img.data ?? '');\n const mediaType = (img.mediaType ?? fromUrl ?? 'image/png').toLowerCase();\n if (!ALLOWED_IMAGE_MEDIA_TYPES.has(mediaType)) {\n throw new IncomingImageError(\n `Image ${i + 1}: unsupported media type \"${mediaType}\" (allowed: ${[...ALLOWED_IMAGE_MEDIA_TYPES].join(', ')}).`,\n );\n }\n if (!base64) {\n throw new IncomingImageError(`Image ${i + 1}: empty image data.`);\n }\n // Base64 alphabet check \u2014 cheap linear scan that rejects raw binary or\n // JSON smuggled into the field before it reaches a provider wire.\n if (!/^[A-Za-z0-9+/]+={0,2}$/.test(base64)) {\n throw new IncomingImageError(`Image ${i + 1}: data is not valid base64.`);\n }\n const bytes = Math.floor((base64.length * 3) / 4);\n if (bytes > MAX_INCOMING_IMAGE_BYTES) {\n throw new IncomingImageError(\n `Image ${i + 1}: ${(bytes / (1024 * 1024)).toFixed(1)} MB exceeds the ${MAX_INCOMING_IMAGE_BYTES / (1024 * 1024)} MB limit.`,\n );\n }\n return {\n type: 'image',\n source: { type: 'base64', media_type: mediaType, data: base64 },\n } satisfies ImageBlock;\n });\n}\n\n/**\n * Assemble the agent input for a user message that carries images: image\n * blocks first (the order vision providers prefer), then the text block.\n */\nexport function buildUserContentBlocks(\n text: string,\n images: readonly ImageBlock[],\n): ContentBlock[] {\n const blocks: ContentBlock[] = [...images];\n if (text) blocks.push({ type: 'text', text });\n return blocks;\n}\n", "/**\n * Shared IP-address guards for SSRF protection.\n *\n * Exported so `fetch.ts` (tools), `web-search/index.ts` (plugins), and any\n * other package that needs to validate IPs can all consume the same logic.\n * Any future additions (e.g. extra CIDR blocks) need only be made here.\n */\n\nimport * as dns from 'node:dns/promises';\nimport * as net from 'node:net';\n\n/**\n * True if `addr` is in a private / loopback / link-local / reserved / CGNAT /\n * multicast range. `net.isIP` is called by the caller first so `addr` is\n * guaranteed to be a canonical dotted-quad at this point.\n */\nexport function isPrivateIPv4(addr: string): boolean {\n const parts = addr.split('.').map((p) => Number.parseInt(p, 10));\n if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) {\n return true; // defensive: malformed \u2192 block\n }\n const [a, b, c] = parts as [number, number, number, number];\n if (a === 0) return true; // 0.0.0.0/8 \"this host\"\n if (a === 10) return true; // 10.0.0.0/8 private\n if (a === 127) return true; // 127.0.0.0/8 loopback\n if (a === 169 && b === 254) return true; // 169.254.0.0/16 link-local + AWS/GCE/Azure IMDS\n if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12 private\n if (a === 192 && b === 168) return true; // 192.168.0.0/16 private\n if (a === 192 && b === 0 && c === 0) return true; // 192.0.0.0/24 reserved\n if (a === 100 && b >= 64 && b <= 127) return true; // 100.64.0.0/10 CGNAT\n if (a >= 224) return true; // 224.0.0.0/4 multicast + 240.0.0.0/4 reserved\n return false;\n}\n\n/**\n * True if `raw` (an IPv6 literal, already lowercased) is loopback / unique-local /\n * link-local / unspecified / IPv4-mapped-private.\n */\nexport function isPrivateIPv6(raw: string): boolean {\n const lower = raw.toLowerCase();\n if (lower === '::' || lower === '::1') return true; // loopback / unspecified\n\n // Expand to 8-group canonical form so range checks don't have to handle every\n // shorthand notation. Returns null on malformed input \u2014 we conservatively\n // block in that case rather than leaking.\n const groups = expandIPv6(lower);\n if (!groups) return true;\n\n // IPv4-mapped: ::ffff:0:0/96 \u2192 groups[0..5] all 0, groups[6..7] hold the\n // embedded IPv4 as two 16-bit words. Node URL normalises the dotted form to\n // this representation (e.g. ::ffff:127.0.0.1 \u2192 ::ffff:7f00:1).\n if (\n groups[0] === 0 &&\n groups[1] === 0 &&\n groups[2] === 0 &&\n groups[3] === 0 &&\n groups[4] === 0 &&\n groups[5] === 0xffff\n ) {\n const a = (groups[6] ?? 0) >> 8;\n const b = (groups[6] ?? 0) & 0xff;\n const c = (groups[7] ?? 0) >> 8;\n const d = (groups[7] ?? 0) & 0xff;\n return isPrivateIPv4(`${a}.${b}.${c}.${d}`);\n }\n\n const high = groups[0] ?? 0;\n if ((high & 0xfe00) === 0xfc00) return true; // fc00::/7 unique local (fc..fd)\n if ((high & 0xffc0) === 0xfe80) return true; // fe80::/10 link-local\n if ((high & 0xff00) === 0xff00) return true; // ff00::/8 multicast\n return false;\n}\n\n/**\n * Expand an IPv6 string into exactly 8 16-bit numbers. Handles `::` compression.\n * Returns null on malformed input \u2014 caller should treat that as \"block\".\n */\nexport function expandIPv6(addr: string): number[] | null {\n const parts = addr.split('::');\n if (parts.length > 2) return null;\n\n const parseGroups = (s: string): number[] | null => {\n if (s === '') return [];\n const out: number[] = [];\n for (const g of s.split(':')) {\n if (g.length === 0 || g.length > 4) return null;\n const n = Number.parseInt(g, 16);\n if (Number.isNaN(n) || n < 0 || n > 0xffff) return null;\n out.push(n);\n }\n return out;\n };\n\n if (parts.length === 1) {\n const groups = parseGroups(parts[0] ?? '');\n if (groups?.length !== 8) return null;\n return groups;\n }\n\n const head = parseGroups(parts[0] ?? '');\n const tail = parseGroups(parts[1] ?? '');\n if (!head || !tail) return null;\n const fill = 8 - head.length - tail.length;\n if (fill < 0) return null;\n return [...head, ...new Array<number>(fill).fill(0), ...tail];\n}\n\n/**\n * Convenience: throw if `hostname` resolves to a private / loopback IP.\n * Use as a pre-flight check before opening a socket.\n *\n * \u26A0\uFE0F This is not sufficient alone \u2014 connections must also use a pinned\n * dispatcher (so the OS re-uses the already-resolved address) or the same\n * check must be applied after every redirect hop. See `guardedLookup` in\n * `fetch.ts` for the connection-level enforcement.\n */\nexport async function assertNotPrivateHost(hostname: string): Promise<void> {\n const host =\n hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;\n\n if (host === 'localhost' || host.endsWith('.localhost')) {\n throw new Error('fetch: blocked localhost target');\n }\n\n const ipVersion = net.isIP(host);\n if (ipVersion === 4) {\n if (isPrivateIPv4(host)) {\n throw new Error(`fetch: blocked private/loopback address \"${host}\"`);\n }\n } else if (ipVersion === 6) {\n if (isPrivateIPv6(host)) {\n throw new Error(`fetch: blocked private/loopback address \"${host}\"`);\n }\n } else {\n // Hostname \u2014 resolve and reject if ANY record is private.\n try {\n const records = await dns.lookup(host, { all: true });\n for (const r of records) {\n // dns.lookup family: 4 = IPv4, 6 = IPv6\n const bad = r.family === 4 ? isPrivateIPv4(r.address) : isPrivateIPv6(r.address);\n if (bad) {\n throw new Error(`fetch: resolved to private address ${r.address}`);\n }\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('fetch:')) throw err;\n // DNS failure \u2014 let fetch handle it rather than doubling the error.\n }\n }\n}\n", "import { expectDefined } from './expect-defined.js';\n\n/**\n * Attempt to close an incomplete JSON object string by auto-closing braces\n * and completing any unclosed double-quoted string values.\n *\n * Strategy:\n * 1. Compute origOpen from the ORIGINAL input (how many braces are unclosed).\n * 2. Add that many closing braces. If result is now valid JSON \u2192 return it.\n * 3. If still invalid: trim trailing whitespace, strip trailing backslash.\n * 4. Walk backwards to detect an unclosed string value.\n * - Quote followed by `:` \u2192 key-name, skip\n * - Quote followed by `,` `}` or end-of-string \u2192 toggle in/out of string\n * 5. If we end INSIDE a string (unclosed opening `\"`), append `\"` + origOpen `}`.\n *\n * Known limitations:\n * - Strings whose content ends with a `\"` character cannot be repaired\n * (algorithm can't distinguish content-`\"` from string-terminator `\"`).\n * - Input ending in bare `:` (incomplete value expression) can't be meaningfully repaired.\n * - Bare `{` returns unchanged.\n * - If origOpen=0 (braces balanced) but string is unclosed, repair is skipped\n * (the input would be valid JSON per JSON.parse, so it's returned as-is).\n */\nexport function completePartialObject(s: string): string {\n if (!s.trim().startsWith('{')) return s;\n if (tryParse(s).ok) return s;\n return repairTruncated(s);\n}\n\nfunction repairTruncated(s: string): string {\n // Single forward scan capturing the structural state at the truncation point:\n // the open-container stack, whether we are inside a string, a dangling escape,\n // and where the last significant (non-trailing-whitespace) character sits.\n const stack: ('{' | '[')[] = [];\n let inString = false;\n let escaped = false;\n let sawKey = false; // have we seen any string (i.e. real content) yet?\n let prevSig = ''; // last significant char seen outside of a string\n let contentEnd = 0; // index just past the last significant char\n // Count unbalanced `{` accumulated *inside* the currently-open string value,\n // so a truncation mid-string like `\"a{` can be balanced before closing it.\n let stringBraceDepth = 0;\n\n for (let i = 0; i < s.length; i++) {\n const ch = expectDefined(s[i]);\n if (inString) {\n contentEnd = i + 1;\n if (escaped) {\n escaped = false;\n continue;\n }\n if (ch === '\\\\') {\n escaped = true;\n continue;\n }\n if (ch === '\"') {\n inString = false;\n prevSig = '\"';\n stringBraceDepth = 0;\n continue;\n }\n if (ch === '{') stringBraceDepth++;\n else if (ch === '}' && stringBraceDepth > 0) stringBraceDepth--;\n continue;\n }\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') continue;\n contentEnd = i + 1;\n if (ch === '\"') {\n inString = true;\n sawKey = true;\n stringBraceDepth = 0;\n prevSig = '\"';\n } else if (ch === '{' || ch === '[') {\n stack.push(ch);\n prevSig = ch;\n } else if (ch === '}' || ch === ']') {\n stack.pop();\n prevSig = ch;\n } else {\n prevSig = ch;\n }\n }\n\n // A lone open brace (or anything with no key/content) can't be meaningfully\n // completed \u2014 return it untouched.\n if (!sawKey && !inString) return s;\n\n // Drop trailing whitespace that sits outside any string.\n let result = s.slice(0, contentEnd);\n\n if (inString) {\n // A dangling lone backslash can't begin a valid escape \u2014 drop it.\n if (escaped) {\n result = result.slice(0, -1);\n } else if (endsWithInvalidEscape(result)) {\n // A trailing invalid escape (e.g. `\\}`) can't be completed into valid\n // JSON \u2014 strip the backslash and its bogus escapee.\n result = result.slice(0, -2);\n }\n // Balance braces opened inside the truncated string before closing it.\n if (stringBraceDepth > 0) result += '}'.repeat(stringBraceDepth);\n result += '\"';\n } else if (prevSig === ':') {\n // A key with no value (e.g. `{\"k\":`) \u2014 complete it to null.\n result += 'null';\n }\n\n // Close any still-open containers in reverse order.\n for (let k = stack.length - 1; k >= 0; k--) {\n result += stack[k] === '{' ? '}' : ']';\n }\n\n // Last resort: an empty value sitting before an existing close (`{\"k\":}`)\n // leaves invalid JSON \u2014 fill it with null.\n if (!tryParse(result).ok) {\n const patched = result.replace(/:(\\s*)([}\\]])/g, ':null$2');\n if (tryParse(patched).ok) result = patched;\n }\n\n return result;\n}\n\nconst VALID_ESCAPE = new Set(['\"', '\\\\', '/', 'b', 'f', 'n', 'r', 't', 'u']);\n\n/** True when `str` ends with a backslash escape that JSON does not allow. */\nfunction endsWithInvalidEscape(str: string): boolean {\n const last = str[str.length - 1];\n if (str[str.length - 2] !== '\\\\' || last === undefined) return false;\n if (VALID_ESCAPE.has(last)) return false;\n // The backslash must itself be unescaped (odd run of backslashes before it).\n let backslashes = 0;\n for (let k = str.length - 2; k >= 0 && str[k] === '\\\\'; k--) backslashes++;\n return backslashes % 2 === 1;\n}\n\nfunction tryParse(s: string): { ok: true; value: unknown } | { ok: false } {\n try {\n return { ok: true, value: JSON.parse(s) };\n } catch {\n return { ok: false };\n }\n}\n", "/**\n * Minimal JSON Schema validator \u2014 covers the subset needed for plugin\n * configSchema validation and tool inputSchema sanity checks. Intentionally\n * small (~80 lines, zero deps) and tolerant: unknown keywords are ignored so\n * authors can mix in non-standard extensions without breaking validation.\n *\n * NOT for full JSON Schema 2020-12 conformance. If a plugin needs $ref,\n * conditional schemas, format validation, or anything else exotic, it should\n * bring its own ajv-based validator and call this only for the cheap path.\n */\nimport type { JSONSchema } from '../types/tool.js';\n\nexport interface ValidationError {\n path: string;\n message: string;\n}\n\nexport interface ValidationResult {\n ok: boolean;\n errors: ValidationError[];\n}\n\nexport function validateAgainstSchema(value: unknown, schema: JSONSchema): ValidationResult {\n const errors: ValidationError[] = [];\n walk(value, schema, '', errors, 0);\n return { ok: errors.length === 0, errors };\n}\n\n/**\n * Maximum nesting depth before the validator stops recursing and reports a\n * \"schema too deep\" error. Deeply nested input (e.g. batch_tool_use with 100\n * nested calls \u2192 tool_use \u2192 input) can otherwise hit `RangeError: Maximum\n * call stack size exceeded` and crash the tool executor.\n *\n * 64 is generous: real-world tool schemas rarely nest beyond 5-6 levels, and\n * even pathological inputs (deeply recursive JSON) stay well under this.\n * The limit is a safety net against unbounded recursion, not a tight bound.\n */\nconst MAX_SCHEMA_DEPTH = 64;\n\nfunction walk(\n value: unknown,\n schema: JSONSchema,\n path: string,\n errors: ValidationError[],\n depth: number,\n): void {\n // P2 #8 (before-release.md): cap recursion depth to prevent\n // `RangeError: Maximum call stack size exceeded` on deeply nested input.\n // Push a validation error and stop descending \u2014 the caller still gets a\n // usable (ok: false) result instead of a crash.\n if (depth > MAX_SCHEMA_DEPTH) {\n errors.push({\n path: path || '<root>',\n message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`,\n });\n return;\n }\n if (schema.enum !== undefined) {\n if (!enumIncludes(schema.enum, value)) {\n errors.push({\n path: path || '<root>',\n message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`,\n });\n return;\n }\n }\n\n if (typeof schema.type === 'string') {\n if (!checkType(value, schema.type)) {\n errors.push({\n path: path || '<root>',\n message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`,\n });\n return;\n }\n }\n\n if (schema.type === 'object' && isPlainObject(value)) {\n const obj = value as Record<string, unknown>;\n for (const req of schema.required ?? []) {\n if (!(req in obj)) {\n const expected = schema.properties?.[req]?.type;\n errors.push({\n path: joinPath(path, req),\n message: `required property missing${typeof expected === 'string' ? ` (expected ${expected})` : ''}`,\n });\n }\n }\n if (schema.properties) {\n for (const [key, subSchema] of Object.entries(schema.properties)) {\n if (key in obj) {\n walk(obj[key], subSchema, joinPath(path, key), errors, depth + 1);\n }\n }\n }\n }\n\n if (schema.type === 'array' && Array.isArray(value) && schema.items) {\n for (let i = 0; i < value.length; i++) {\n walk(value[i], schema.items as JSONSchema, `${path}[${i}]`, errors, depth + 1);\n }\n }\n}\n\nexport interface CoercionResult {\n value: unknown;\n /** True when at least one leaf was rewritten. */\n changed: boolean;\n}\n\n/**\n * Best-effort, lossless coercion of a value toward a JSON Schema. Models \u2014\n * especially through OpenAI-compatible proxies \u2014 frequently deliver\n * arguments with the right *content* in the wrong *type*: numbers and\n * booleans encoded as strings (\"5\", \"true\"), scalars where a string is\n * expected, or a whole nested object serialized into a JSON string.\n *\n * Only conversions that cannot lose information are applied:\n * - string \u2192 number/integer when the whole trimmed string parses cleanly\n * - string \"true\"/\"false\" \u2192 boolean\n * - number/boolean \u2192 string when a string is expected\n * - JSON-serialized string \u2192 object/array when the parse yields that shape\n * Recurses into `properties`/`items`. Returns the (possibly new) value and\n * whether anything changed \u2014 callers should re-validate before trusting it.\n */\nexport function coerceAgainstSchema(value: unknown, schema: JSONSchema): CoercionResult {\n return coerceWalk(value, schema, 0);\n}\n\nfunction coerceWalk(value: unknown, schema: JSONSchema, depth: number): CoercionResult {\n if (depth > MAX_SCHEMA_DEPTH) return { value, changed: false };\n\n const type = typeof schema.type === 'string' ? schema.type : undefined;\n\n // Leaf conversions (only when the current type does NOT already match).\n if (type && !checkType(value, type)) {\n if (type === 'string' && (typeof value === 'number' || typeof value === 'boolean')) {\n return { value: String(value), changed: true };\n }\n if ((type === 'number' || type === 'integer') && typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed !== '' && /^-?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$/.test(trimmed)) {\n const num = Number(trimmed);\n if (!Number.isNaN(num) && (type === 'number' || Number.isInteger(num))) {\n return { value: num, changed: true };\n }\n }\n return { value, changed: false };\n }\n if (type === 'boolean' && typeof value === 'string') {\n const lowered = value.trim().toLowerCase();\n if (lowered === 'true') return { value: true, changed: true };\n if (lowered === 'false') return { value: false, changed: true };\n return { value, changed: false };\n }\n if ((type === 'object' || type === 'array') && typeof value === 'string') {\n // A nested structure serialized into a string (double-encoded args).\n try {\n const parsed: unknown = JSON.parse(value);\n if (checkType(parsed, type)) {\n // Recurse so leaves inside the revived structure also coerce.\n return { value: coerceWalk(parsed, schema, depth + 1).value, changed: true };\n }\n } catch {\n // fall through \u2014 leave as-is, validation will report it\n }\n return { value, changed: false };\n }\n return { value, changed: false };\n }\n\n // Structural recursion.\n if (type === 'object' && isPlainObject(value) && schema.properties) {\n const obj = value as Record<string, unknown>;\n let changed = false;\n const out: Record<string, unknown> = { ...obj };\n for (const [key, subSchema] of Object.entries(schema.properties)) {\n if (!(key in obj)) continue;\n const r = coerceWalk(obj[key], subSchema, depth + 1);\n if (r.changed) {\n out[key] = r.value;\n changed = true;\n }\n }\n return changed ? { value: out, changed } : { value, changed: false };\n }\n\n if (type === 'array' && Array.isArray(value) && schema.items) {\n let changed = false;\n const out = value.map((item) => {\n const r = coerceWalk(item, schema.items as JSONSchema, depth + 1);\n if (r.changed) changed = true;\n return r.value;\n });\n return changed ? { value: out, changed } : { value, changed: false };\n }\n\n return { value, changed: false };\n}\n\nfunction checkType(value: unknown, type: string): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n return typeof value === 'number' && !Number.isNaN(value);\n case 'integer':\n return typeof value === 'number' && Number.isInteger(value);\n case 'boolean':\n return typeof value === 'boolean';\n case 'null':\n return value === null;\n case 'array':\n return Array.isArray(value);\n case 'object':\n return isPlainObject(value);\n default:\n return true;\n }\n}\n\nfunction isPlainObject(v: unknown): boolean {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction describeType(v: unknown): string {\n if (v === null) return 'null';\n if (Array.isArray(v)) return 'array';\n return typeof v;\n}\n\n/** Short serialized preview of the offending value for error messages. */\nfunction previewValue(v: unknown): string {\n try {\n const s = JSON.stringify(v);\n if (s === undefined) return String(v);\n return s.length > 80 ? `${s.slice(0, 80)}\u2026` : s;\n } catch {\n return String(v);\n }\n}\n\nfunction joinPath(parent: string, key: string): string {\n if (!parent) return key;\n return `${parent}.${key}`;\n}\n\nfunction enumIncludes(values: readonly unknown[], value: unknown): boolean {\n if (value === null || typeof value !== 'object') return values.includes(value);\n return values.some((candidate) => deepEqual(candidate, value));\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));\n }\n if (typeof a === 'object' && typeof b === 'object') {\n const ak = Object.keys(a as object);\n const bk = Object.keys(b as object);\n if (ak.length !== bk.length) return false;\n return ak.every((k) =>\n deepEqual((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),\n );\n }\n return false;\n}\n", "import type { CustomModelDefinition } from '../types/config.js';\n\n/**\n * Merge per-provider `customModels` into top-level `configModels`.\n *\n * Keys present in `configModels` always win over `providerCustomModels`\n * when the same model id appears in both places. This lets the user\n * override provider-attached definitions from the top-level config.\n *\n * Pure: never mutates its inputs.\n */\nexport function mergeCustomModelDefs(\n providerCustomModels: Record<string, CustomModelDefinition> | undefined,\n configModels: Record<string, CustomModelDefinition> | undefined,\n): Record<string, CustomModelDefinition> | undefined {\n const out: Record<string, CustomModelDefinition> = {};\n\n // Layer 1: provider-level definitions (weaker).\n if (providerCustomModels) {\n for (const [id, def] of Object.entries(providerCustomModels)) {\n out[id] = { ...def };\n }\n }\n\n // Layer 2: top-level definitions (stronger).\n if (configModels) {\n for (const [id, def] of Object.entries(configModels)) {\n out[id] = { ...def }; // top-level overwrites provider-level\n }\n }\n\n if (Object.keys(out).length === 0) return undefined;\n return out;\n}\n", "import type {\n ModelsDevModel,\n ModelsDevProvider,\n ModelsDevPayload,\n} from '../types/models-registry.js';\n\n/**\n * Deep-merge a curated `overlay` payload on top of a `base` payload (both in\n * the models.dev `api.json` shape). The overlay always wins: it can add\n * providers/models the base lacks and override fields the base gets wrong.\n *\n * Precedence rules:\n * - Provider present in both \u2192 scalar fields (`name`, `npm`, `api`, `env`,\n * `doc`) come from the overlay when set; `models` maps merge by model id.\n * - Provider only in the overlay \u2192 added wholesale.\n * - Model present in both \u2192 overlay model fields override base model fields\n * (`{ ...base, ...overlay }`), with the nested `limit` / `cost` /\n * `modalities` objects merged one level deeper so an overlay can fix just\n * `limit.context` without restating the rest of the model.\n * - Model only in the overlay \u2192 added.\n *\n * Pure: never mutates its inputs.\n */\nexport function mergeModelsPayload(\n base: ModelsDevPayload,\n overlay: ModelsDevPayload,\n): ModelsDevPayload {\n const out: ModelsDevPayload = {};\n for (const [id, provider] of Object.entries(base)) {\n out[id] = cloneProvider(provider);\n }\n for (const [id, ovProvider] of Object.entries(overlay)) {\n const existing = out[id];\n out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);\n }\n return out;\n}\n\nfunction mergeProvider(base: ModelsDevProvider, overlay: ModelsDevProvider): ModelsDevProvider {\n const models: Record<string, ModelsDevModel> = {};\n for (const [mid, m] of Object.entries(base.models ?? {})) {\n models[mid] = { ...m };\n }\n for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {\n const existing = models[mid];\n models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };\n }\n return {\n ...base,\n // Overlay scalar fields win when explicitly provided; otherwise keep base.\n ...stripUndefined({\n id: overlay.id,\n name: overlay.name,\n npm: overlay.npm,\n api: overlay.api,\n env: overlay.env,\n doc: overlay.doc,\n }),\n models,\n };\n}\n\nfunction mergeModel(base: ModelsDevModel, overlay: ModelsDevModel): ModelsDevModel {\n const merged: ModelsDevModel = { ...base, ...overlay };\n // One level deeper for the structured fields so a partial overlay (e.g. only\n // `limit.context`) doesn't blow away the base's other sub-fields.\n if (base.limit || overlay.limit) {\n merged.limit = { ...base.limit, ...overlay.limit };\n }\n if (base.cost || overlay.cost) {\n merged.cost = { ...base.cost, ...overlay.cost };\n }\n if (base.modalities || overlay.modalities) {\n merged.modalities = { ...base.modalities, ...overlay.modalities };\n }\n return merged;\n}\n\nfunction cloneProvider(p: ModelsDevProvider): ModelsDevProvider {\n const models: Record<string, ModelsDevModel> = {};\n for (const [mid, m] of Object.entries(p.models ?? {})) {\n models[mid] = { ...m };\n }\n return { ...p, models };\n}\n\n/** Drop keys whose value is `undefined` so they don't clobber base fields. */\nfunction stripUndefined<T extends Record<string, unknown>>(obj: T): Partial<T> {\n const out: Partial<T> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) out[k as keyof T] = v as T[keyof T];\n }\n return out;\n}\n", "export type NewlineStyle = 'lf' | 'crlf' | 'cr';\n\nexport function detectNewlineStyle(text: string): NewlineStyle {\n let lf = 0;\n let crlf = 0;\n let cr = 0;\n for (let i = 0; i < text.length; i++) {\n const c = text.charCodeAt(i);\n if (c === 0x0d) {\n if (text.charCodeAt(i + 1) === 0x0a) {\n crlf++;\n i++;\n } else {\n cr++;\n }\n } else if (c === 0x0a) {\n lf++;\n }\n }\n if (crlf > lf && crlf > cr) return 'crlf';\n if (cr > lf && cr > crlf) return 'cr';\n return 'lf';\n}\n\nexport function toStyle(text: string, style: NewlineStyle): string {\n const normalized = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n if (style === 'lf') return normalized;\n if (style === 'crlf') return normalized.replace(/\\n/g, '\\r\\n');\n return normalized.replace(/\\n/g, '\\r');\n}\n\nexport function normalizeToLf(text: string): string {\n return text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n}\n", "/**\n * Compile a user-supplied regex with conservative bounds against ReDoS.\n *\n * Duplicated from @wrongstack/tools/_regex.ts to avoid a circular\n * dependency (tools depends on core, not vice versa). Keep both copies\n * in sync if the heuristics change.\n *\n * V8's regex engine is backtracking-based and cannot interrupt a\n * synchronous match \u2014 a pattern like `(a+)+$` against a sufficiently\n * long line will pin a worker for seconds.\n */\n\nconst MAX_PATTERN_LEN = 512;\n\n// Heuristics for catastrophic-backtracking constructs.\nconst DANGEROUS_PATTERNS: ReadonlyArray<RegExp> = [\n /(\\([^)]*[+*][^)]*\\))[+*]/, // (a+)+, (.*)+, etc\n /(\\(\\?:[^)]*[+*][^)]*\\))[+*]/, // same, with non-capturing group\n];\n\nexport interface CompileResult {\n ok: true;\n regex: RegExp;\n}\n\nexport interface CompileFail {\n ok: false;\n reason: string;\n}\n\nexport function compileUserRegex(pattern: string, flags: string): CompileResult | CompileFail {\n if (typeof pattern !== 'string') {\n return { ok: false, reason: 'pattern must be a string' };\n }\n if (pattern.length === 0) {\n return { ok: false, reason: 'pattern is empty' };\n }\n if (pattern.length > MAX_PATTERN_LEN) {\n return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };\n }\n for (const rx of DANGEROUS_PATTERNS) {\n if (rx.test(pattern)) {\n return {\n ok: false,\n reason:\n 'pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers',\n };\n }\n }\n try {\n return { ok: true, regex: new RegExp(pattern, flags) };\n } catch (err) {\n return {\n ok: false,\n reason: err instanceof Error ? err.message : 'invalid regex',\n };\n }\n}\n", "import { toErrorMessage } from './error.js';\n\nexport interface SafeParseResult<T> {\n ok: boolean;\n value?: T | undefined;\n error?: string | undefined;\n}\n\nexport function safeParse<T = unknown>(input: string, maxBytes = 5_000_000): SafeParseResult<T> {\n if (Buffer.byteLength(input, 'utf8') > maxBytes) {\n return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };\n }\n try {\n return { ok: true, value: JSON.parse(input) as T };\n } catch (err) {\n return {\n ok: false,\n error: toErrorMessage(err),\n };\n }\n}\n\nexport function safeStringify(value: unknown, pretty = false): string {\n const seen = new WeakSet();\n const replacer = (_k: string, v: unknown): unknown => {\n if (typeof v === 'bigint') return v.toString();\n if (v instanceof Error) {\n return { name: v.name, message: v.message, stack: v.stack };\n }\n if (typeof v === 'object' && v !== null) {\n if (seen.has(v as object)) return '[Circular]';\n seen.add(v as object);\n }\n return v;\n };\n try {\n return JSON.stringify(value, replacer, pretty ? 2 : undefined) ?? 'null';\n } catch (err) {\n return JSON.stringify({\n __serialization_error: toErrorMessage(err),\n });\n }\n}\n\n/**\n * Attempt to parse JSON5-style input and return a valid JSON string.\n * Handles trailing commas, line/block comments, and unquoted keys\n * that are common in provider output.\n *\n * Returns the sanitized string if it parses successfully as JSON,\n * or `null` if the input cannot be made valid. Callers use this to\n * decide whether to proceed with the parsed result or fall back to\n * raw handling.\n */\nexport function sanitizeJsonString(s: string): string | null {\n let out = s.trim();\n\n // Stage 1: strip line and block comments outside JSON string values.\n out = stripJsonComments(out);\n\n // Stage 2: strip trailing commas before } or ]\n out = out.replace(/,(\\s*[}\\]])/g, '$1');\n\n // Stage 3: escape literal control characters that appear *inside* string\n // values. Models frequently emit raw newlines/tabs inside a code payload\n // (e.g. edit's old_string/new_string) instead of the required \\n / \\t, which\n // makes JSON.parse throw. This is the single most common malformed-args case.\n out = escapeControlCharsInStrings(out);\n\n // Stage 4: attempt full parse; return null if it fails so callers can\n // distinguish \"already valid JSON\" from \"unrecoverable\".\n try {\n JSON.parse(out);\n return out;\n } catch {\n return null; // stripped but still not valid JSON; caller handles it\n }\n}\n\n/**\n * Strip a Markdown code-fence wrapper from a payload.\n *\n * Models occasionally return tool-call arguments wrapped in ```json fences\n * (or embedded in prose around one) instead of bare JSON. Returns the inner\n * content when the input starts with a fence (closing fence optional, so a\n * truncated stream still unwraps) or contains one complete fenced block;\n * returns null when no fence is present. Callers should only invoke this\n * after a direct parse failed, so fences inside legitimate string values are\n * never touched.\n */\nexport function stripCodeFences(s: string): string | null {\n const trimmed = s.trim();\n // Whole-payload fence: ```lang? \u2026 ```? (closer optional for truncation)\n const opener = /^```[\\w+-]*[ \\t]*\\r?\\n?/.exec(trimmed);\n if (opener) {\n const inner = trimmed.slice(opener[0].length).replace(/(\\r?\\n)?[ \\t]*```[ \\t]*$/, '');\n return inner.trim();\n }\n // Fence embedded in prose: extract the first complete fenced block.\n const embedded = /```[\\w+-]*[ \\t]*\\r?\\n([\\s\\S]*?)\\r?\\n[ \\t]*```/.exec(trimmed);\n if (embedded) return (embedded[1] ?? '').trim();\n return null;\n}\n\n/**\n * Walk the string tracking whether we are inside a JSON string literal and\n * replace raw control characters (U+0000\u2013U+001F) that appear inside strings\n * with their valid JSON escape sequences. Characters outside strings are left\n * untouched (insignificant whitespace stays as-is). Already-escaped sequences\n * are not double-escaped because we only act on *literal* control bytes.\n */\nfunction escapeControlCharsInStrings(s: string): string {\n let inString = false;\n let out = '';\n for (let i = 0; i < s.length; i++) {\n const c = s.charAt(i);\n if (c === '\"' && (i === 0 || s[i - 1] !== '\\\\')) {\n inString = !inString;\n out += c;\n continue;\n }\n const code = c.charCodeAt(0);\n if (inString && code < 0x20) {\n switch (c) {\n case '\\n':\n out += '\\\\n';\n break;\n case '\\r':\n out += '\\\\r';\n break;\n case '\\t':\n out += '\\\\t';\n break;\n case '\\b':\n out += '\\\\b';\n break;\n case '\\f':\n out += '\\\\f';\n break;\n default:\n out += `\\\\u${code.toString(16).padStart(4, '0')}`;\n }\n continue;\n }\n out += c;\n }\n return out;\n}\n\nfunction stripJsonComments(s: string): string {\n let inString = false;\n let escaped = false;\n const chars: string[] = [];\n let i = 0;\n\n while (i < s.length) {\n const c = s.charAt(i);\n\n if (inString) {\n chars.push(c);\n if (escaped) {\n escaped = false;\n } else if (c === '\\\\') {\n escaped = true;\n } else if (c === '\"') {\n inString = false;\n }\n i++;\n continue;\n }\n\n if (c === '\"') {\n inString = true;\n chars.push(c);\n i++;\n continue;\n }\n\n if (c === '/' && s.charAt(i + 1) === '/') {\n while (i < s.length && s.charAt(i) !== '\\n') i++;\n continue;\n }\n\n if (c === '/' && s.charAt(i + 1) === '*') {\n const end = s.indexOf('*/', i + 2);\n if (end === -1) {\n // Preserve an unterminated opener so the final JSON.parse rejects it.\n chars.push(s.slice(i));\n break;\n }\n i = end + 2;\n continue;\n }\n\n chars.push(c);\n i++;\n }\n\n return chars.join('');\n}\n", "import * as path from 'node:path';\nimport { ERROR_CODES, FsError } from '../types/errors.js';\n\n/**\n * Resolve `<dir>/<sessionId><suffix>` for per-session sidecar files\n * (annotations, audit chain, replay log, the session JSONL itself).\n *\n * Modern session ids are date-sharded (\"2026-06-11/sess_<ULID>\"),\n * so a forward slash is a legitimate shard separator \u2014 NOT traversal.\n * Escape attempts are blocked two ways: an explicit ban on `..` and\n * backslashes, plus a resolved-path containment check that rejects any\n * id whose resolved target leaves `dir`. Character bans alone are how\n * several stores ended up throwing on every modern session id.\n */\nexport function sessionScopedPath(dir: string, sessionId: string, suffix: string): string {\n if (!sessionId || sessionId.includes('\\\\') || sessionId.includes('..')) {\n throw invalid(sessionId);\n }\n const resolved = path.resolve(dir, `${sessionId}${suffix}`);\n const rel = path.relative(path.resolve(dir), resolved);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw invalid(sessionId);\n }\n return resolved;\n}\n\nfunction invalid(sessionId: string): FsError {\n return new FsError({\n message: `Invalid sessionId: ${sessionId}`,\n code: ERROR_CODES.FS_DELETE_FAILED,\n path: sessionId,\n context: { reason: 'path_traversal' },\n });\n}\n", "/** Resolve a promise after `ms` milliseconds. Prefer this over raw\n * `setTimeout` wrappers so all delay sites use a single implementation\n * and an abortable variant can be introduced without a codebase-wide hunt. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n", "/**\n * Turn an arbitrary string into a filesystem- and URL-safe lowercase slug.\n *\n * Collapses every run of non-alphanumeric characters into a single hyphen,\n * trims leading/trailing hyphens, and caps the length. Returns `fallback`\n * when the input slugifies to the empty string.\n *\n * Used as the stable dedup + registry key for prompts. (Distinct from the\n * project-folder slug in `wstack-paths.ts`, which has its own `'project'`\n * fallback and shorter cap.)\n */\nexport function slugify(name: string, fallback = 'prompt', maxLen = 64): string {\n return (\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, maxLen)\n .replace(/-+$/g, '') || fallback\n );\n}\n", "/**\n * String utilities shared across the WrongStack codebase.\n */\n\n/**\n * Truncate a string to at most `max` characters, appending an ellipsis if it\n * was longer. Returns the original string unchanged when it fits.\n */\nexport function truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;\n}\n", "import type {\n TaskPriority,\n TaskStatus,\n TaskType,\n TaskProgress as TaskGraphProgress,\n} from '../types/task-graph.js';\nimport { color } from './color.js';\n\n// Re-export graph types for convenience\nexport type { TaskStatus, TaskPriority, TaskType };\n\n// ---------------------------------------------------------------------------\n// Session-level task item \u2014 mirrors TaskNode but with string timestamps\n// for JSON serialization and a flat-list structure (no graph edges).\n// ---------------------------------------------------------------------------\n\nexport interface TaskItem {\n id: string;\n title: string;\n description?: string | undefined;\n type: TaskType;\n priority: TaskPriority;\n status: TaskStatus;\n /** IDs of tasks this one depends on. */\n dependsOn?: string[] | undefined;\n /** Agent/subagent name assigned to this task. */\n assignee?: string | undefined;\n estimateHours?: number | undefined;\n tags?: string[] | undefined;\n createdAt: string;\n updatedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Progress (re-export computeTaskItemProgress adapted for TaskItem[])\n// ---------------------------------------------------------------------------\n\nexport function computeTaskItemProgress(tasks: TaskItem[]): TaskGraphProgress {\n let completed = 0;\n let pending = 0;\n let inProgress = 0;\n let blocked = 0;\n let failed = 0;\n let review = 0;\n let estimatedHours = 0;\n const actualHours = 0;\n for (const t of tasks) {\n switch (t.status) {\n case 'completed':\n completed++;\n break;\n case 'pending':\n pending++;\n break;\n case 'in_progress':\n inProgress++;\n break;\n case 'blocked':\n blocked++;\n break;\n case 'failed':\n failed++;\n break;\n case 'review':\n review++;\n break;\n }\n estimatedHours += t.estimateHours ?? 0;\n }\n return {\n total: tasks.length,\n pending,\n inProgress,\n blocked,\n failed,\n review,\n completed,\n percentComplete: tasks.length > 0 ? Math.round((completed / tasks.length) * 100) : 0,\n estimatedHours,\n actualHours,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Icons\n// ---------------------------------------------------------------------------\n\nconst STATUS_ICON: Record<TaskStatus, string> = {\n pending: '\u25CB',\n in_progress: '\u25D0',\n blocked: '\u2298',\n failed: '\u2717',\n review: '\u25D1',\n completed: '\u25CF',\n};\n\nconst PRIORITY_ICON: Record<TaskPriority, string> = {\n critical: '\uD83D\uDD34',\n high: '\uD83D\uDFE0',\n medium: '\uD83D\uDFE1',\n low: '\uD83D\uDFE2',\n};\n\nconst TYPE_ICON: Record<TaskType, string> = {\n feature: '\u26A1',\n bugfix: '\uD83D\uDC1B',\n refactor: '\u267B\uFE0F',\n docs: '\uD83D\uDCDD',\n test: '\uD83E\uDDEA',\n chore: '\uD83D\uDD27',\n};\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\nexport function formatTaskProgress(tasks: TaskItem[]): string {\n const p = computeTaskItemProgress(tasks);\n if (p.total === 0) return 'No tasks.';\n const barWidth = 24;\n const filled = Math.round((p.percentComplete / 100) * barWidth);\n const empty = barWidth - filled;\n const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);\n return [\n `${color.bold('Tasks')} [${bar}] ${p.percentComplete}%`,\n ` ${color.green('\u25CF')} ${p.completed} done \u2502 ${color.yellow('\u25D0')} ${p.inProgress} active \u2502 ${color.dim('\u25CB')} ${p.pending} pending \u2502 \u2298 ${p.blocked} blocked \u2502 \u2717 ${p.failed} failed`,\n p.estimatedHours > 0\n ? ` ${color.dim(`est. ${p.estimatedHours}h`)}`\n : '',\n ]\n .filter(Boolean)\n .join('\\n');\n}\n\nexport function formatTaskList(tasks: TaskItem[]): string {\n if (tasks.length === 0) return 'No tasks.';\n\n // Group by status\n const order: TaskStatus[] = ['in_progress', 'blocked', 'review', 'pending', 'failed', 'completed'];\n const groups = new Map<TaskStatus, TaskItem[]>();\n for (const t of tasks) {\n const list = groups.get(t.status) ?? [];\n list.push(t);\n groups.set(t.status, list);\n }\n\n const lines: string[] = [];\n lines.push(color.dim(`Tasks (${tasks.length} total):`));\n\n for (const status of order) {\n const group = groups.get(status);\n if (!group || group.length === 0) continue;\n const icon = STATUS_ICON[status];\n lines.push(` ${icon} ${status.toUpperCase()} (${group.length})`);\n for (const t of group) {\n const prio = PRIORITY_ICON[t.priority];\n const type = TYPE_ICON[t.type];\n const deps =\n t.dependsOn && t.dependsOn.length > 0\n ? ` ${color.dim('\u2190')} ${color.dim(t.dependsOn.map((d) => d.slice(0, 8)).join(', '))}`\n : '';\n const who = t.assignee ? ` ${color.dim(`@${t.assignee}`)}` : '';\n const hrs = t.estimateHours ? ` ${color.dim(`${t.estimateHours}h`)}` : '';\n lines.push(` ${type} ${prio} ${t.title}${deps}${who}${hrs}`);\n }\n }\n\n return lines.join('\\n');\n}\n", "import type { TodoItem } from '../core/context.js';\nimport { color } from './color.js';\n\n/**\n * Canonical text rendering of the live todo list, shared by the CLI's\n * `/todos` slash command and the TUI's auto-echo (which prints the same\n * snapshot to chat history each time the `todo` tool mutates the list).\n *\n * Layout: a header line with the `done/total done` count, then one row\n * per item \u2014 `[ ]` pending, `[~]` in-progress, `[x]` completed. In-\n * progress rows prefer `activeForm` (\"Building the project\") over the\n * imperative `content` (\"Build the project\") when present.\n *\n * Returned as a single newline-joined string so callers can hand it\n * straight to a history dispatcher or stdout.\n */\nexport function formatTodosList(todos: TodoItem[]): string {\n if (todos.length === 0) return 'No todos.';\n const lines: string[] = [];\n const done = todos.filter((t) => t.status === 'completed').length;\n lines.push(color.dim(`Todos (${done}/${todos.length} done):`));\n todos.forEach((t, i) => {\n const mark =\n t.status === 'completed'\n ? color.green('[x]')\n : t.status === 'in_progress'\n ? color.yellow('[~]')\n : color.dim('[ ]');\n const text = t.status === 'in_progress' && t.activeForm ? t.activeForm : t.content;\n const label = t.status === 'completed' ? color.dim(text) : text;\n lines.push(` ${color.dim(String(i + 1).padStart(2))}. ${mark} ${label}`);\n });\n return lines.join('\\n');\n}\n\n/**\n * True when the todos list still has at least one unfinished item \u2014 either\n * `pending` (not started) or `in_progress` (underway). The REPL and other\n * post-turn handlers call this to decide whether to surface `<nextsteps>`\n * suggestions to the user: as long as the agent has open todos, finishing\n * them takes priority over offering new prompt options. Surfacing\n * `<nextsteps>` mid-task is what causes YOLO+auto mode and the autonomy\n * 'auto' loop to prematurely pivot away from the in-flight todo list.\n *\n * Returns false for an empty / undefined list (nothing pending, nothing to\n * block on) and false for an all-completed list. Treats any non-array input\n * (legacy contexts, mocks) as \"no todos\" rather than throwing.\n */\nexport function hasOpenTodos(todos: readonly TodoItem[] | undefined | null): boolean {\n if (!Array.isArray(todos) || todos.length === 0) return false;\n return todos.some((t) => t.status === 'pending' || t.status === 'in_progress');\n}\n", "import type { ToolDescriptionMode, ToolDescriptionModeConfig } from '../types/config.js';\nimport type { Tool } from '../types/tool.js';\n\nexport const DEFAULT_TOOL_DESCRIPTION_MODE: ToolDescriptionMode = 'extend';\n\nconst ORIGINAL_TOOL_DESCRIPTION = Symbol.for('wrongstack.tool.originalDescription');\n\ninterface OriginalToolDescription {\n description: string;\n usageHint?: string | undefined;\n}\n\ntype ToolWithOriginalDescription = Tool & {\n [ORIGINAL_TOOL_DESCRIPTION]?: OriginalToolDescription | undefined;\n};\n\nexport interface ToolDescriptionRegistryLike {\n get(name: string): Tool | undefined;\n list(): Tool[];\n wrap?(name: string, wrapper: (tool: Tool) => Tool, owner?: string): void;\n setDescriptionMode?(name: string, mode: ToolDescriptionMode): boolean;\n applyDescriptionModes?(\n modes?: ToolDescriptionModeConfig,\n ): { applied: number; missing: string[] };\n getDescriptionMode?(name: string): ToolDescriptionMode;\n}\n\nexport function normalizeToolDescriptionMode(value: unknown): ToolDescriptionMode | undefined {\n if (typeof value !== 'string') return undefined;\n const raw = value.trim().toLowerCase();\n if (raw === 'extend' || raw === 'extended' || raw === 'full') return 'extend';\n if (raw === 'simple' || raw === 'short' || raw === 'brief') return 'simple';\n return undefined;\n}\n\nexport function resolveToolDescriptionMode(\n modes: ToolDescriptionModeConfig | undefined,\n toolName: string,\n): ToolDescriptionMode {\n return normalizeToolDescriptionMode(modes?.[toolName]) ?? DEFAULT_TOOL_DESCRIPTION_MODE;\n}\n\nexport function simplifyToolDescription(\n text: string,\n opts: { maxSentences?: number | undefined; maxChars?: number | undefined } = {},\n): string {\n const maxSentences = Math.max(1, opts.maxSentences ?? 2);\n const maxChars = Math.max(40, opts.maxChars ?? 180);\n const normalized = text\n .replace(/\\r\\n?/g, '\\n')\n .split('\\n')\n .map((line) => line.trim())\n .filter(Boolean)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n if (normalized.length <= maxChars) return normalized;\n\n const sentences = normalized.match(/[^.!?]+[.!?]+(?=\\s|$)|[^.!?]+$/g) ?? [normalized];\n const selected: string[] = [];\n for (const sentence of sentences) {\n selected.push(sentence.trim());\n const candidate = selected.join(' ');\n if (selected.length >= maxSentences || candidate.length >= maxChars) break;\n }\n\n /* v8 ignore next -- defensive: selected always has \u22651 non-empty sentence so summary is never falsy */\n const summary = selected.join(' ').trim() || normalized;\n if (summary.length <= maxChars) return summary;\n\n const hardLimit = maxChars - 4;\n const boundary = findWordBoundary(summary, hardLimit);\n /* v8 ignore next -- findWordBoundary always returns >0 (semantic/space/limit floor), so the hardLimit fallback is dead */\n return `${summary.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd()} ...`;\n}\n\nexport function applyToolDescriptionModeToTool(\n tool: Tool,\n mode: ToolDescriptionMode,\n): Tool {\n const existingOriginal = getOriginalDescription(tool);\n if (mode === 'extend' && !existingOriginal) return tool;\n\n const original = existingOriginal ?? {\n description: tool.description,\n usageHint: tool.usageHint,\n };\n\n const next =\n mode === 'simple'\n ? withDescription(tool, {\n description: simplifyToolDescription(original.description),\n usageHint:\n original.usageHint === undefined\n ? undefined\n : simplifyToolDescription(original.usageHint),\n })\n : withDescription(tool, original);\n\n return attachOriginalDescription(next, original);\n}\n\nexport function setToolDescriptionMode(\n registry: ToolDescriptionRegistryLike,\n name: string,\n mode: ToolDescriptionMode,\n): boolean {\n if (typeof registry.setDescriptionMode === 'function') {\n return registry.setDescriptionMode(name, mode);\n }\n if (!registry.get(name) || typeof registry.wrap !== 'function') return false;\n registry.wrap(\n name,\n (tool) => applyToolDescriptionModeToTool(tool, mode),\n 'tool-description-mode',\n );\n return true;\n}\n\nexport function getToolDescriptionMode(\n registry: ToolDescriptionRegistryLike,\n name: string,\n): ToolDescriptionMode {\n return registry.getDescriptionMode?.(name) ?? DEFAULT_TOOL_DESCRIPTION_MODE;\n}\n\nexport function applyToolDescriptionModes(\n registry: ToolDescriptionRegistryLike,\n modes?: ToolDescriptionModeConfig,\n): { applied: number; missing: string[] } {\n if (typeof registry.applyDescriptionModes === 'function') {\n return registry.applyDescriptionModes(modes);\n }\n\n const entries = Object.entries(modes ?? {});\n const missing: string[] = [];\n let applied = 0;\n for (const [name, rawMode] of entries) {\n const mode = normalizeToolDescriptionMode(rawMode);\n if (!mode) continue;\n if (setToolDescriptionMode(registry, name, mode)) applied++;\n else missing.push(name);\n }\n return { applied, missing };\n}\n\nfunction getOriginalDescription(tool: Tool): OriginalToolDescription | undefined {\n return (tool as ToolWithOriginalDescription)[ORIGINAL_TOOL_DESCRIPTION];\n}\n\nfunction attachOriginalDescription(tool: Tool, original: OriginalToolDescription): Tool {\n Object.defineProperty(tool, ORIGINAL_TOOL_DESCRIPTION, {\n configurable: true,\n enumerable: false,\n value: original,\n writable: true,\n });\n return tool;\n}\n\nfunction withDescription(tool: Tool, next: OriginalToolDescription): Tool {\n const copy: Tool = {\n ...tool,\n description: next.description,\n usageHint: next.usageHint,\n };\n if (next.usageHint === undefined) {\n delete (copy as { usageHint?: string | undefined }).usageHint;\n }\n return copy;\n}\n\nfunction findWordBoundary(text: string, limit: number): number {\n const semantic = Math.max(\n text.lastIndexOf('. ', limit),\n text.lastIndexOf('; ', limit),\n text.lastIndexOf(', ', limit),\n );\n if (semantic > 40) return semantic + 1;\n const space = text.lastIndexOf(' ', limit);\n return space > 40 ? space : limit;\n}\n", "/**\n * Tool output serialization utilities.\n * Extracted from Agent.executeTools to allow reuse and consistent output handling.\n */\n\nexport interface ToolOutputSerializerOptions {\n perIterationOutputCapBytes?: number | undefined;\n estimator?: ((text: string) => number) | undefined;\n}\n\nexport interface ToolOutputSerializeContext {\n toolName?: string | undefined;\n input?: unknown;\n /**\n * Optional reference to the Tool object. When present and the tool defines\n * a `serialize()` method, the serializer delegates to it instead of the\n * central `renderToolObject()` switch (P3 #21).\n */\n tool?: { serialize?: (output: unknown, input: unknown) => string } | undefined;\n}\n\ntype RecordValue = Record<string, unknown>;\n\nconst DEFAULT_LIST_LIMIT = 500;\nconst LOG_ENTRY_LIMIT = 200;\nconst INLINE_LIMIT = 240;\nconst GREP_FILE_LIMIT = 80;\nconst GREP_MATCHES_PER_FILE = 3;\nconst DIFF_INLINE_LINE_LIMIT = 260;\nconst DIFF_HUNK_LIMIT = 8;\nconst DIFF_HUNK_CONTEXT = 14;\n\n// Pre-compiled regex \u2014 used in parseGrepContentLine() for every grep match line.\n// Compiling once at module load avoids repeated RegExp construction overhead.\nconst GREP_LINE_RE = /^(.+?):(\\d+):(.*)$/;\n\nexport function createToolOutputSerializer(opts: ToolOutputSerializerOptions = {}) {\n const capBytes = opts.perIterationOutputCapBytes ?? 100_000;\n\n function serialize(value: unknown, context: ToolOutputSerializeContext = {}): string {\n if (typeof value === 'string') return value;\n if (value === null || value === undefined) return '';\n if (typeof value === 'object') {\n if (Array.isArray(value)) return value.map((item) => serialize(item)).join('\\n');\n // P3 #21 (before-release.md): prefer the tool's own serialize() method\n // when it defines one \u2014 lets tools own their output formatting without\n // adding a branch to the central renderToolObject() god function.\n if (context.tool?.serialize) {\n try {\n return context.tool.serialize(value, context.input);\n } catch {\n // Fall through to the central renderer if the tool's serializer\n // throws \u2014 never let a formatting error break the tool result.\n }\n }\n if (context.toolName) {\n const compact = renderToolObject(context.toolName, value as RecordValue, context.input);\n if (compact !== undefined) return compact;\n return renderGenericToolObject(context.toolName, value as RecordValue);\n }\n if ('text' in (value as Record<string, unknown>)) {\n const t = (value as Record<string, unknown>).text;\n return typeof t === 'string' ? t : JSON.stringify(value, null, 2);\n }\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }\n return String(value);\n }\n\n function enforceCap(text: string, remainingBudget: number): { text: string; newBudget: number } {\n if (remainingBudget <= 0) {\n return { text: '[truncated: iteration output cap exceeded]', newBudget: 0 };\n }\n const textBytes = Buffer.byteLength(text, 'utf8');\n if (textBytes <= remainingBudget) {\n return { text, newBudget: remainingBudget - textBytes };\n }\n const marker = `\\n\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026\\n`;\n const markerBytes = Buffer.byteLength(marker, 'utf8');\n const available = remainingBudget - markerBytes;\n if (available <= 0) {\n return { text: '[truncated: iteration output cap exceeded]', newBudget: 0 };\n }\n const half = Math.floor(available / 2);\n const first = text.slice(0, half);\n const second = text.slice(text.length - half);\n return { text: `${first}${marker}${second}`, newBudget: 0 };\n }\n\n return { serialize, enforceCap, capBytes };\n}\n\nfunction renderToolObject(toolName: string, obj: RecordValue, input: unknown): string | undefined {\n if (toolName === 'read' && typeof obj['text'] === 'string') {\n return joinSections([\n renderHeader(\n `read: ${stringFromInput(input, 'path') ?? stringField(obj, 'path') ?? '<unknown>'}`,\n {\n offset: numberFromInput(input, 'offset'),\n limit: numberFromInput(input, 'limit'),\n total_lines: obj['total_lines'],\n encoding: obj['encoding'],\n truncated: obj['truncated'],\n cached: obj['cached'],\n note: obj['note'],\n },\n ),\n obj['text'],\n ]);\n }\n\n if (toolName === 'grep' && Array.isArray(obj['matches'])) {\n const matches = stringArrayField(obj, 'matches');\n return joinSections([\n renderHeader(`grep: ${stringFromInput(input, 'pattern') ?? '<pattern>'}`, {\n path: stringFromInput(input, 'path'),\n glob: stringFromInput(input, 'glob'),\n mode: stringFromInput(input, 'output_mode'),\n count: obj['count'],\n shown: matches.length,\n truncated: obj['truncated'],\n used: obj['used'],\n }),\n renderGrepMatches(matches, stringFromInput(input, 'output_mode')),\n ]);\n }\n\n if (toolName === 'patch' && Array.isArray(obj['files'])) {\n const files = stringArrayField(obj, 'files');\n return joinSections([\n renderHeader('patch', {\n applied: obj['applied'],\n rejected: obj['rejected'],\n files: files.length,\n dry_run: obj['dry_run'],\n }),\n typeof obj['message'] === 'string' ? `message:\\n${obj['message']}` : undefined,\n files.length > 0 ? `files:\\n${renderStringList(files)}` : undefined,\n ]);\n }\n\n if (toolName === 'glob' && Array.isArray(obj['files'])) {\n const files = stringArrayField(obj, 'files');\n return joinSections([\n renderHeader(\n `${toolName}: ${stringFromInput(input, 'pattern') ?? stringFromInput(input, 'files') ?? stringFromInput(input, 'path') ?? ''}`.trim(),\n {\n path: stringFromInput(input, 'path'),\n files: files.length,\n truncated: obj['truncated'],\n },\n ),\n renderStringList(files, '(no files)'),\n ]);\n }\n\n if (toolName === 'tree' && typeof obj['tree'] === 'string') {\n return joinSections([\n renderHeader(\n `tree: ${stringField(obj, 'path') ?? stringFromInput(input, 'path') ?? '<cwd>'}`,\n {\n total_files: obj['total_files'],\n total_dirs: obj['total_dirs'],\n truncated: obj['truncated'],\n },\n ),\n obj['tree'],\n ]);\n }\n\n if (toolName === 'fetch' && typeof obj['content'] === 'string') {\n return joinSections([\n renderHeader(\n `fetch: ${stringField(obj, 'url') ?? stringFromInput(input, 'url') ?? '<url>'}`,\n {\n status: obj['status'],\n content_type: obj['content_type'],\n },\n ),\n obj['content'],\n ]);\n }\n\n if (toolName === 'replace' && Array.isArray(obj['results'])) {\n const results = obj['results'].filter(isRecord);\n const sections: Array<string | undefined> = [\n renderHeader('replace', {\n files_modified: obj['files_modified'],\n total_replacements: obj['total_replacements'],\n dry_run: obj['dry_run'],\n }),\n ];\n for (const r of results.slice(0, DEFAULT_LIST_LIMIT)) {\n sections.push(\n joinSections([\n renderHeader(`file: ${stringField(r, 'path') ?? '<unknown>'}`, {\n replacements: r['replacements'],\n }),\n typeof r['diff'] === 'string' ? r['diff'] : undefined,\n ]),\n );\n }\n if (results.length > DEFAULT_LIST_LIMIT) {\n sections.push(`[serializer omitted ${results.length - DEFAULT_LIST_LIMIT} result item(s)]`);\n }\n return joinSections(sections);\n }\n\n if (typeof obj['diff'] === 'string') {\n const diff = obj['diff'];\n // matched_by: 'exact' is the default and carries no information \u2014 only\n // surface the field when a fallback tier actually fired.\n const matchedBy =\n typeof obj['matched_by'] === 'string' && obj['matched_by'] !== 'exact'\n ? obj['matched_by']\n : undefined;\n const syntaxErrors = Array.isArray(obj['syntax_errors'])\n ? obj['syntax_errors'].filter((e): e is string => typeof e === 'string')\n : [];\n return joinSections([\n renderHeader(toolName, {\n path: obj['path'],\n replacements: obj['replacements'],\n bytes_written: obj['bytes_written'],\n created: obj['created'],\n matched_by: matchedBy,\n note: obj['note'],\n files: Array.isArray(obj['files']) ? obj['files'].length : undefined,\n truncated: obj['truncated'],\n mode: obj['mode'],\n }),\n compactDiff(diff),\n syntaxErrors.length > 0 ? `syntax_errors:\\n${renderStringList(syntaxErrors)}` : undefined,\n ]);\n }\n\n if (toolName === 'test' && typeof obj['output'] === 'string') {\n return renderTestOutput(obj, input);\n }\n\n if (\n (toolName === 'typecheck' || toolName === 'lint' || toolName === 'format') &&\n typeof obj['output'] === 'string'\n ) {\n return renderVerifierOutput(toolName, obj, input);\n }\n\n if (hasCommandOutputShape(obj)) {\n return renderCommandOutput(toolName, obj, input);\n }\n\n if (toolName === 'json' && typeof obj['formatted'] === 'string') {\n return joinSections([\n renderHeader('json', {\n type: obj['type'],\n keys: Array.isArray(obj['keys']) ? obj['keys'].length : undefined,\n query: stringFromInput(input, 'query'),\n error: obj['error'],\n }),\n obj['formatted'],\n ]);\n }\n\n if (toolName === 'logs' && Array.isArray(obj['entries'])) {\n const entries = obj['entries'].filter(isRecord);\n const lines = entries.slice(0, LOG_ENTRY_LIMIT).map((entry) => {\n const ts = stringField(entry, 'timestamp') ?? '';\n const level = stringField(entry, 'level') ?? 'info';\n const message = stringField(entry, 'message') ?? '';\n const source = stringField(entry, 'source');\n return [ts, level, source, message].filter(Boolean).join(' ');\n });\n if (entries.length > LOG_ENTRY_LIMIT) {\n lines.push(`[serializer omitted ${entries.length - LOG_ENTRY_LIMIT} log entry item(s)]`);\n }\n return joinSections([\n renderHeader(`logs: ${stringField(obj, 'source') ?? '<source>'}`, {\n total: obj['total'],\n shown: Math.min(entries.length, LOG_ENTRY_LIMIT),\n truncated: obj['truncated'],\n stream_mode: obj['stream_mode'],\n }),\n lines.length > 0 ? lines.join('\\n') : '(no log entries)',\n ]);\n }\n\n if (toolName === 'audit' && Array.isArray(obj['vulnerabilities'])) {\n const vulns = obj['vulnerabilities'].filter(isRecord);\n const lines = vulns.slice(0, DEFAULT_LIST_LIMIT).map((v) => {\n const severity = stringField(v, 'severity') ?? 'unknown';\n const pkg = stringField(v, 'package') ?? '<package>';\n const title = stringField(v, 'title') ?? '';\n const url = stringField(v, 'url');\n return [severity, pkg, title, url].filter(Boolean).join(' | ');\n });\n if (vulns.length > DEFAULT_LIST_LIMIT) {\n lines.push(`[serializer omitted ${vulns.length - DEFAULT_LIST_LIMIT} vulnerability item(s)]`);\n }\n return joinSections([\n renderHeader('audit', {\n exit_code: obj['exit_code'],\n total: obj['total'],\n summary: obj['summary'],\n truncated: obj['truncated'],\n }),\n lines.length > 0 ? lines.join('\\n') : stringField(obj, 'output'),\n ]);\n }\n\n if (toolName === 'outdated' && Array.isArray(obj['packages'])) {\n const packages = obj['packages'].filter(isRecord);\n const lines = packages\n .slice(0, DEFAULT_LIST_LIMIT)\n .map((p) =>\n [\n stringField(p, 'name') ?? '<package>',\n `current=${stringField(p, 'current') ?? 'unknown'}`,\n `wanted=${stringField(p, 'wanted') ?? 'unknown'}`,\n `latest=${stringField(p, 'latest') ?? 'unknown'}`,\n stringField(p, 'type'),\n ]\n .filter(Boolean)\n .join(' | '),\n );\n if (packages.length > DEFAULT_LIST_LIMIT) {\n lines.push(`[serializer omitted ${packages.length - DEFAULT_LIST_LIMIT} package item(s)]`);\n }\n return joinSections([\n renderHeader('outdated', {\n exit_code: obj['exit_code'],\n total: obj['total'],\n truncated: obj['truncated'],\n }),\n lines.length > 0 ? lines.join('\\n') : stringField(obj, 'output'),\n ]);\n }\n\n return undefined;\n}\n\nfunction renderTestOutput(obj: RecordValue, input: unknown): string {\n const exitCode = numberField(obj, 'exit_code') ?? 0;\n const failed = numberField(obj, 'failed') ?? 0;\n const output = stringField(obj, 'output') ?? '';\n const header = renderHeader(`test: ${stringField(obj, 'runner') ?? 'runner'}`, {\n exit_code: obj['exit_code'],\n tests_run: obj['tests_run'],\n passed: obj['passed'],\n failed: obj['failed'],\n duration_ms: obj['duration_ms'],\n truncated: obj['truncated'],\n files: inputListSummary(input, 'files'),\n grep: stringFromInput(input, 'grep'),\n });\n\n if (exitCode === 0 && failed === 0) {\n return joinSections([\n header,\n joinSections([\n 'report:',\n `status=passed`,\n `tests_run=${obj['tests_run'] ?? 0}`,\n `passed=${obj['passed'] ?? 0}`,\n `failed=${obj['failed'] ?? 0}`,\n `duration_ms=${obj['duration_ms'] ?? 0}`,\n extractSpoolNote(output),\n ]),\n ]);\n }\n\n return joinSections([\n header,\n `error_context:\\n${compactFailureOutput(output || '(no runner output)')}`,\n ]);\n}\n\nfunction renderVerifierOutput(toolName: string, obj: RecordValue, input: unknown): string {\n const exitCode = numberField(obj, 'exit_code') ?? 0;\n const errors = numberField(obj, 'errors') ?? 0;\n const warnings = numberField(obj, 'warnings') ?? 0;\n const output = stringField(obj, 'output') ?? '';\n const changed = numberField(obj, 'files_changed') ?? 0;\n const header = renderHeader(toolName, {\n exit_code: obj['exit_code'],\n errors: obj['errors'],\n warnings: obj['warnings'],\n files_checked: obj['files_checked'],\n files_changed: obj['files_changed'],\n fix_applied: obj['fix_applied'],\n fixer: obj['fixer'],\n linter: obj['linter'],\n project: obj['project'],\n truncated: obj['truncated'],\n files: inputListSummary(input, 'files'),\n cwd: stringFromInput(input, 'cwd'),\n });\n\n if (exitCode === 0 && errors === 0 && (toolName !== 'format' || changed === 0)) {\n return joinSections([\n header,\n joinSections([\n 'report:',\n 'status=passed',\n `errors=${errors}`,\n `warnings=${warnings}`,\n toolName === 'format' ? `files_changed=${changed}` : undefined,\n extractSpoolNote(output),\n ]),\n ]);\n }\n\n if (exitCode === 0 && toolName === 'format') {\n return joinSections([\n header,\n joinSections([\n 'report:',\n 'status=changed',\n `files_changed=${changed}`,\n extractSpoolNote(output),\n ]),\n ]);\n }\n\n return joinSections([\n header,\n `error_context:\\n${compactFailureOutput(output || '(no verifier output)')}`,\n ]);\n}\n\nfunction renderGrepMatches(matches: string[], mode: string | undefined): string {\n if (matches.length === 0) return '(no matches)';\n if (mode === 'files_with_matches') return renderStringList(matches, '(no files)');\n if (mode === 'count') return renderStringList(matches, '(no counts)');\n\n const groups = new Map<string, string[]>();\n const passthrough: string[] = [];\n for (const match of matches) {\n const parsed = parseGrepContentLine(match);\n if (!parsed) {\n passthrough.push(match);\n continue;\n }\n const list = groups.get(parsed.file) ?? [];\n list.push(`${parsed.line}:${parsed.text}`);\n groups.set(parsed.file, list);\n }\n\n if (groups.size === 0) return renderStringList(matches, '(no matches)');\n\n const sections: string[] = [];\n let fileIndex = 0;\n for (const [file, lines] of groups) {\n fileIndex++;\n if (fileIndex > GREP_FILE_LIMIT) break;\n const shown = lines.slice(0, GREP_MATCHES_PER_FILE);\n sections.push(\n `${file} (${lines.length} match(es), showing ${shown.length})\\n${shown.join('\\n')}`,\n );\n }\n if (groups.size > GREP_FILE_LIMIT) {\n sections.push(`[serializer omitted ${groups.size - GREP_FILE_LIMIT} file group(s)]`);\n }\n if (passthrough.length > 0) {\n sections.push(`ungrouped:\\n${renderStringList(passthrough, '', 50)}`);\n }\n return sections.join('\\n');\n}\n\nfunction parseGrepContentLine(\n line: string,\n): { file: string; line: string; text: string } | undefined {\n const match = GREP_LINE_RE.exec(line);\n if (!match?.[1] || !match[2]) return undefined;\n return { file: match[1], line: match[2], text: match[3] ?? '' };\n}\n\nfunction compactDiff(diff: string): string {\n const lines = diff.split(/\\r?\\n/);\n if (lines.length <= DIFF_INLINE_LINE_LIMIT) return diff;\n\n const fileCount = Math.max(\n new Set(\n lines\n .map(\n (line) => /^diff --git\\s+a\\/(.+?)\\s+b\\//.exec(line)?.[1] ?? /^---\\s+(.+)/.exec(line)?.[1],\n )\n .filter(Boolean),\n ).size,\n 0,\n );\n const hunks = lines.filter((line) => line.startsWith('@@')).length;\n const added = lines.filter((line) => line.startsWith('+') && !line.startsWith('+++')).length;\n const removed = lines.filter((line) => line.startsWith('-') && !line.startsWith('---')).length;\n\n // Collect [start, end] intervals as we scan lines sequentially.\n // Intervals are naturally ordered by line index \u2014 no sort needed.\n const intervals: Array<[number, number]> = [];\n let hunkCount = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n if (line.startsWith('diff --git') || line.startsWith('--- ') || line.startsWith('+++ ')) {\n intervals.push([i, i]);\n continue;\n }\n if (!line.startsWith('@@')) continue;\n if (hunkCount >= DIFF_HUNK_LIMIT) continue;\n hunkCount++;\n intervals.push([i, Math.min(lines.length - 1, i + DIFF_HUNK_CONTEXT)]);\n }\n\n if (intervals.length === 0) {\n return joinSections([\n renderHeader('diff_summary', {\n files: fileCount,\n hunks,\n added,\n removed,\n lines: lines.length,\n }),\n lines.slice(0, DIFF_INLINE_LINE_LIMIT).join('\\n'),\n `[serializer omitted ${Math.max(0, lines.length - DIFF_INLINE_LINE_LIMIT)} diff line(s)]`,\n ]);\n }\n\n // Merge overlapping / adjacent intervals in a single O(n) pass.\n // Intervals are already in ascending order from the sequential scan.\n const merged: Array<[number, number]> = [intervals[0]!];\n for (let i = 1; i < intervals.length; i++) {\n const last = merged[merged.length - 1]!;\n const current = intervals[i]!;\n if (current[0] <= last[1] + 1) {\n last[1] = Math.max(last[1], current[1]);\n } else {\n merged.push(current);\n }\n }\n\n // Build excerpt from merged intervals \u2014 O(n), no sort.\n const excerpt: string[] = [];\n let prevLine = -1;\n for (const [start, end] of merged) {\n if (start > prevLine + 1) {\n const omitted = prevLine === -1 ? start : start - prevLine - 1;\n excerpt.push(`[serializer omitted ${omitted} diff line(s)]`);\n }\n for (let j = start; j <= end; j++) {\n excerpt.push(lines[j] ?? '');\n }\n prevLine = end;\n }\n\n const trailing = lines.length - prevLine - 1;\n if (trailing > 0) excerpt.push(`[serializer omitted ${trailing} trailing diff line(s)]`);\n\n return joinSections([\n renderHeader('diff_summary', {\n files: fileCount,\n hunks,\n shown_hunks: Math.min(hunks, DIFF_HUNK_LIMIT),\n added,\n removed,\n lines: lines.length,\n }),\n excerpt.join('\\n'),\n ]);\n}\n\nfunction compactFailureOutput(output: string): string {\n const lines = output.split(/\\r?\\n/);\n if (lines.length <= 260) return output.trimEnd();\n\n const selected = new Set<number>();\n const marker =\n /\\b(fail|failed|failure|error|exception|assertionerror|expected|received|actual|timeout|stack)\\b/i;\n let markerHits = 0;\n for (let i = 0; i < lines.length; i++) {\n if (!marker.test(lines[i] ?? '')) continue;\n markerHits++;\n for (let j = Math.max(0, i - 4); j <= Math.min(lines.length - 1, i + 10); j++) {\n selected.add(j);\n }\n }\n\n if (markerHits === 0) {\n return lines.slice(-220).join('\\n').trimEnd();\n }\n\n const ordered = [...selected].sort((a, b) => a - b);\n const out: string[] = [];\n let previous = -1;\n for (const index of ordered) {\n if (index > previous + 1) {\n const omitted = previous === -1 ? index : index - previous - 1;\n out.push(`[serializer omitted ${omitted} line(s)]`);\n }\n out.push(lines[index] ?? '');\n previous = index;\n }\n return out.join('\\n').trimEnd();\n}\n\nfunction extractSpoolNote(output: string): string | undefined {\n return output\n .split(/\\r?\\n/)\n .find((line) => line.startsWith('[output truncated') && line.includes('full'));\n}\n\nfunction hasCommandOutputShape(obj: RecordValue): boolean {\n return (\n typeof obj['stdout'] === 'string' ||\n typeof obj['stderr'] === 'string' ||\n typeof obj['output'] === 'string' ||\n typeof obj['exitCode'] === 'number' ||\n typeof obj['exit_code'] === 'number'\n );\n}\n\nfunction renderCommandOutput(toolName: string, obj: RecordValue, input: unknown): string {\n const command = stringField(obj, 'command') ?? stringFromInput(input, 'command');\n const args = stringArrayField(obj, 'args');\n const commandLine = command ? [command, ...args].join(' ') : undefined;\n const output = stringField(obj, 'output');\n const stdout = stringField(obj, 'stdout');\n const stderr = stringField(obj, 'stderr');\n return joinSections([\n renderHeader(commandLine ? `${toolName}: ${commandLine}` : toolName, {\n exit_code: obj['exit_code'] ?? obj['exitCode'],\n timed_out: obj['timed_out'],\n pid: obj['pid'],\n allowed: obj['allowed'],\n truncated: obj['truncated'],\n runner: obj['runner'],\n linter: obj['linter'],\n fixer: obj['fixer'],\n project: obj['project'],\n tests_run: obj['tests_run'],\n passed: obj['passed'],\n failed: obj['failed'],\n duration_ms: obj['duration_ms'],\n errors: obj['errors'],\n warnings: obj['warnings'],\n files_checked: obj['files_checked'],\n files_changed: obj['files_changed'],\n fix_applied: obj['fix_applied'],\n }),\n stringField(obj, 'error') ? `error:\\n${stringField(obj, 'error')}` : undefined,\n output ? `output:\\n${output}` : undefined,\n stdout ? `stdout:\\n${stdout}` : undefined,\n stderr ? `stderr:\\n${stderr}` : undefined,\n ]);\n}\n\nfunction renderGenericToolObject(toolName: string, obj: RecordValue): string {\n const scalars: RecordValue = {};\n const blocks: string[] = [];\n for (const [key, value] of Object.entries(obj)) {\n if (value === undefined) continue;\n if (isScalar(value)) {\n const inline = String(value);\n if (inline.length <= INLINE_LIMIT && !inline.includes('\\n')) {\n scalars[key] = value;\n } else {\n blocks.push(`${key}:\\n${inline}`);\n }\n continue;\n }\n if (Array.isArray(value)) {\n if (value.every((item) => typeof item === 'string')) {\n blocks.push(`${key}:\\n${renderStringList(value as string[])}`);\n } else {\n blocks.push(`${key}:\\n${renderUnknownList(value)}`);\n }\n continue;\n }\n blocks.push(`${key}: ${clipInline(oneLineJson(value))}`);\n }\n return joinSections([renderHeader(toolName, scalars), ...blocks]);\n}\n\nfunction renderHeader(label: string, fields: RecordValue): string {\n const parts = Object.entries(fields)\n .filter(([, value]) => value !== undefined && value !== null && value !== '')\n .map(([key, value]) => `${key}=${clipInline(formatInlineValue(value))}`);\n return parts.length > 0 ? `${label} (${parts.join(' ')})` : label;\n}\n\nfunction renderStringList(items: string[], empty = '', limit = DEFAULT_LIST_LIMIT): string {\n if (items.length === 0) return empty;\n const shown = items.slice(0, limit);\n const omitted = items.length - shown.length;\n return [\n ...shown,\n ...(omitted > 0\n ? [`[serializer omitted ${omitted} item(s); narrow the request for more]`]\n : []),\n ].join('\\n');\n}\n\nfunction renderUnknownList(items: unknown[], limit = DEFAULT_LIST_LIMIT): string {\n const shown = items.slice(0, limit).map((item) => clipInline(oneLineJson(item), 1_000));\n const omitted = items.length - shown.length;\n if (omitted > 0)\n shown.push(`[serializer omitted ${omitted} item(s); narrow the request for more]`);\n return shown.join('\\n');\n}\n\nfunction joinSections(sections: Array<string | undefined>): string {\n return sections\n .map((section) => (typeof section === 'string' ? section.trimEnd() : undefined))\n .filter((section): section is string => !!section)\n .join('\\n');\n}\n\nfunction formatInlineValue(value: unknown): string {\n /* v8 ignore next -- no renderHeader field is ever an array (all callers pass scalars) */\n if (Array.isArray(value)) return `[${value.map(formatInlineValue).join(',')}]`;\n if (isScalar(value)) return String(value);\n return oneLineJson(value);\n}\n\nfunction clipInline(value: string, max = INLINE_LIMIT): string {\n const compact = value.replace(/\\s+/g, ' ').trim();\n return compact.length <= max\n ? compact\n : `${compact.slice(0, max - 15)}...(${compact.length} chars)`;\n}\n\nfunction oneLineJson(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction stringField(obj: RecordValue, key: string): string | undefined {\n const value = obj[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction numberField(obj: RecordValue, key: string): number | undefined {\n const value = obj[key];\n return typeof value === 'number' ? value : undefined;\n}\n\nfunction stringArrayField(obj: RecordValue, key: string): string[] {\n const value = obj[key];\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === 'string')\n : [];\n}\n\nfunction stringFromInput(input: unknown, key: string): string | undefined {\n if (!isRecord(input)) return undefined;\n const value = input[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction numberFromInput(input: unknown, key: string): number | undefined {\n if (!isRecord(input)) return undefined;\n const value = input[key];\n return typeof value === 'number' ? value : undefined;\n}\n\nfunction inputListSummary(input: unknown, key: string): string | undefined {\n if (!isRecord(input)) return undefined;\n const value = input[key];\n if (typeof value === 'string') return value;\n if (Array.isArray(value)) return value.filter((item) => typeof item === 'string').join(',');\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is RecordValue {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isScalar(value: unknown): value is string | number | boolean | null {\n return value === null || ['string', 'number', 'boolean'].includes(typeof value);\n}\n\n/**\n * Render a tool result body for inclusion in the `tool.executed` event.\n * Tool outputs can be large (file dumps, command output); UIs only want a\n * preview line, so cap at ~400 chars with an ellipsis marker.\n */\nexport function truncateForEvent(content: string, max = 400): string {\n if (!content) return '';\n return content.length <= max ? content : `${content.slice(0, max - 1)}\u2026`;\n}\n\n/**\n * Derive size signals (bytes / tokens / lines) for the chip rendered beside\n * each tool result. Computed once over the FULL `content` BEFORE the\n * 400-char event preview is taken.\n *\n * - bytes: UTF-8 byte length (multi-byte aware).\n * - tokens: standard ~3.5 chars/token heuristic.\n * - lines: read prefixes lines with `<n>\u2192`; for shell/grep/logs we fall\n * back to a newline count. Undefined for tools without a line notion.\n */\nconst READ_LINE_PREFIX_RE = /^\\s*\\d+\u2192/gm;\n\nexport function sizeSignals(\n toolName: string | undefined,\n content: string,\n): { outputBytes: number; outputTokens: number; outputLines: number | undefined } {\n if (!content || content.length === 0) {\n return { outputBytes: 0, outputTokens: 0, outputLines: undefined };\n }\n const outputBytes = Buffer.byteLength(content, 'utf8');\n const outputTokens = Math.max(1, Math.round(outputBytes / 3.5));\n let outputLines: number | undefined;\n if (toolName === 'read') {\n READ_LINE_PREFIX_RE.lastIndex = 0;\n let count = 0;\n while (READ_LINE_PREFIX_RE.exec(content) !== null) count++;\n if (count > 0) outputLines = count;\n } else if (\n toolName === 'bash' ||\n toolName === 'shell' ||\n toolName === 'grep' ||\n toolName === 'logs'\n ) {\n let nl = 0;\n for (let i = 0; i < content.length; i++) if (content.charCodeAt(i) === 10) nl++;\n outputLines = nl + (content.endsWith('\\n') ? 0 : 1);\n }\n return { outputBytes, outputTokens, outputLines };\n}\n", "import type {\n ToolResultRenderMode,\n ToolResultRenderModeConfig,\n} from '../types/config.js';\nimport type { Tool } from '../types/tool.js';\n\nexport const DEFAULT_TOOL_RESULT_RENDER_MODE: ToolResultRenderMode = 'extend';\n\n/**\n * Normalize a raw value to a {@link ToolResultRenderMode}. Accepts the\n * canonical strings (`'extend' | 'simple'`) plus a few synonyms so the\n * slash command feels forgiving (`extended`/`full` \u2192 `extend`,\n * `short`/`brief` \u2192 `simple`). Returns `undefined` for anything else so\n * the caller can reject unknown input without throwing.\n */\nexport function normalizeToolResultRenderMode(value: unknown): ToolResultRenderMode | undefined {\n if (typeof value !== 'string') return undefined;\n const raw = value.trim().toLowerCase();\n if (raw === 'extend' || raw === 'extended' || raw === 'full') return 'extend';\n if (raw === 'simple' || raw === 'short' || raw === 'brief') return 'simple';\n return undefined;\n}\n\n/**\n * Look up the result-render mode for `toolName` from a config map. Falls\n * back to the default `'extend'` when the map is missing the entry.\n */\nexport function resolveToolResultRenderMode(\n modes: ToolResultRenderModeConfig | undefined,\n toolName: string,\n): ToolResultRenderMode {\n return normalizeToolResultRenderMode(modes?.[toolName]) ?? DEFAULT_TOOL_RESULT_RENDER_MODE;\n}\n\n/**\n * Subset of {@link import('../registry/tool-registry.js').ToolRegistry}\n * the result-render-mode setters need. Decouples this module from the\n * concrete registry class so it can be reused by tests and by tools\n * that wrap their own registry.\n */\nexport interface ToolResultRenderModeRegistryLike {\n get(name: string): Tool | undefined;\n setResultRenderMode?(name: string, mode: ToolResultRenderMode): boolean;\n applyResultRenderModes?(\n modes?: ToolResultRenderModeConfig,\n ): { applied: number; missing: string[] };\n getResultRenderMode?(name: string): ToolResultRenderMode;\n}\n\n/**\n * Set a single tool's result-render mode on a registry. Prefers the\n * registry's native accessor (so it can update any internal state, e.g.\n * usage caches); falls back to a no-op so callers stay decoupled from\n * the registry implementation.\n */\nexport function setToolResultRenderMode(\n registry: ToolResultRenderModeRegistryLike,\n name: string,\n mode: ToolResultRenderMode,\n): boolean {\n if (typeof registry.setResultRenderMode === 'function') {\n return registry.setResultRenderMode(name, mode);\n }\n return false;\n}\n\n/**\n * Look up the current result-render mode for a single tool. Returns the\n * registry's view if it has one, otherwise the default. This is what the\n * tool-executor calls on each tool invocation to decide whether the next\n * `writeToolResult` should be `simple` or `extend`.\n */\nexport function getToolResultRenderMode(\n registry: ToolResultRenderModeRegistryLike,\n name: string,\n): ToolResultRenderMode {\n return registry.getResultRenderMode?.(name) ?? DEFAULT_TOOL_RESULT_RENDER_MODE;\n}\n\n/**\n * Bulk-apply a config map (`tools.resultRenderMode`) to a registry.\n * Mirrors {@link import('./tool-description-mode.js').applyToolDescriptionModes}\n * for symmetry with the LLM-side description mode.\n */\nexport function applyToolResultRenderModes(\n registry: ToolResultRenderModeRegistryLike,\n modes?: ToolResultRenderModeConfig,\n): { applied: number; missing: string[] } {\n if (typeof registry.applyResultRenderModes === 'function') {\n return registry.applyResultRenderModes(modes);\n }\n\n const entries = Object.entries(modes ?? {});\n const missing: string[] = [];\n let applied = 0;\n for (const [name, rawMode] of entries) {\n const mode = normalizeToolResultRenderMode(rawMode);\n if (!mode) continue;\n if (setToolResultRenderMode(registry, name, mode)) applied++;\n else missing.push(name);\n }\n return { applied, missing };\n}", "const GLOB_METACHARACTERS = /[*?[\\]]/g;\n\nexport function escapeGlobSubject(value: string): string {\n return value.replace(GLOB_METACHARACTERS, (char) => `\\\\${char}`);\n}\n\nexport function normalizePathSubject(value: string): string {\n return escapeGlobSubject(value.replace(/\\\\/g, '/'));\n}\n\nexport function isPathSubjectKey(subjectKey: string): boolean {\n return subjectKey === 'path' || subjectKey === 'file' || subjectKey === 'files';\n}\n\nexport function subjectForToolInput(\n toolName: string,\n input: unknown,\n subjectKey?: string,\n): string | undefined {\n if (!input || typeof input !== 'object') return undefined;\n const obj = input as Record<string, unknown>;\n\n if (subjectKey) {\n const value = obj[subjectKey];\n if (typeof value === 'string') {\n return isPathSubjectKey(subjectKey) ? normalizePathSubject(value) : escapeGlobSubject(value);\n }\n }\n\n if (toolName === 'bash' && typeof obj.command === 'string') {\n return escapeGlobSubject(obj.command);\n }\n if (typeof obj.path === 'string') {\n return normalizePathSubject(obj.path);\n }\n if (typeof obj.url === 'string') {\n return escapeGlobSubject(obj.url);\n }\n if (typeof obj.name === 'string') {\n return escapeGlobSubject(obj.name);\n }\n return undefined;\n}\n", "import { randomBytes } from 'node:crypto';\n\n/**\n * Crockford base32 alphabet (excludes I, L, O, U to avoid ambiguity).\n */\nconst ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\nconst ENCODING_LEN = ENCODING.length;\nconst TIME_LEN = 10;\nconst RANDOM_LEN = 16;\n\nfunction encodeTime(now: number, len: number): string {\n let mod: number;\n let str = '';\n for (let i = len - 1; i >= 0; i--) {\n mod = now % ENCODING_LEN;\n str = ENCODING[mod] + str;\n now = (now - mod) / ENCODING_LEN;\n }\n return str;\n}\n\nfunction encodeRandom(len: number): string {\n const bytes = randomBytes(len);\n let str = '';\n for (let i = 0; i < len; i++) {\n str += ENCODING[(bytes[i] as number) % ENCODING_LEN];\n }\n return str;\n}\n\n/**\n * Generate a ULID \u2014 a 26-char Crockford-base32 identifier whose first 10 chars\n * encode the millisecond timestamp (so IDs sort lexicographically by creation\n * time) followed by 16 chars of randomness. Zero runtime dependencies.\n *\n * The codebase convention is \"IDs are ULIDs\"; use this for any new store key.\n */\nexport function ulid(seedTime: number = Date.now()): string {\n return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN);\n}\n\n/** True for a well-formed 26-char Crockford-base32 ULID. */\nexport function isUlid(value: string): boolean {\n if (value.length !== TIME_LEN + RANDOM_LEN) return false;\n for (const ch of value) {\n if (!ENCODING.includes(ch)) return false;\n }\n return true;\n}\n", "import { createHash } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\n/**\n * Path layout. All developer-level state lives in ~/.wrongstack/.\n * Per-project state is keyed by the canonical project root under\n * ~/.wrongstack/projects/<slug>/. Linked Git worktrees resolve to their main\n * checkout so coordination and durable state are shared across worktrees.\n *\n * The ONLY thing inside the project tree is the optional\n * .wrongstack/AGENTS.md (committed) and .wrongstack/skills/ (committed).\n */\n\nexport interface WstackPaths {\n /** ~/.wrongstack \u2014 global root. */\n globalRoot: string;\n /** Absolute project root. */\n projectRoot: string;\n /** Home directory (~) \u2014 base for foreign tool state (~/.codex, ~/.agents, \u2026). */\n homeDir: string;\n /**\n * ~/.wrongstack \u2014 directory for user-global stateful config files\n * (mode.json, theme.json, \u2026). Currently an alias for `globalRoot`;\n * separate name lets us split out per-OS XDG_CONFIG_HOME later\n * without rewriting callers.\n */\n configDir: string;\n /** ~/.wrongstack/config.json \u2014 bootstrap config (activeProfile + version). */\n globalConfig: string;\n /**\n * ~/.wrongstack/profiles \u2014 directory containing per-profile configs.\n * Each profile has its own subdirectory with a config.json.\n */\n profilesDir: string;\n /**\n * Resolve the config path for a named profile.\n * Returns ~/.wrongstack/profiles/<name>/config.json.\n */\n profileConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/statusline.json */\n profileStatuslineConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/mode.json */\n profileModeConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/provider-status.json */\n profileProviderStatus: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/update-cache.json */\n profileUpdateCache: (name: string) => string;\n /** ~/.wrongstack/.key \u2014 32 random bytes, mode 0600, AES-GCM key for the secret vault. */\n secretsKey: string;\n /** ~/.wrongstack/memory.md \u2014 user-global memory. */\n globalMemory: string;\n /** ~/.wrongstack/skills \u2014 user-global skills. */\n globalSkills: string;\n /** ~/.claude/skills \u2014 user-global skills from foreign coding agents (Claude Code, Codex, \u2026). Read-only. */\n globalClaudeSkills: string;\n /** ~/.wrongstack/design-kits \u2014 user-global Design Studio kits. */\n globalDesignKits: string;\n /** ~/.wrongstack/prompts \u2014 user-global prompt library. */\n globalPrompts: string;\n /** ~/.wrongstack/instructions \u2014 user-global system instruction overrides. */\n globalInstructions: string;\n /** ~/.wrongstack/prompt-usage.json \u2014 per-slug insert counts (recent/popular). */\n promptUsage: string;\n /** ~/.wrongstack/cache \u2014 fetched data (models.dev, etc.). */\n cacheDir: string;\n /** ~/.wrongstack/cache/models.dev.json */\n modelsCache: string;\n /** ~/.wrongstack/cache/models-overlay.json \u2014 cached curated overlay. */\n modelsOverlayCache: string;\n /**\n * Per-project codebase symbol index (SQLite). Lives under the global project\n * dir \u2014 NOT inside the repo \u2014 so it never clutters the working tree or needs\n * gitignoring. `~/.wrongstack/projects/<hash>/codebase-index`.\n */\n projectCodebaseIndex: string;\n /** ~/.wrongstack/history \u2014 REPL line history. */\n historyFile: string;\n /** ~/.wrongstack/logs/wrongstack.log */\n logFile: string;\n /** ~/.wrongstack/projects/<hash> */\n projectDir: string;\n /** ~/.wrongstack/projects/<hash>/memory.md */\n projectMemory: string;\n /** ~/.wrongstack/projects/<hash>/sessions */\n projectSessions: string;\n /** ~/.wrongstack/projects/<hash>/trust.json */\n projectTrust: string;\n /** ~/.wrongstack/projects/<hash>/meta.json */\n projectMeta: string;\n /** ~/.wrongstack/projects/<hash>/config.local.json \u2014 optional override */\n projectLocalConfig: string;\n /** <project>/.wrongstack/config.json \u2014 per-project settings (safe fields only).\n * This lives inside the project root so it can be gitignored or shared. */\n inProjectConfig: string;\n /** <project>/.wrongstack/AGENTS.md \u2014 committed project memory. */\n inProjectAgentsFile: string;\n /** <project>/.wrongstack/skills \u2014 committed project skills. */\n inProjectSkills: string;\n /** <project>/.claude/skills \u2014 project skills authored for foreign coding agents (Claude Code, \u2026). Read-only. */\n inProjectClaudeSkills: string;\n /** <project>/.wrongstack/prompts \u2014 committed project prompt library. */\n inProjectPrompts: string;\n /** <project>/.wrongstack/instructions \u2014 committed project instruction overrides. */\n inProjectInstructions: string;\n /** <project>/.wrongstack/design-kits \u2014 committed project Design Studio kits. */\n inProjectDesignKits: string;\n /** <project>/.wrongstack/worktrees \u2014 git worktrees for per-phase isolation (gitignored). */\n inProjectWorktrees: string;\n /** Stable hash for the canonical project root (shared by linked Git worktrees). */\n projectHash: string;\n /** Human-readable canonical project slug, shared by linked Git worktrees. */\n projectSlug: string;\n /** ~/.wrongstack/projects/<hash>/goal.json \u2014 goal persistence */\n projectGoal: string;\n /** ~/.wrongstack/projects/<hash>/input-history.json \u2014 TUI prompt input history */\n projectInputHistory: string;\n /** ~/.wrongstack/projects/<hash>/specs \u2014 SDD spec files */\n projectSpecs: string;\n /** ~/.wrongstack/projects/<hash>/task-graphs \u2014 SDD task graphs */\n projectTaskGraphs: string;\n /** ~/.wrongstack/projects/<hash>/sdd-session.json \u2014 SDD session state */\n projectSddSession: string;\n /** ~/.wrongstack/projects/<hash>/plan.json \u2014 plan persistence */\n projectPlan: string;\n /** ~/.wrongstack/projects/<hash>/autophase \u2014 Goal phase-graph JSON files (dir name kept for backward compat) */\n projectAutophase: string;\n /** ~/.wrongstack/projects/<hash>/sdd-boards \u2014 live SDD board snapshots + JSONL event logs */\n projectSddBoards: string;\n /** ~/.wrongstack/sync.json \u2014 CloudSync configuration */\n syncConfig: string;\n /** ~/.wrongstack/config-history \u2014 timestamped backups on every config write */\n configHistoryDir: string;\n /** Function to get the status.json path for a project given its hash. */\n projectStatus: (projectHash: string) => string;\n}\n\n/**\n * Resolve the stable project identity root used by global WrongStack state.\n *\n * A linked Git worktree has its own checkout path and a `.git` *file* that\n * points into `<main>/.git/worktrees/<name>`. Its `commondir` points back to\n * the main checkout's `.git` directory. Treating the linked checkout path as\n * the project identity would split one repository into multiple session,\n * registry, and mailbox directories \u2014 agents in different worktrees would be\n * unable to see or message each other.\n *\n * Project-local paths still use the caller's actual checkout. Only global\n * state identity is canonicalized. Non-Git projects, normal checkouts, Git\n * submodules, and separate-git-dir layouts keep their existing identity.\n */\nexport function canonicalProjectRoot(absRoot: string): string {\n const checkoutRoot = path.resolve(absRoot);\n const dotGit = path.join(checkoutRoot, '.git');\n\n try {\n if (!fs.statSync(dotGit).isFile()) return checkoutRoot;\n\n const gitDirLine = fs.readFileSync(dotGit, 'utf8').trim();\n const match = /^gitdir:\\s*(.+)$/i.exec(gitDirLine);\n if (!match?.[1]) return checkoutRoot;\n\n const gitDir = path.resolve(checkoutRoot, match[1].trim());\n const commonDirFile = path.join(gitDir, 'commondir');\n if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;\n\n const commonDir = path.resolve(gitDir, fs.readFileSync(commonDirFile, 'utf8').trim());\n // Linked worktrees created by Git share the main checkout's `.git` dir.\n // A submodule or --separate-git-dir layout may point elsewhere; do not\n // guess a working-tree root for those shapes.\n if (path.basename(commonDir).toLowerCase() !== '.git') return checkoutRoot;\n return path.dirname(commonDir);\n } catch {\n // Missing/malformed administrative files must never make path resolution\n // fail. Falling back preserves the pre-canonicalization behavior.\n return checkoutRoot;\n }\n}\n\nexport function projectHash(absRoot: string): string {\n return createHash('sha256').update(canonicalProjectRoot(absRoot)).digest('hex').slice(0, 12);\n}\n\n/**\n * Human-readable project directory name: slugified folder name + short hash\n * suffix for uniqueness. e.g. `wrongstack-a1b2c3` instead of `3024e5e6fa58`.\n */\nexport function projectSlug(absRoot: string): string {\n const identityRoot = canonicalProjectRoot(absRoot);\n const base = slugify(path.basename(identityRoot));\n const hash = createHash('sha256').update(identityRoot).digest('hex').slice(0, 6);\n return `${base}-${hash}`;\n}\n\n/** Turn a folder name into a filesystem-safe lowercase slug. */\nfunction slugify(name: string): string {\n return (\n name\n .toLowerCase()\n // Collapse any run of non-alphanumeric chars into a single hyphen.\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40) || 'project'\n );\n}\n\nexport interface WstackPathOptions {\n userHome?: string | undefined;\n projectRoot: string;\n /** Override the global root (e.g. for tests). Default: `${userHome}/.wrongstack`. */\n globalRoot?: string | undefined;\n}\n\n/**\n * The global `~/.wrongstack` root, honoring the `WRONGSTACK_HOME` env\n * override. The override exists so tests (and sandboxed runs) can redirect\n * ALL global state \u2014 config, secrets, logs, projects/, mailboxes \u2014 away from\n * the real user home. Before it existed, `pnpm test` booted runtimes against\n * the real `~/.wrongstack`: it read the user's real config.json (starting a\n * second live Telegram poller), appended to the real wrongstack.log, and left\n * ~20k orphaned fixture dirs under projects/.\n *\n * Every code path that wants the global dir must come through here (or\n * through `resolveWstackPaths`) instead of `path.join(os.homedir(), '.wrongstack')`.\n */\nexport function wstackGlobalRoot(): string {\n const fromEnv = process.env['WRONGSTACK_HOME'];\n if (fromEnv && fromEnv.trim().length > 0) return path.resolve(fromEnv);\n return path.join(os.homedir(), '.wrongstack');\n}\n\nexport function resolveWstackPaths(opts: WstackPathOptions): WstackPaths {\n // Precedence: explicit globalRoot > explicit userHome (callers/tests that\n // pass one expect paths under it) > WRONGSTACK_HOME env > real home dir.\n const globalRoot =\n opts.globalRoot ?? (opts.userHome ? path.join(opts.userHome, '.wrongstack') : wstackGlobalRoot());\n // Home dir for FOREIGN tool state (Claude Code's ~/.claude). Independent of\n // WRONGSTACK_HOME, which redirects only WrongStack state: a real user's\n // Claude skills live in their real home, but tests pass `userHome` to keep\n // both `.wrongstack` and `.claude` under a temp dir.\n const homeDir = opts.userHome ?? os.homedir();\n const hash = projectHash(opts.projectRoot);\n const slug = projectSlug(opts.projectRoot);\n const projectDir = path.join(globalRoot, 'projects', slug);\n return {\n globalRoot,\n projectRoot: opts.projectRoot,\n homeDir,\n configDir: globalRoot,\n globalConfig: path.join(globalRoot, 'config.json'),\n profilesDir: path.join(globalRoot, 'profiles'),\n profileConfig: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'config.json');\n },\n profileStatuslineConfig: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'statusline.json');\n },\n profileModeConfig: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'mode.json');\n },\n profileProviderStatus: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'provider-status.json');\n },\n profileUpdateCache: (name: string) => {\n const safe = name.replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_');\n return path.join(globalRoot, 'profiles', safe || 'default', 'update-cache.json');\n },\n secretsKey: path.join(globalRoot, '.key'),\n globalMemory: path.join(globalRoot, 'memory.md'),\n globalSkills: path.join(globalRoot, 'skills'),\n globalClaudeSkills: path.join(homeDir, '.claude', 'skills'),\n globalDesignKits: path.join(globalRoot, 'design-kits'),\n globalPrompts: path.join(globalRoot, 'prompts'),\n globalInstructions: path.join(globalRoot, 'instructions'),\n promptUsage: path.join(globalRoot, 'prompt-usage.json'),\n cacheDir: path.join(globalRoot, 'cache'),\n modelsCache: path.join(globalRoot, 'cache', 'models.dev.json'),\n modelsOverlayCache: path.join(globalRoot, 'cache', 'models-overlay.json'),\n historyFile: path.join(globalRoot, 'history'),\n logFile: path.join(globalRoot, 'logs', 'wrongstack.log'),\n projectDir,\n projectCodebaseIndex: path.join(projectDir, 'codebase-index'),\n projectMemory: path.join(projectDir, 'memory.md'),\n projectSessions: path.join(projectDir, 'sessions'),\n projectTrust: path.join(projectDir, 'trust.json'),\n projectMeta: path.join(projectDir, 'meta.json'),\n projectLocalConfig: path.join(projectDir, 'config.local.json'),\n inProjectConfig: path.join(opts.projectRoot, '.wrongstack', 'config.json'),\n inProjectAgentsFile: path.join(opts.projectRoot, '.wrongstack', 'AGENTS.md'),\n inProjectSkills: path.join(opts.projectRoot, '.wrongstack', 'skills'),\n inProjectClaudeSkills: path.join(opts.projectRoot, '.claude', 'skills'),\n inProjectPrompts: path.join(opts.projectRoot, '.wrongstack', 'prompts'),\n inProjectInstructions: path.join(opts.projectRoot, '.wrongstack', 'instructions'),\n inProjectDesignKits: path.join(opts.projectRoot, '.wrongstack', 'design-kits'),\n inProjectWorktrees: path.join(opts.projectRoot, '.wrongstack', 'worktrees'),\n projectHash: hash,\n projectSlug: slug,\n projectGoal: path.join(projectDir, 'goal.json'),\n projectInputHistory: path.join(projectDir, 'input-history.json'),\n projectSpecs: path.join(projectDir, 'specs'),\n projectTaskGraphs: path.join(projectDir, 'task-graphs'),\n projectSddSession: path.join(projectDir, 'sdd-session.json'),\n projectPlan: path.join(projectDir, 'plan.json'),\n projectAutophase: path.join(projectDir, 'autophase'),\n projectSddBoards: path.join(projectDir, 'sdd-boards'),\n syncConfig: path.join(globalRoot, 'sync.json'),\n configHistoryDir: path.join(globalRoot, 'config-history'),\n projectStatus: (projectHash: string) => path.join(globalRoot, 'projects', projectHash, 'status.json'),\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["stat", "resolve", "open", "fs", "path", "cached", "fs", "path", "path", "cached", "cached", "isAbsolute", "resolve", "walk", "stat", "cached", "path", "path", "resolve", "isRecord", "randomBytes", "createHash", "fs", "path", "slugify", "projectHash"]
7
7
  }