@wrongstack/core 0.292.1 → 0.293.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/coordination/index.js.map +2 -2
  2. package/dist/core/fallback-profile-manager.d.ts +0 -2
  3. package/dist/core/fallback-profile-manager.d.ts.map +1 -1
  4. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  5. package/dist/defaults/index.js +346 -317
  6. package/dist/defaults/index.js.map +4 -4
  7. package/dist/execution/compaction-core.d.ts +3 -0
  8. package/dist/execution/compaction-core.d.ts.map +1 -1
  9. package/dist/execution/compactor.d.ts +4 -0
  10. package/dist/execution/compactor.d.ts.map +1 -1
  11. package/dist/execution/index.js +133 -49
  12. package/dist/execution/index.js.map +4 -4
  13. package/dist/execution/intelligent-compactor.d.ts +5 -1
  14. package/dist/execution/intelligent-compactor.d.ts.map +1 -1
  15. package/dist/execution/selective-compactor.d.ts +4 -0
  16. package/dist/execution/selective-compactor.d.ts.map +1 -1
  17. package/dist/goal/phase-orchestrator.d.ts +4 -0
  18. package/dist/goal/phase-orchestrator.d.ts.map +1 -1
  19. package/dist/hooks/runner.d.ts +0 -2
  20. package/dist/hooks/runner.d.ts.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +116 -109
  24. package/dist/index.js.map +2 -2
  25. package/dist/models/alibaba-token-plan-catalog.d.ts +85 -0
  26. package/dist/models/alibaba-token-plan-catalog.d.ts.map +1 -0
  27. package/dist/models/index.d.ts +1 -0
  28. package/dist/models/index.d.ts.map +1 -1
  29. package/dist/models/index.js +200 -15
  30. package/dist/models/index.js.map +4 -4
  31. package/dist/models/llm-selector.d.ts +4 -0
  32. package/dist/models/llm-selector.d.ts.map +1 -1
  33. package/dist/models/models-registry.d.ts +8 -0
  34. package/dist/models/models-registry.d.ts.map +1 -1
  35. package/dist/security/index.js +0 -23
  36. package/dist/security/index.js.map +2 -2
  37. package/dist/security/permission-policy.d.ts +0 -23
  38. package/dist/security/permission-policy.d.ts.map +1 -1
  39. package/dist/storage/config-loader.d.ts.map +1 -1
  40. package/dist/storage/index.js +6 -5
  41. package/dist/storage/index.js.map +2 -2
  42. package/dist/tools/index.js.map +2 -2
  43. package/dist/types/config.d.ts +9 -16
  44. package/dist/types/config.d.ts.map +1 -1
  45. package/dist/types/index.js +2 -3
  46. package/dist/types/index.js.map +2 -2
  47. package/dist/utils/index.js +15 -0
  48. package/dist/utils/index.js.map +2 -2
  49. package/dist/utils/merge-models-payload.d.ts +9 -0
  50. package/dist/utils/merge-models-payload.d.ts.map +1 -1
  51. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/models/models-registry.ts", "../../src/utils/atomic-write.ts", "../../src/types/blocks.ts", "../../src/utils/error.ts", "../../src/utils/expect-defined.ts", "../../src/utils/token-estimate.ts", "../../src/utils/merge-models-payload.ts", "../../src/types/errors.ts", "../../src/models/mode-store.ts", "../../src/types/mode-prompts.ts", "../../src/types/mode.ts", "../../src/utils/compaction-preview.ts", "../../src/utils/instruction-file.ts", "../../src/models/llm-selector.ts", "../../src/models/codex-catalog.ts", "../../src/models/provider-model-resolve.ts", "../../src/models/model-intelligence.ts", "../../src/models/model-router.ts"],
4
- "sourcesContent": ["import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type {\n ModelsDevPayload,\n ModelsDevProvider,\n ModelsRegistry,\n ResolvedModel,\n ResolvedProvider,\n WireFamily,\n} from '../types/models-registry.js';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { mergeModelsPayload } from '../utils/merge-models-payload.js';\nimport { FetchError } from '../types/errors.js';\n\nconst DEFAULT_URL = 'https://models.dev/api.json';\n/** Env var to override the models.dev base URL (e.g. for self-hosted mirrors). */\nconst ENV_URL_KEY = 'WRONGSTACK_MODELS_DEV_URL';\nconst DEFAULT_TTL_SECONDS = 24 * 3600;\nconst DEFAULT_REFRESH_TIMEOUT_MS = 15_000;\n\ninterface CacheEnvelope {\n fetchedAt: string;\n url: string;\n payload: ModelsDevPayload;\n}\n\nexport interface DefaultModelsRegistryOptions {\n cacheFile: string;\n url?: string | undefined;\n ttlSeconds?: number | undefined;\n fetchImpl?: typeof fetch | undefined;\n /** Pre-seeded payload \u2014 useful for offline scenarios and tests. */\n seed?: ModelsDevPayload | undefined;\n /**\n * Maximum age in seconds for stale cache fallback when network fails.\n * Defaults to 7 days. Set to `Infinity` for full offline resilience\n * (risk: deprecated models, wrong pricing). Set to `0` to disable\n * stale fallback entirely.\n */\n maxStaleAgeSeconds?: number | undefined;\n /**\n * Timeout in milliseconds for the models.dev network fetch. When exceeded,\n * the fetch is aborted and cache/stale fallback is used instead.\n * Defaults to 15 seconds. Set to `0` to disable (infinite wait).\n */\n refreshTimeoutMs?: number | undefined;\n /**\n * Curated override payload deep-merged ON TOP of the models.dev base via\n * `mergeModelsPayload` \u2014 adds providers/models the base lacks and overrides\n * fields it gets wrong. Resolution order (first non-empty wins): this\n * in-memory `overlay` \u2192 `overlayUrl` (fetched, cached) \u2192 `overlayFile`\n * (bundled, read from disk). A missing/broken overlay degrades to `{}` and\n * never throws, so the base alone still works.\n */\n overlay?: ModelsDevPayload | undefined;\n /** GitHub-raw (or any) URL serving the curated overlay `providers.json`. */\n overlayUrl?: string | undefined;\n /** Path to the bundled overlay `providers.json` (offline floor). */\n overlayFile?: string | undefined;\n /** Cache file for the fetched `overlayUrl`. Defaults next to `cacheFile`. */\n overlayCacheFile?: string | undefined;\n}\n\n/**\n * The npm package each models.dev provider declares determines which wire\n * family WrongStack speaks. Anything not listed falls into `unsupported` and\n * can be enabled by registering a custom provider factory via a plugin.\n */\nconst FAMILY_BY_NPM: Record<string, WireFamily> = {\n '@ai-sdk/anthropic': 'anthropic',\n '@ai-sdk/google-vertex/anthropic': 'anthropic',\n '@ai-sdk/openai': 'openai',\n '@ai-sdk/openai-compatible': 'openai-compatible',\n '@ai-sdk/groq': 'openai-compatible',\n '@ai-sdk/xai': 'openai-compatible',\n '@ai-sdk/cerebras': 'openai-compatible',\n '@ai-sdk/togetherai': 'openai-compatible',\n '@ai-sdk/mistral': 'openai-compatible',\n '@ai-sdk/perplexity': 'openai-compatible',\n '@ai-sdk/deepinfra': 'openai-compatible',\n '@openrouter/ai-sdk-provider': 'openai-compatible',\n 'ai-gateway-provider': 'openai-compatible',\n '@ai-sdk/vercel': 'openai-compatible',\n '@ai-sdk/gateway': 'openai-compatible',\n '@aihubmix/ai-sdk-provider': 'openai-compatible',\n 'venice-ai-sdk-provider': 'openai-compatible',\n '@ai-sdk/deepseek': 'openai-compatible',\n '@ai-sdk/google': 'google',\n};\n\nconst FAMILY_BY_PROVIDER_ID: Partial<Record<string, WireFamily>> = {\n 'anthropic-oauth': 'anthropic-oauth',\n 'github-copilot': 'github-copilot',\n 'openai-codex': 'openai-codex',\n};\n\nexport function classifyFamily(npm: string | undefined): WireFamily {\n if (!npm) return 'unsupported';\n return FAMILY_BY_NPM[npm] ?? 'unsupported';\n}\n\nfunction classifyProviderFamily(p: ModelsDevProvider): WireFamily {\n const byNpm = classifyFamily(p.npm);\n return byNpm !== 'unsupported' ? byNpm : (FAMILY_BY_PROVIDER_ID[p.id] ?? 'unsupported');\n}\n\nexport class DefaultModelsRegistry implements ModelsRegistry {\n /** Merged (base + overlay) payload \u2014 what every reader sees. */\n private payload?: ModelsDevPayload | undefined;\n /** Memoised overlay payload (in-memory / fetched / file). */\n private overlayPayload?: ModelsDevPayload | undefined;\n /**\n * Extra providers injected at runtime via `mergeOverlay()` \u2014 e.g. an\n * openai-compatible server (omniroute, LiteLLM, \u2026) auto-discovered from its\n * `/v1/models` endpoint at boot. Applied LAST (on top of base + curated\n * overlay) and re-applied across `refresh()` so the discovered catalog\n * survives a models.dev refetch.\n */\n private extraOverlay?: ModelsDevPayload | undefined;\n private fetchedAt?: Date | undefined;\n private readonly cacheFile: string;\n private readonly url: string;\n private readonly ttlMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly seed?: ModelsDevPayload | undefined;\n private readonly maxStaleAgeMs: number;\n private readonly refreshTimeoutMs: number;\n private readonly overlay?: ModelsDevPayload | undefined;\n private readonly overlayUrl?: string | undefined;\n private readonly overlayFile?: string | undefined;\n private readonly overlayCacheFile?: string | undefined;\n\n constructor(opts: DefaultModelsRegistryOptions) {\n this.cacheFile = opts.cacheFile;\n this.url = opts.url ?? process.env[ENV_URL_KEY] ?? DEFAULT_URL;\n this.ttlMs = (opts.ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1000;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n this.seed = opts.seed;\n // Default max stale age: 7 days\n const maxStaleSeconds = opts.maxStaleAgeSeconds ?? 7 * 24 * 3600;\n this.maxStaleAgeMs = maxStaleSeconds * 1000;\n this.refreshTimeoutMs = opts.refreshTimeoutMs ?? DEFAULT_REFRESH_TIMEOUT_MS;\n this.overlay = opts.overlay;\n this.overlayUrl = opts.overlayUrl;\n this.overlayFile = opts.overlayFile;\n this.overlayCacheFile =\n opts.overlayCacheFile ??\n (opts.overlayUrl\n ? path.join(path.dirname(opts.cacheFile), 'models-overlay-cache.json')\n : undefined);\n }\n\n async load(opts: { force?: boolean | undefined } = {}): Promise<ModelsDevPayload> {\n if (this.payload && !opts.force) return this.payload;\n // A `seed` is treated as the complete, final payload \u2014 used for offline\n // scenarios and tests. It bypasses both the base fetch and the overlay.\n if (this.seed) {\n this.payload = this.seed;\n this.fetchedAt = new Date();\n return this.payload;\n }\n // Load the overlay first so base degradation can tell whether there is\n // actually curated data to serve when models.dev is unreachable.\n const overlay = await this.loadOverlay(opts);\n const base = await this.loadBase(opts, Object.keys(overlay).length > 0);\n this.payload = this.withExtraOverlay(mergeModelsPayload(base, overlay));\n return this.payload;\n }\n\n /**\n * Merge an additional provider payload on top of the resolved catalog. Used\n * for runtime-discovered openai-compatible providers. Remembered so it is\n * re-applied across `refresh()`. A no-op for an empty payload.\n */\n mergeOverlay(payload: ModelsDevPayload): void {\n if (!hasEntries(payload)) return;\n this.extraOverlay = this.extraOverlay\n ? mergeModelsPayload(this.extraOverlay, payload)\n : payload;\n if (this.payload) this.payload = mergeModelsPayload(this.payload, this.extraOverlay);\n }\n\n private withExtraOverlay(payload: ModelsDevPayload): ModelsDevPayload {\n return this.extraOverlay ? mergeModelsPayload(payload, this.extraOverlay) : payload;\n }\n\n /**\n * Load the models.dev base payload: fresh cache \u2192 network \u2192 stale cache.\n * On total failure, degrade to `{}` (so a non-empty overlay still drives\n * the catalog) rather than throwing \u2014 unless there's no curated overlay to\n * fall back on, in which case the original error propagates so pure-\n * models.dev setups still surface the problem.\n */\n private async loadBase(\n opts: { force?: boolean | undefined } = {},\n overlayAvailable = false,\n ): Promise<ModelsDevPayload> {\n if (!opts.force) {\n const cached = await this.readCacheAt(this.cacheFile);\n if (cached && this.isFresh(cached.fetchedAt)) {\n this.fetchedAt = new Date(cached.fetchedAt);\n return cached.payload;\n }\n }\n try {\n return await this.refreshBase();\n } catch (err) {\n // Network failed \u2014 fall back to stale cache if within maxStaleAgeMs.\n const cached = await this.readCacheAt(this.cacheFile);\n if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {\n this.fetchedAt = new Date(cached.fetchedAt);\n const ageSeconds = Math.floor((Date.now() - this.fetchedAt.getTime()) / 1000);\n // eslint-disable-next-line no-console -- user-visible operator warning\n console.warn(\n `ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); ` +\n `using stale cache from ${formatAge(ageSeconds)} ago. Run \\`wstack models refresh\\` to retry.`,\n );\n return cached.payload;\n }\n if (overlayAvailable) {\n // eslint-disable-next-line no-console -- one-line operator warning\n console.warn(\n `ModelsRegistry: models.dev unavailable (${toErrorMessage(\n err,\n )}); serving curated overlay only.`,\n );\n return {};\n }\n throw err;\n }\n }\n\n /** Fetch + cache the models.dev base. Throws on failure (used by `refresh`). */\n private async refreshBase(): Promise<ModelsDevPayload> {\n const controller = new AbortController();\n /* v8 ignore next -- timing: the abort callback only fires if the real fetch exceeds the timeout */\n const timeout = setTimeout(() => controller.abort(), this.refreshTimeoutMs);\n try {\n const res = await this.fetchImpl(this.url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n signal: controller.signal,\n });\n clearTimeout(timeout);\n if (!res.ok) {\n throw new FetchError({\n message: `ModelsRegistry: HTTP ${res.status} fetching ${this.url}`,\n status: res.status,\n context: { url: this.url, op: 'refreshModels' },\n });\n }\n const json = (await res.json()) as ModelsDevPayload;\n this.fetchedAt = new Date();\n const envelope: CacheEnvelope = {\n fetchedAt: this.fetchedAt.toISOString(),\n url: this.url,\n payload: json,\n };\n await atomicWrite(this.cacheFile, JSON.stringify(envelope));\n return json;\n } catch (err) {\n clearTimeout(timeout);\n if (err instanceof Error && err.name === 'AbortError') {\n throw new FetchError({\n message: `ModelsRegistry: fetch timed out after ${this.refreshTimeoutMs}ms`,\n status: 408,\n context: { url: this.url, op: 'refreshModels', timedOut: true },\n });\n }\n throw err;\n }\n }\n\n /**\n * Resolve the curated overlay, memoised. Order: in-memory `overlay` \u2192\n * fetched `overlayUrl` (cached, same TTL/stale rules) \u2192 `overlayFile` on\n * disk. Never throws \u2014 a missing/broken overlay yields `{}`.\n */\n private async loadOverlay(opts: { force?: boolean | undefined } = {}): Promise<ModelsDevPayload> {\n /* v8 ignore next -- unreachable: load() caches `payload` and short-circuits before re-calling loadOverlay non-forced */\n if (this.overlayPayload && !opts.force) return this.overlayPayload;\n if (hasEntries(this.overlay)) {\n this.overlayPayload = this.overlay;\n return this.overlayPayload;\n }\n const fetched = await this.loadOverlayFromUrl(opts);\n if (hasEntries(fetched)) {\n this.overlayPayload = fetched;\n return fetched;\n }\n const fromFile = await this.readOverlayFile();\n this.overlayPayload = fromFile ?? {};\n return this.overlayPayload;\n }\n\n private async loadOverlayFromUrl(opts: {\n force?: boolean | undefined;\n }): Promise<ModelsDevPayload | undefined> {\n if (!this.overlayUrl || !this.overlayCacheFile) return undefined;\n if (!opts.force) {\n const cached = await this.readCacheAt(this.overlayCacheFile);\n if (cached && this.isFresh(cached.fetchedAt)) return cached.payload;\n }\n try {\n const res = await this.fetchImpl(this.overlayUrl, {\n method: 'GET',\n headers: { accept: 'application/json' },\n });\n if (!res.ok)\n throw new FetchError({\n message: `HTTP ${res.status}`,\n status: res.status,\n context: { url: this.url, op: 'refreshModels' },\n });\n const json = (await res.json()) as ModelsDevPayload;\n const envelope: CacheEnvelope = {\n fetchedAt: new Date().toISOString(),\n url: this.overlayUrl,\n payload: json,\n };\n /* v8 ignore next -- best-effort: overlay-cache write failure is intentionally ignored */\n await atomicWrite(this.overlayCacheFile, JSON.stringify(envelope)).catch(() => {});\n return json;\n } catch {\n // Network/parse failure \u2014 fall back to stale overlay cache, then the\n // bundled file (handled by the caller).\n const cached = await this.readCacheAt(this.overlayCacheFile);\n if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {\n const ageSeconds = Math.floor((Date.now() - new Date(cached.fetchedAt).getTime()) / 1000);\n // eslint-disable-next-line no-console -- operator-visible warning\n console.warn(\n `ModelsRegistry: overlay unavailable; using stale overlay from ${formatAge(ageSeconds)} ago.`,\n );\n return cached.payload;\n }\n return undefined;\n }\n }\n\n private async readOverlayFile(): Promise<ModelsDevPayload | undefined> {\n if (!this.overlayFile) return undefined;\n try {\n const raw = await fs.readFile(this.overlayFile, 'utf8');\n return JSON.parse(raw) as ModelsDevPayload;\n } catch {\n return undefined;\n }\n }\n\n async refresh(): Promise<ModelsDevPayload> {\n // Refresh the models.dev base (throws on failure so `wstack models refresh`\n // can report it), then recompute the merged payload with a fresh overlay.\n const base = await this.refreshBase();\n const overlay = await this.loadOverlay({ force: true });\n this.payload = this.withExtraOverlay(mergeModelsPayload(base, overlay));\n return this.payload;\n }\n\n async listProviders(): Promise<ResolvedProvider[]> {\n const payload = await this.load();\n return Object.values(payload).map((p) => this.resolveProvider(p));\n }\n\n async getProvider(id: string): Promise<ResolvedProvider | undefined> {\n const payload = await this.load();\n const p = payload[id];\n return p ? this.resolveProvider(p) : undefined;\n }\n\n async getModel(providerId: string, modelId: string): Promise<ResolvedModel | undefined> {\n const provider = await this.getProvider(providerId);\n if (!provider) return undefined;\n const model = provider.models.find((m) => m.id === modelId);\n if (!model) return undefined;\n return {\n providerId,\n modelId,\n capabilities: {\n tools: model.tool_call ?? false,\n vision: Boolean(model.modalities?.input?.includes('image')),\n reasoning: model.reasoning ?? model.reasoningConfig !== undefined,\n maxContext: model.limit?.context ?? 0,\n maxOutput: model.limit?.output,\n knowledge: model.knowledge,\n reasoningConfig: model.reasoningConfig,\n },\n cost: model.cost,\n };\n }\n\n async suggestModel(providerId: string): Promise<string | undefined> {\n const provider = await this.getProvider(providerId);\n if (!provider || provider.models.length === 0) return undefined;\n const ranked = [...provider.models].sort((a, b) => {\n const at = a.release_date ?? a.last_updated ?? '';\n const bt = b.release_date ?? b.last_updated ?? '';\n return bt.localeCompare(at);\n });\n return ranked[0]?.id;\n }\n\n async ageSeconds(): Promise<number> {\n if (!this.fetchedAt) {\n const cached = await this.readCacheAt(this.cacheFile);\n if (!cached) return Number.POSITIVE_INFINITY;\n return (Date.now() - new Date(cached.fetchedAt).getTime()) / 1000;\n }\n return (Date.now() - this.fetchedAt.getTime()) / 1000;\n }\n\n private resolveProvider(p: ModelsDevProvider): ResolvedProvider {\n return {\n id: p.id,\n name: p.name,\n family: classifyProviderFamily(p),\n apiBase: p.api,\n envVars: p.env ?? [],\n doc: p.doc,\n models: Object.values(p.models ?? {}),\n npm: p.npm,\n };\n }\n\n private isFresh(fetchedAtIso: string): boolean {\n return Date.now() - new Date(fetchedAtIso).getTime() < this.ttlMs;\n }\n\n private isWithinMaxStaleAge(fetchedAtIso: string): boolean {\n return Date.now() - new Date(fetchedAtIso).getTime() < this.maxStaleAgeMs;\n }\n\n private async readCacheAt(file: string): Promise<CacheEnvelope | undefined> {\n try {\n const raw = await fs.readFile(file, 'utf8');\n return JSON.parse(raw) as CacheEnvelope;\n } catch {\n return undefined;\n }\n }\n\n /** Used by `wstack models refresh` to expose where the cache lives. */\n cacheLocation(): string {\n return path.resolve(this.cacheFile);\n }\n}\n\n/** Render a seconds-duration as a human-friendly \"Xh Ym\" or \"Xd\" string. */\nfunction formatAge(seconds: number): string {\n if (seconds < 60) return '<1m';\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;\n if (seconds < 86400) {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n return m > 0 ? `${h}h ${m}m` : `${h}h`;\n }\n return `${Math.floor(seconds / 86400)}d`;\n}\n\nfunction hasEntries(payload: ModelsDevPayload | undefined): payload is ModelsDevPayload {\n return payload !== undefined && Object.keys(payload).length > 0;\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", "export interface TextBlock {\n type: 'text';\n text: string;\n cache_control?: { type: 'ephemeral' | undefined };\n}\n\nexport interface ToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n /**\n * Provider-specific opaque metadata captured from the wire response.\n * Echoed back verbatim in the next request so providers that bind\n * extra state to function calls keep working. Example: Gemini's\n * `thoughtSignature` \u2014 required for tool-use turns with thinking\n * models, otherwise the next request fails with 400 \"Function call\n * is missing a thought_signature in functionCall parts\".\n *\n * Keys are namespaced by intent so multiple wires can coexist:\n * - `google.thoughtSignature` \u2014 Gemini signed-thought blob\n * Other providers can add their own keys without colliding.\n */\n providerMeta?: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock {\n type: 'tool_result';\n tool_use_id: string;\n /**\n * The original tool name. Useful for providers like Google Gemini that\n * need the tool name in `functionResponse.name` \u2014 the tool_use_id is\n * only a session-local identifier and is not stable across replays.\n * Always set by ToolExecutor; may be absent on manually-constructed blocks.\n */\n name?: string | undefined;\n content: string;\n is_error?: boolean | undefined;\n}\n\nexport interface ImageBlock {\n type: 'image';\n source: {\n type: 'base64' | 'url';\n media_type?: string | undefined;\n data?: string | undefined;\n url?: string | undefined;\n };\n}\n\n/**\n * Chain-of-thought / extended-thinking content emitted by the model.\n *\n * Both Anthropic extended thinking (`{type:'thinking', thinking, signature}`)\n * and DeepSeek reasoning mode (top-level `reasoning_content` on the assistant\n * message) require this content to be echoed back verbatim on the next\n * request, otherwise the provider returns 400:\n * - Anthropic: \"The `content[].thinking` in the thinking mode must be passed back\"\n * - DeepSeek: \"The `reasoning_content` in the thinking mode must be passed back\"\n *\n * `signature` is Anthropic-specific (an opaque integrity blob). DeepSeek\n * doesn't issue a signature \u2014 the field is absent for that provider.\n *\n * Per Anthropic, thinking blocks MUST appear before any text/tool_use blocks\n * in an assistant message. Stream builders preserve that order.\n */\nexport interface ThinkingBlock {\n type: 'thinking';\n thinking: string;\n signature?: string | undefined;\n providerMeta?: Record<string, unknown>;\n}\n\nexport type ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | ImageBlock | ThinkingBlock;\n\nexport function isTextBlock(b: ContentBlock): b is TextBlock {\n return b.type === 'text';\n}\nexport function isToolUseBlock(b: ContentBlock): b is ToolUseBlock {\n return b.type === 'tool_use';\n}\nexport function isToolResultBlock(b: ContentBlock): b is ToolResultBlock {\n return b.type === 'tool_result';\n}\nexport function isImageBlock(b: ContentBlock): b is ImageBlock {\n return b.type === 'image';\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 { 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 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", "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", "import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport type { Mode, ModeConfig, ModeManifest, ModeStore } from '../types/mode.js';\nimport { DEFAULT_MODES } from '../types/mode.js';\n\nexport class DefaultModeStore implements ModeStore {\n private activeModeId: string | null = null;\n private modes: Mode[];\n private configDir: string;\n\n constructor(config: ModeConfig) {\n this.configDir = config.directory;\n this.modes = [...DEFAULT_MODES];\n }\n\n async getActiveMode(): Promise<Mode | null> {\n if (!this.activeModeId) {\n await this.loadActiveMode();\n }\n if (!this.activeModeId) return null;\n return this.modes.find((m) => m.id === this.activeModeId) ?? null;\n }\n\n async setActiveMode(modeId: string | null): Promise<void> {\n this.activeModeId = modeId;\n await this.saveActiveMode();\n }\n\n async listModes(): Promise<Mode[]> {\n return [...this.modes];\n }\n\n async getMode(modeId: string): Promise<Mode | null> {\n return this.modes.find((m) => m.id === modeId) ?? null;\n }\n\n async addMode(mode: Mode): Promise<void> {\n const idx = this.modes.findIndex((m) => m.id === mode.id);\n if (idx >= 0) {\n this.modes[idx] = mode;\n } else {\n this.modes.push(mode);\n }\n }\n\n async removeMode(modeId: string): Promise<void> {\n const builtIn = DEFAULT_MODES.find((m) => m.id === modeId);\n if (builtIn) {\n throw new Error(`Cannot remove built-in mode \"${modeId}\"`);\n }\n this.modes = this.modes.filter((m) => m.id !== modeId);\n if (this.activeModeId === modeId) {\n this.activeModeId = null;\n await this.saveActiveMode();\n }\n }\n\n private async loadActiveMode(): Promise<void> {\n try {\n const configPath = path.join(this.configDir, 'mode.json');\n const content = await fs.readFile(configPath, 'utf8');\n const data = JSON.parse(content);\n this.activeModeId = data.activeMode ?? null;\n } catch {\n this.activeModeId = 'default';\n }\n }\n\n private async saveActiveMode(): Promise<void> {\n try {\n await fs.mkdir(this.configDir, { recursive: true });\n const configPath = path.join(this.configDir, 'mode.json');\n // atomicWrite: torn write would leave mode.json malformed and the\n // next load would silently reset to \"default\".\n await atomicWrite(\n configPath,\n JSON.stringify({ activeMode: this.activeModeId }, null, 2),\n );\n } catch {\n // ignore save errors\n }\n }\n}\n\nexport interface ModeLoaderOptions {\n projectModesDir?: string | undefined;\n userModesDir?: string | undefined;\n}\n\nexport async function loadProjectModes(modesDir: string): Promise<Mode[]> {\n const modes: Mode[] = [];\n try {\n const entries = await fs.readdir(modesDir);\n for (const entry of entries) {\n if (!entry.endsWith('.md') && !entry.endsWith('.txt')) continue;\n const filePath = path.join(modesDir, entry);\n const stat = await fs.stat(filePath);\n if (!stat.isFile()) continue;\n const content = await fs.readFile(filePath, 'utf8');\n const id = path.basename(entry, path.extname(entry));\n modes.push({\n id,\n name: id.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()),\n description: content.split('\\n')[0] ?? id,\n prompt: content,\n tags: ['project'],\n });\n }\n } catch {\n // no project modes\n }\n return modes;\n}\n\nexport async function loadUserModes(modesDir: string): Promise<Mode[]> {\n const modes: Mode[] = [];\n try {\n const manifestPath = path.join(modesDir, 'modes.json');\n const content = await fs.readFile(manifestPath, 'utf8');\n const manifest: ModeManifest = JSON.parse(content);\n for (const mode of manifest.modes) {\n modes.push(mode);\n }\n } catch {\n // no user modes\n }\n return modes;\n}\n", "import { readFileSync, statSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport function modePrompt(id: string): string {\n for (const dir of modePromptDirCandidates()) {\n try {\n return readFileSync(path.join(dir, `${id}.md`), 'utf8').trimEnd();\n } catch {\n // try next candidate\n }\n }\n return '';\n}\n\nfunction modePromptDirCandidates(): string[] {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, '../../instructions/modes'),\n path.resolve(here, '../instructions/modes'),\n path.resolve(here, 'instructions/modes'),\n ];\n return candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));\n}\n\nfunction isDirectory(candidate: string): boolean {\n try {\n return statSync(candidate).isDirectory();\n } catch {\n return false;\n }\n}\n", "import { modePrompt } from './mode-prompts.js';\nexport interface Mode {\n id: string;\n name: string;\n description: string;\n /** Additional prompt text injected into system prompt when mode is active */\n prompt: string;\n /** Tags for tool_search filtering */\n tags?: string[] | undefined;\n /** Tools that should be prioritized/highlighted when this mode is active */\n toolPreferences?: string[] | undefined;\n /**\n * Skill names that are particularly relevant to this mode. The system\n * prompt builder appends a \"Suggested skills\" note so the model knows\n * which domain knowledge to leverage first. Skill must exist in the\n * loaded skill set to appear.\n */\n suggestedSkills?: string[] | undefined;\n}\n\nexport interface ModeManifest {\n modes: Mode[];\n defaultMode?: string | undefined;\n}\n\nexport interface ModeStore {\n getActiveMode(): Promise<Mode | null>;\n setActiveMode(modeId: string | null): Promise<void>;\n listModes(): Promise<Mode[]>;\n getMode(modeId: string): Promise<Mode | null>;\n}\n\nexport interface ModeConfig {\n directory: string;\n}\n\nexport const DEFAULT_MODES: Mode[] = [\n {\n id: 'default',\n name: 'Default',\n description: 'Balanced general-purpose mode; use when no special token/coverage trade-off is needed',\n prompt: '',\n tags: ['general', 'balanced'],\n },\n {\n id: 'brief',\n name: 'Brief',\n description: 'Ultra-compact responses for low-context, high-speed work',\n prompt: modePrompt('brief'),\n tags: ['lite', 'fast', 'concise', 'token-saving'],\n toolPreferences: ['read', 'edit', 'bash'],\n suggestedSkills: [],\n },\n {\n id: 'review-lite',\n name: 'Review Lite',\n description: 'Token-saving code review: changed files only, top correctness/security risks',\n prompt: modePrompt('review-lite'),\n tags: ['lite', 'review', 'quality', 'token-saving'],\n toolPreferences: ['git', 'diff', 'read', 'grep'],\n suggestedSkills: ['bug-hunter', 'typescript-strict'],\n },\n {\n id: 'audit-lite',\n name: 'Audit Lite',\n description: 'Token-saving security triage for a small diff or named file',\n prompt: modePrompt('audit-lite'),\n tags: ['lite', 'security', 'audit', 'token-saving'],\n toolPreferences: ['grep', 'read', 'git'],\n suggestedSkills: ['security-scanner'],\n },\n {\n id: 'plan-lite',\n name: 'Plan Lite',\n description: 'Token-saving planning: 3-6 actionable steps, minimal design debate',\n prompt: modePrompt('plan-lite'),\n tags: ['lite', 'planning', 'architecture', 'token-saving'],\n toolPreferences: ['tree', 'glob', 'read', 'grep'],\n suggestedSkills: ['refactor-planner'],\n },\n {\n id: 'debug-lite',\n name: 'Debug Lite',\n description: 'Token-saving bug triage: one hypothesis, nearest evidence, narrow check',\n prompt: modePrompt('debug-lite'),\n tags: ['lite', 'debug', 'triage', 'token-saving'],\n toolPreferences: ['read', 'grep', 'test', 'logs'],\n suggestedSkills: ['bug-hunter'],\n },\n {\n id: 'test-lite',\n name: 'Test Lite',\n description: 'Token-saving tests: one focused regression or narrow verification target',\n prompt: modePrompt('test-lite'),\n tags: ['lite', 'testing', 'qa', 'token-saving'],\n toolPreferences: ['test', 'read', 'grep'],\n suggestedSkills: ['testing'],\n },\n {\n id: 'refactor-lite',\n name: 'Refactor Lite',\n description: 'Token-saving cleanup: small scoped behavior-preserving changes',\n prompt: modePrompt('refactor-lite'),\n tags: ['lite', 'refactor', 'token-saving'],\n toolPreferences: ['read', 'edit', 'test'],\n suggestedSkills: ['typescript-strict'],\n },\n {\n id: 'research-lite',\n name: 'Research Lite',\n description: 'Token-saving web research: one search, one authoritative fetch, short answer',\n prompt: modePrompt('research-lite'),\n tags: ['lite', 'research', 'web', 'token-saving'],\n toolPreferences: ['search', 'fetch'],\n suggestedSkills: ['research-web'],\n },\n {\n id: 'code-reviewer',\n name: 'Review Deep',\n description: 'Comprehensive code review across contracts, edge cases, lifecycle, errors, concurrency',\n prompt: modePrompt('code-reviewer'),\n tags: ['deep', 'review', 'quality', 'security'],\n toolPreferences: ['read', 'grep', 'git', 'diff', 'test'],\n suggestedSkills: ['bug-hunter', 'security-scanner', 'typescript-strict', 'testing'],\n },\n {\n id: 'code-auditor',\n name: 'Audit Deep',\n description: 'Comprehensive security audit with category coverage and exploitability notes',\n prompt: modePrompt('code-auditor'),\n tags: ['deep', 'security', 'audit', 'compliance'],\n toolPreferences: ['grep', 'read', 'audit', 'bash'],\n suggestedSkills: ['security-scanner', 'bug-hunter', 'audit-log'],\n },\n {\n id: 'architect',\n name: 'Architecture Deep',\n description: 'Comprehensive architecture and cross-module contract analysis',\n prompt: modePrompt('architect'),\n tags: ['deep', 'architecture', 'design', 'scalability'],\n toolPreferences: ['read', 'glob', 'tree', 'diff'],\n suggestedSkills: ['api-design', 'refactor-planner', 'node-modern', 'docker-deploy'],\n },\n {\n id: 'debugger',\n name: 'Debug Deep',\n description: 'Comprehensive root-cause analysis with traces, logs, assumptions, and verification',\n prompt: modePrompt('debugger'),\n tags: ['deep', 'debug', 'investigation', 'error-resolution'],\n toolPreferences: ['read', 'grep', 'bash', 'logs', 'test'],\n suggestedSkills: ['bug-hunter', 'audit-log', 'observability'],\n },\n {\n id: 'tester',\n name: 'Test Deep',\n description: 'Comprehensive QA mode for coverage, boundaries, isolation, and integration gaps',\n prompt: modePrompt('tester'),\n tags: ['deep', 'testing', 'qa', 'quality'],\n toolPreferences: ['read', 'grep', 'test', 'bash'],\n suggestedSkills: ['testing', 'bug-hunter', 'typescript-strict'],\n },\n {\n id: 'devops',\n name: 'DevOps Deep',\n description: 'Comprehensive infrastructure, deployment, observability, and operations review',\n prompt: modePrompt('devops'),\n tags: ['deep', 'devops', 'infrastructure', 'operations'],\n toolPreferences: ['read', 'bash', 'grep', 'logs', 'git'],\n suggestedSkills: ['docker-deploy', 'observability', 'security-scanner'],\n },\n {\n id: 'refactorer',\n name: 'Refactor Deep',\n description: 'Comprehensive modernization/refactor mode with contracts and verification discipline',\n prompt: modePrompt('refactorer'),\n tags: ['deep', 'refactor', 'modernization', 'improvement'],\n toolPreferences: ['read', 'edit', 'test', 'git', 'grep'],\n suggestedSkills: ['refactor-planner', 'typescript-strict', 'node-modern', 'testing'],\n },\n {\n id: 'ui-design',\n name: 'UI Design Deep',\n description: 'Comprehensive design-first frontend/mobile UI work with kit, tokens, and accessibility',\n prompt: modePrompt('ui-design'),\n tags: ['deep', 'ui', 'frontend', 'mobile', 'design'],\n toolPreferences: ['design', 'write', 'edit', 'read', 'scaffold'],\n suggestedSkills: ['react-modern'],\n },\n {\n id: 'teach',\n name: 'Teach Deep',\n description: 'Mentor mode with explanations, mental models, trade-offs, and takeaways',\n prompt: modePrompt('teach'),\n tags: ['deep', 'teaching', 'mentor', 'learning'],\n toolPreferences: ['read', 'edit', 'explain'],\n suggestedSkills: ['prompt-engineering', 'skill-creator', 'node-modern', 'typescript-strict'],\n },\n {\n id: 'research-web',\n name: 'Research Deep',\n description: 'Comprehensive current-data research with cross-checking and reusable findings',\n prompt: modePrompt('research-web'),\n tags: ['deep', 'research', 'web', 'current-data', 'up-to-date'],\n toolPreferences: ['search', 'fetch', 'context_manager'],\n suggestedSkills: ['research-web', 'tech-stack', 'node-modern', 'security-scanner', 'react-modern'],\n },\n];\n", "import { isTextBlock } from '../types/blocks.js';\nimport type { Message } from '../types/messages.js';\n\nconst PATH_HINT_PATTERN =\n /(?:(?:[A-Za-z]:)?[./\\\\]?[\\w@.-]+(?:[\\\\/][\\w@(). -]+)+\\.[A-Za-z0-9]{1,12})/g;\nconst PATH_BACKSLASH_PATTERN = /\\\\/g;\nconst PATH_TRIM_PATTERN = /^[\"'`]+|[\"'`),;:]+$/g;\nconst ERROR_LINE_PATTERN =\n /\\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\\b/i;\nconst NEWLINE_SPLIT_PATTERN = /\\r?\\n/;\nconst WHITESPACE_COLLAPSE_PATTERN = /\\s+/g;\n\n/**\n * Compact, information-dense representation of one message for selector and\n * summarizer calls. Kept in the neutral utils layer so models do not depend\n * on execution at runtime.\n */\nexport function buildCompactionPreview(message: Message, maxChars = 600): string {\n if (maxChars <= 0) return '';\n if (typeof message.content === 'string') {\n return truncatePreview(message.content, maxChars);\n }\n\n const parts: string[] = [];\n for (const block of message.content) {\n if (isTextBlock(block)) {\n const text = truncatePreview(block.text, maxChars);\n if (text) parts.push(text);\n continue;\n }\n if (block.type === 'tool_use') {\n const input = truncatePreview(safePreviewString(block.input), 220);\n parts.push(`[tool_use: ${block.name}${input ? `; input=${input}` : ''}]`);\n continue;\n }\n if (block.type === 'tool_result') {\n const raw = safePreviewString(block.content);\n const details: string[] = [block.is_error ? 'error' : 'ok'];\n if (block.name) details.push(`tool=${block.name}`);\n const files = extractPathHints(raw).slice(0, 5);\n if (files.length > 0) details.push(`files=${files.join(', ')}`);\n const error = firstErrorLine(raw);\n if (error) details.push(`error=${error}`);\n const sample = truncatePreview(raw, 260);\n if (sample) details.push(`sample=${sample}`);\n parts.push(`[tool_result: ${details.join('; ')}]`);\n continue;\n }\n parts.push(`[${block.type}]`);\n }\n return truncatePreview(parts.join(' '), maxChars);\n}\n\nfunction safePreviewString(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction truncatePreview(value: string, maxChars: number): string {\n const compact = value.replace(WHITESPACE_COLLAPSE_PATTERN, ' ').trim();\n if (compact.length <= maxChars) return compact;\n return `${compact.slice(0, Math.max(0, maxChars - 1))}\u2026`;\n}\n\nfunction extractPathHints(content: unknown): string[] {\n const text = safePreviewString(content);\n const out = new Set<string>();\n for (const match of text.matchAll(PATH_HINT_PATTERN)) {\n const clean = match[0]?.replace(PATH_BACKSLASH_PATTERN, '/').replace(PATH_TRIM_PATTERN, '');\n if (clean) out.add(clean);\n }\n return [...out];\n}\n\nfunction firstErrorLine(content: unknown): string | undefined {\n const text = safePreviewString(content);\n for (const line of text.split(NEWLINE_SPLIT_PATTERN)) {\n if (!ERROR_LINE_PATTERN.test(line)) continue;\n const trimmed = line.replace(WHITESPACE_COLLAPSE_PATTERN, ' ').trim();\n if (trimmed) return truncatePreview(trimmed, 220);\n }\n return undefined;\n}\n", "import { readFileSync, statSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Cache of resolved instruction text, keyed by the relative path. Bundled\n * instruction files are immutable for the process lifetime, so re-reading them\n * on every call (some callers do so per LLM request) is wasted disk I/O.\n */\nconst textCache = new Map<string, string>();\n\n/** Resolved once \u2014 the candidate roots only depend on this module's location. */\nlet rootCandidates: string[] | undefined;\n\nexport function readBundledInstructionText(relativePath: string): string {\n const cached = textCache.get(relativePath);\n if (cached !== undefined) return cached;\n\n let resolved = '';\n for (const root of instructionRootCandidates()) {\n try {\n resolved = readFileSync(path.join(root, relativePath), 'utf8').trimEnd();\n break;\n } catch {\n // try next candidate\n }\n }\n textCache.set(relativePath, resolved);\n return resolved;\n}\n\nexport function renderInstructionTemplate(\n template: string,\n values: Record<string, string>,\n): string {\n return template.replace(/\\{\\{\\s*([a-zA-Z0-9_.-]+)\\s*\\}\\}/g, (match, key: string) =>\n Object.hasOwn(values, key) ? (values[key] ?? '') : match,\n );\n}\n\nfunction instructionRootCandidates(): string[] {\n if (rootCandidates !== undefined) return rootCandidates;\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, '../../instructions'),\n path.resolve(here, '../instructions'),\n path.resolve(here, 'instructions'),\n ];\n rootCandidates = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));\n return rootCandidates;\n}\n\nfunction isDirectory(candidate: string): boolean {\n try {\n return statSync(candidate).isDirectory();\n } catch {\n return false;\n }\n}\n", "import { expectDefined } from '../utils/expect-defined.js';\nimport { estimateMessageTokens, estimateTextTokens } from '../utils/token-estimate.js';\nimport { isTextBlock } from '../types/blocks.js';\nimport type { Message } from '../types/messages.js';\nimport type { Provider, Request } from '../types/provider.js';\nimport type { MessageSelector, SelectorResult } from '../types/selector.js';\nimport type { OneShotOrchestrator } from '../execution/one-shot-llm.js';\nimport { buildCompactionPreview } from '../utils/compaction-preview.js';\nimport { readBundledInstructionText } from '../utils/instruction-file.js';\nexport interface LLMSelectorOptions {\n /** Provider used for the selector LLM call. Required. */\n provider: Provider;\n /** Model for the selector. Defaults to the provider's default model. */\n model?: string | undefined;\n /**\n * Maximum tokens to keep in context (target budget).\n * Selector will aim to keep total content below this.\n */\n maxContextTokens?: number | undefined;\n /**\n * Prompt instructing the selector how to behave.\n * Should guide the LLM on importance tiers and output format.\n */\n systemPrompt?: string | undefined;\n /**\n * Maximum output tokens for the selector LLM call.\n * Controls both the JSON response budget and the token reservation for the\n * history text budget calculation (default: 1024).\n */\n maxOutputTokens?: number | undefined;\n /**\n * OneShotOrchestrator for the selector LLM call. When set, uses it\n * instead of direct provider.complete(), gaining fallback chain support.\n */\n oneShotOrchestrator?: OneShotOrchestrator | undefined;\n}\n\nconst DEFAULT_SYSTEM_PROMPT = readBundledInstructionText('llm/llm-selector.md');\n\n/**\n * Format messages as a compact text dump for the selector LLM.\n * Uses token estimation (not character count) to budget the output,\n * so long sessions don't silently truncate the selector's view of history.\n */\nfunction formatMessages(messages: Message[], maxTokens = 2048): string {\n const previewChars = Math.max(\n 120,\n Math.min(600, Math.floor((maxTokens * 3.2) / Math.max(1, messages.length))),\n );\n const lines: Array<{ index: number; text: string; tokens: number }> = [];\n for (let i = 0; i < messages.length; i++) {\n const m = expectDefined(messages[i]);\n const role = m.role.padEnd(10, ' ');\n const line = `[${i}][${role}]: ${buildCompactionPreview(m, previewChars)}`;\n lines.push({ index: i, text: line, tokens: estimateTextTokens(line) });\n }\n\n const total = lines.reduce((sum, line) => sum + line.tokens, 0);\n if (total <= maxTokens) return lines.map((line) => line.text).join('\\n');\n\n // A selector must always see the live working tail. Fill the remainder from\n // the oldest side so long histories retain both the original constraints\n // and the current task, with an explicit marker for any omitted middle.\n const recentCount = Math.min(8, lines.length);\n const recentStart = lines.length - recentCount;\n const tail: typeof lines = [];\n let usedTokens = 0;\n for (let i = lines.length - 1; i >= recentStart; i--) {\n const line = expectDefined(lines[i]);\n if (tail.length >= 2 && usedTokens + line.tokens > Math.floor(maxTokens * 0.45)) continue;\n tail.unshift(line);\n usedTokens += line.tokens;\n }\n\n const head: typeof lines = [];\n for (let i = 0; i < recentStart; i++) {\n const line = expectDefined(lines[i]);\n if (usedTokens + line.tokens > maxTokens) break;\n head.push(line);\n usedTokens += line.tokens;\n }\n const omitted = Math.max(0, recentStart - head.length);\n const marker = omitted > 0 ? [`[\u2026 ${omitted} middle message(s) omitted from selector preview \u2026]`] : [];\n return [...head.map((line) => line.text), ...marker, ...tail.map((line) => line.text)].join('\\n');\n}\n\n/**\n * LLM-powered message selector. Calls a sub-LLM to analyze the\n * message history and produce a keep/collapse plan \u2014 more surgical\n * than fixed-window rules.\n */\nexport class LLMSelector implements MessageSelector {\n private readonly provider: Provider;\n private readonly model: string;\n private readonly maxContextTokens: number;\n private readonly systemPrompt: string;\n private readonly maxOutputTokens: number;\n private readonly oneShotOrchestrator?: OneShotOrchestrator | undefined;\n\n constructor(opts: LLMSelectorOptions) {\n this.provider = opts.provider;\n this.model = opts.model ?? 'unknown';\n if (\n this.model === 'unknown' &&\n (process.env['NODE_ENV'] === 'development' || process.env['WRONGSTACK_DEBUG'] === '1')\n ) {\n console.warn(\n '[LLMSelector] model not set \u2014 selector will use the provider default. Set `model` explicitly in LLMSelectorOptions to silence this warning.',\n );\n }\n this.maxContextTokens = opts.maxContextTokens ?? 40_000;\n this.systemPrompt = opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n this.maxOutputTokens = opts.maxOutputTokens ?? 1024;\n this.oneShotOrchestrator = opts.oneShotOrchestrator;\n }\n\n async select(messages: Message[], maxToKeep: number): Promise<SelectorResult> {\n const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);\n\n const totalTokens = estimateMessageTokens(messages);\n const systemText = `${this.systemPrompt}\\n\\nConversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):\\n`;\n // Reserve tokens for the system prefix and output (maxOutputTokens), then give the\n // rest to the formatted history so the selector sees the maximum possible context.\n const systemTokens = estimateTextTokens(systemText);\n const historyBudget = Math.max(512, effectiveBudget - systemTokens - this.maxOutputTokens);\n\n // Build a concise representation of the conversation within the token budget\n const historyText = formatMessages(messages, historyBudget);\n\n // Add instruction to stay within budget\n const budgetInstruction =\n totalTokens > effectiveBudget\n ? `\\n\\nIMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiveBudget}). You MUST collapse enough to fit. Prefer collapsing older/lower-importance ranges.`\n : '';\n\n const req: Request = {\n model: this.model,\n system: [{ type: 'text', text: systemText + budgetInstruction }],\n messages: [{ role: 'user', content: historyText }],\n maxTokens: this.maxOutputTokens,\n };\n\n let raw: string;\n const ac = new AbortController();\n try {\n if (this.oneShotOrchestrator) {\n const result = await this.oneShotOrchestrator.call({\n system: systemText + budgetInstruction,\n userPrompt: historyText,\n maxTokens: this.maxOutputTokens,\n timeoutMs: 30_000,\n });\n if (result.error) throw new Error(result.error);\n raw = result.text;\n } else {\n // 30-second timeout so a stuck selector LLM call can't hang the compactor.\n const timeoutSignal = AbortSignal.timeout(30_000);\n const res = await this.provider.complete(req, {\n signal: AbortSignal.any([ac.signal, timeoutSignal]),\n });\n const textBlocks = res.content.filter(isTextBlock);\n raw = textBlocks\n .map((b) => b.text)\n .join('\\n')\n .trim();\n }\n } catch (err) {\n if (err instanceof Error) {\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'llm_selector.call_failed',\n message: `selector call failed, using recency fallback: ${err.message}`,\n timestamp: new Date().toISOString(),\n }),\n );\n }\n return this.fallbackSelect(messages, effectiveBudget);\n } finally {\n ac.abort();\n }\n\n return this.parseSelectorOutput(raw, messages);\n }\n\n private fallbackSelect(messages: Message[], budget: number): SelectorResult {\n // Simple fallback: keep from the end until we hit budget\n const toKeep: SelectorResult['kept'] = [];\n const toCollapse: SelectorResult['collapsed'] = [];\n\n let tokenCount = 0;\n let startIdx = 0;\n\n // Scan from the end backwards\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = expectDefined(messages[i]);\n const cost = estimateMessageTokens([m]);\n\n if (tokenCount + cost <= budget) {\n tokenCount += cost;\n } else {\n startIdx = i + 1;\n break;\n }\n }\n\n if (startIdx > 0) {\n toCollapse.push({ from: 0, to: startIdx - 1 });\n }\n toKeep.push({ from: startIdx, to: messages.length - 1, importance: 'high' });\n\n return {\n kept: toKeep,\n collapsed: toCollapse,\n reasoning: `Fallback: kept last ${messages.length - startIdx} messages within ${budget} token budget`,\n };\n }\n\n /**\n * Parse and validate the raw LLM output into a SelectorResult.\n * Falls back to recency-based selection if the LLM output is malformed,\n * out-of-bounds, or internally inconsistent.\n */\n private parseSelectorOutput(raw: string, messages: Message[]): SelectorResult {\n const messageCount = messages.length;\n if (messageCount === 0) {\n return { kept: [], collapsed: [], reasoning: 'empty session' };\n }\n\n // Try to extract JSON from the response\n const jsonStart = raw.indexOf('{');\n const jsonEnd = raw.lastIndexOf('}');\n if (jsonStart === -1 || jsonEnd === -1) {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw.slice(jsonStart, jsonEnd + 1));\n } catch {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n\n const obj = parsed as Record<string, unknown>;\n const keptRaw =\n (obj.kept as Array<{ from: number; to: number; importance: string }> | undefined) ?? [];\n const collapsedRaw =\n (obj.collapsed as Array<{ from: number; to: number; summary?: string | undefined }> | undefined) ?? [];\n\n // Validate kept ranges \u2014 must be within [0, messageCount), from <= to\n const kept: SelectorResult['kept'] = [];\n for (const k of keptRaw) {\n if (\n typeof k.from !== 'number' ||\n typeof k.to !== 'number' ||\n k.from < 0 ||\n k.to >= messageCount ||\n k.from > k.to\n ) {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n kept.push({\n from: k.from,\n to: k.to,\n importance: (k.importance ?? 'medium') as 'critical' | 'high' | 'medium',\n });\n }\n\n // Validate collapsed ranges \u2014 same bounds check\n const collapsed: SelectorResult['collapsed'] = [];\n for (const c of collapsedRaw) {\n if (\n typeof c.from !== 'number' ||\n typeof c.to !== 'number' ||\n c.from < 0 ||\n c.to >= messageCount ||\n c.from > c.to\n ) {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n collapsed.push({ from: c.from, to: c.to, summary: c.summary });\n }\n\n // Check for overlaps: kept ranges must not overlap with each other or with collapsed ranges\n const allRanges: Array<{ from: number; to: number }> = [...kept, ...collapsed];\n for (let i = 0; i < allRanges.length; i++) {\n const a = allRanges[i];\n if (!a) continue;\n for (let j = i + 1; j < allRanges.length; j++) {\n const b = allRanges[j];\n if (!b) continue;\n // Overlap: a starts before b ends AND a ends after b starts\n if (a.from <= b.to && a.to >= b.from) {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n }\n }\n\n return {\n kept,\n collapsed,\n reasoning: typeof obj.reasoning === 'string' ? obj.reasoning : '',\n };\n }\n}\n", "/**\n * Offline **floor** for the ChatGPT \"Sign in with ChatGPT\" (`openai-codex`)\n * models, mirroring what the official Codex CLI / ChatGPT model picker shows.\n *\n * Source-of-truth precedence (live path wins):\n * 1. The curated overlay `packages/cli/data/providers.json` \u2014 provider\n * `openai-codex` \u2014 which is **synced from raw GitHub** + cached + bundled\n * offline by `DefaultModelsRegistry` (overlayUrl/overlayFile). Editing that\n * JSON and pushing updates every client without a code release; this is the\n * authoritative list whenever the registry overlay is wired (the CLI path).\n * 2. This hardcoded table \u2014 the last-resort floor used where no overlay is\n * available (a standalone registry without the bundled file) and by the CLI\n * auth-menu login flow, which needs a synchronous id list.\n *\n * Keep this in lockstep with the `openai-codex` block in `providers.json`; the\n * drift-guard test `packages/cli/tests/codex-catalog-overlay-sync.test.ts`\n * asserts they agree.\n *\n * Layer note: this lives in `core` (not the CLI auth-menu) so the model-list\n * resolver can read it without `core` depending on `cli`. The CLI's\n * `FALLBACK_CODEX_MODELS` is derived from this list.\n */\n\nexport interface CodexModelMeta {\n /** The wire id sent to the backend and stored in config (e.g. `gpt-5.5`). */\n id: string;\n /** Human-readable display name shown in pickers. */\n name: string;\n /** One-line capability blurb, matching the official Codex picker copy. */\n description: string;\n /** The recommended / latest model \u2014 tagged \"(current)\" in the official UI. */\n current?: boolean;\n}\n\n/**\n * Current ChatGPT sign-in models, newest first. The first entry is the\n * recommended default (`current`). Order is significant: callers that need a\n * default model id use `CODEX_MODELS[0]`.\n */\nexport const CODEX_MODELS: ReadonlyArray<CodexModelMeta> = [\n {\n id: 'gpt-5.6-sol',\n name: 'GPT-5.6 Sol',\n description: 'Latest frontier agentic coding model.',\n current: true,\n },\n {\n id: 'gpt-5.5',\n name: 'GPT-5.5',\n description: 'Frontier model for complex coding, research, and real-world work.',\n },\n {\n id: 'gpt-5.6-terra',\n name: 'GPT-5.6 Terra',\n description: 'Balanced agentic coding model for everyday work.',\n },\n {\n id: 'gpt-5.6-luna',\n name: 'GPT-5.6 Luna',\n description: 'Fast and affordable agentic coding model.',\n },\n {\n id: 'gpt-5.4',\n name: 'GPT-5.4',\n description: 'Strong model for everyday coding.',\n },\n {\n id: 'gpt-5.4-mini',\n name: 'GPT-5.4 Mini',\n description: 'Small, fast, and cost-efficient model for simpler coding tasks.',\n },\n {\n id: 'gpt-5.3-codex-spark',\n name: 'GPT-5.3 Codex Spark',\n description: 'Ultra-fast coding model.',\n },\n];\n\nconst BY_ID: ReadonlyMap<string, CodexModelMeta> = new Map(CODEX_MODELS.map((m) => [m.id, m]));\n\n/** Look up the canonical metadata for a Codex model id, or `undefined`. */\nexport function codexModelMeta(id: string): CodexModelMeta | undefined {\n return BY_ID.get(id);\n}\n", "import type { ModelsDevModel, ResolvedProvider } from '../types/models-registry.js';\nimport { CODEX_MODELS, codexModelMeta } from './codex-catalog.js';\n\n/**\n * A model descriptor shaped for the WebUI `provider.models` message. All\n * metadata fields are optional because OAuth / subscription providers that\n * models.dev doesn't list contribute only a bare id.\n */\nexport interface ProviderModelDescriptor {\n id: string;\n name: string;\n /** One-line capability blurb, when known (e.g. the Codex subscription models). */\n description?: string | undefined;\n releaseDate?: string | undefined;\n contextWindow?: number | undefined;\n inputCost?: number | undefined;\n outputCost?: number | undefined;\n capabilities: string[];\n}\n\n/** Map a models.dev catalog model to the WebUI descriptor shape. */\nexport function describeCatalogModel(m: ModelsDevModel): ProviderModelDescriptor {\n return {\n id: m.id,\n name: m.name,\n ...(m.description !== undefined ? { description: m.description } : {}),\n releaseDate: m.release_date,\n contextWindow: m.limit?.context,\n inputCost: m.cost?.input,\n outputCost: m.cost?.output,\n capabilities: [\n ...(m.tool_call ? ['tools'] : []),\n ...(m.reasoning ? ['reasoning'] : []),\n ...(m.modalities?.input?.includes('image') ? ['vision'] : []),\n ...(m.open_weights ? ['open_weights'] : []),\n ],\n };\n}\n\n/**\n * Resolve the model list to offer for a provider, merging a saved-config\n * allowlist with optional models.dev catalog metadata.\n *\n * The result is a **union** of all available sources \u2014 the saved allowlist\n * (if any), the catalog models (base + curated overlay), the sibling\n * catalog models (e.g. `openai` for `openai-codex`), and the offline Codex\n * catalog (when `providerHint === 'openai-codex'`). Each source is\n * deduplicated by model id; the saved allowlist ids appear first so the\n * user's explicit preferences float to the top.\n *\n * Priority:\n * 1. The saved `models` allowlist (if non-empty) \u2014 enriched with catalog\n * metadata or the offline Codex catalog name+description when a same-id\n * entry exists. These ids always come first in the result.\n * 2. Catalog models (base + layered `providers.json` overlay) not already\n * in the saved list \u2014 this ensures the curated overlay is *never* hidden\n * by a stale saved model list. An empty `[]` saved list (not `undefined`)\n * still shows the full catalog.\n * 3. Sibling catalog models (when `siblingCatalog` is provided) \u2014 e.g. the\n * `openai` models from models.dev for `openai-codex` subscribers. Merged\n * only when the sibling is a different provider from the primary catalog.\n * 4. Offline Codex catalog (`CODEX_MODELS`) for `openai-codex` \u2014 fallback\n * when both saved list and catalog are empty/unavailable. This guarantees\n * ChatGPT sign-in users always see the canonical model list even fully\n * offline.\n * 5. Otherwise an empty list \u2014 *never* an error. A provider the user saved\n * that is neither in the catalog, the curated overlay, nor the offline\n * Codex catalog simply has no suggestions yet; callers must not raise a\n * toast for that case (doing so produced the \"not found in catalog\"\n * notification flood when the WebUI model switcher lazy-loaded every\n * saved provider).\n */\nexport function resolveProviderModelList(\n savedModels: string[] | undefined,\n catalog: ResolvedProvider | undefined,\n /**\n * Provider id / wire family the list is being resolved for. Lets the\n * resolver fall back to a known offline catalog (currently the ChatGPT\n * `openai-codex` models) when the saved allowlist is empty AND the\n * models.dev catalog is unavailable \u2014 so deleting the models from config\n * never leaves the provider showing zero models.\n */\n providerHint?: string | undefined,\n /**\n * Optional sibling catalog \u2014 e.g. the `openai` catalog when resolving\n * `openai-codex` models. Merged alongside the primary `catalog` so users\n * of subscription/OAuth providers also see the wire-family models they\n * may have access to (OpenAI models for ChatGPT Codex subscribers).\n */\n siblingCatalog?: ResolvedProvider | undefined,\n): ProviderModelDescriptor[] {\n const byId = new Map((catalog?.models ?? []).map((m) => [m.id, m]));\n const seen = new Set<string>();\n const out: ProviderModelDescriptor[] = [];\n\n // 1. Saved model allowlist \u2014 user's explicit preferences come first.\n if (savedModels && savedModels.length > 0) {\n for (const id of savedModels) {\n seen.add(id);\n // OAuth / subscription ids (openai-codex) are absent from the models.dev\n // catalog, so layer their canonical name + description on top: enrich a\n // catalog hit with the blurb, or synthesize a full descriptor from it.\n const codex = codexModelMeta(id);\n const hit = byId.get(id);\n if (hit) {\n const described = describeCatalogModel(hit);\n out.push(codex ? { ...described, description: codex.description } : described);\n } else if (codex) {\n out.push({ id, name: codex.name, description: codex.description, capabilities: [] });\n } else {\n out.push({ id, name: id, capabilities: [] });\n }\n }\n }\n\n // 2. Catalog models (base + overlay) \u2014 merge in models not already listed,\n // so the curated overlay (e.g. openai-codex from providers.json) is\n // never hidden by a stale saved model list. This also ensures the\n // \"fetch models from server\" action in the WebUI never loses models.\n for (const [id, m] of byId) {\n if (!seen.has(id)) {\n out.push(describeCatalogModel(m));\n seen.add(id);\n }\n }\n\n // 2b. Sibling catalog models \u2014 e.g. OpenAI models for openai-codex users\n // who may have access through their subscription. Merged only when\n // the sibling provider is different from the primary catalog.\n if (siblingCatalog && siblingCatalog !== catalog) {\n for (const m of siblingCatalog.models) {\n if (!seen.has(m.id)) {\n out.push(describeCatalogModel(m));\n seen.add(m.id);\n }\n }\n }\n\n // 3. Offline fallback \u2014 when both saved list and catalog are empty, surface\n // the known Codex catalog so ChatGPT sign-in users always see models.\n if (providerHint === 'openai-codex') {\n for (const m of CODEX_MODELS) {\n if (!seen.has(m.id)) {\n out.push({ id: m.id, name: m.name, description: m.description, capabilities: [] });\n seen.add(m.id);\n }\n }\n }\n\n return out;\n}\n", "/**\n * Model Intelligence \u2014 knowledge base of model capabilities, strengths,\n * and task-type suitability. Powers automatic agent \u2192 model routing.\n *\n * Each entry records:\n * - Known strengths (what this model family excels at)\n * - Known weaknesses (what it struggles with)\n * - Best-for task types (coding, planning, security, docs, etc.)\n * - Cost tier (budget / standard / premium)\n *\n * This is a curated dataset updated as new models release. Falls back\n * gracefully for unknown models.\n */\n\nexport interface ModelProfile {\n /** Provider id (e.g. \"anthropic\", \"openai\"). */\n provider: string;\n /** Model id regex \u2014 matches partial ids for a model family. */\n pattern: RegExp;\n /** Human-readable model family name. */\n family: string;\n /** What this model is particularly good at. */\n strengths: string[];\n /** Known limitations. */\n weaknesses?: string[];\n /** Task types this model is the best choice for. Ordered by preference. */\n bestFor: TaskType[];\n /** Task types to avoid with this model (use a different one). */\n avoidFor?: TaskType[];\n /** Approximate cost tier. */\n costTier: 'budget' | 'standard' | 'premium';\n /** Approximate speed tier (relative to other models). */\n speedTier: 'fast' | 'normal' | 'slow';\n /** Minimum recommended context window. */\n minContext?: number;\n}\n\n/** Task categories that map to agent roles. */\nexport type TaskType =\n | 'coding' // general software development\n | 'planning' // architecture, design, strategy\n | 'security' // vulnerability scanning, security review\n | 'docs' // documentation, writing, explanation\n | 'testing' // test writing, test review\n | 'refactoring' // code restructuring, cleanup\n | 'debugging' // bug hunting, error tracing\n | 'data' // data analysis, JSON, DB queries\n | 'frontend' // React, UI, CSS\n | 'backend' // API, server, infrastructure\n | 'review' // code review, PR review\n | 'lightweight' // simple tasks, quick answers\n | 'general'; // fallback \u2014 any task\n\n/** Known model profiles. Patterns are tried in order; first match wins. */\nexport const MODEL_PROFILES: ModelProfile[] = [\n // \u2500\u2500 Anthropic \u2500\u2500\n {\n provider: 'anthropic',\n pattern: /claude-opus/i,\n family: 'Claude Opus',\n strengths: [\n 'complex reasoning',\n 'multi-step planning',\n 'nuanced judgment',\n 'long-form analysis',\n ],\n weaknesses: ['latency', 'cost'],\n bestFor: ['planning', 'security', 'debugging', 'review'],\n costTier: 'premium',\n speedTier: 'slow',\n },\n {\n provider: 'anthropic',\n pattern: /claude-haiku/i,\n family: 'Claude Haiku',\n strengths: ['speed', 'low cost', 'simple tasks'],\n weaknesses: ['complex reasoning', 'long context'],\n bestFor: ['lightweight', 'docs', 'frontend'],\n avoidFor: ['planning', 'security'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n\n // \u2500\u2500 OpenAI \u2500\u2500\n {\n provider: 'openai',\n pattern: /gpt-5|o3|o4/i,\n family: 'GPT-5 / o3 / o4',\n strengths: ['complex reasoning', 'coding', 'multi-modal'],\n bestFor: ['planning', 'coding', 'debugging', 'general'],\n costTier: 'premium',\n speedTier: 'normal',\n },\n {\n provider: 'openai',\n pattern: /gpt-4\\.1|gpt-4o/i,\n family: 'GPT-4.1 / 4o',\n strengths: ['coding', 'balanced reasoning', 'fast'],\n bestFor: ['coding', 'refactoring', 'backend', 'docs'],\n costTier: 'standard',\n speedTier: 'fast',\n },\n {\n provider: 'openai',\n pattern: /gpt-4o-mini/i,\n family: 'GPT-4o Mini',\n strengths: ['speed', 'low cost', 'simple tasks'],\n weaknesses: ['complex reasoning'],\n bestFor: ['lightweight', 'frontend', 'docs'],\n avoidFor: ['planning', 'security'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n\n // \u2500\u2500 Google \u2500\u2500\n {\n provider: 'google',\n pattern: /gemini-(?:2\\.5|3)/i,\n family: 'Gemini 2.5 / 3',\n strengths: ['long context', 'multi-modal', 'coding', 'reasoning'],\n bestFor: ['coding', 'planning', 'data', 'general'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n {\n provider: 'google',\n pattern: /gemini-2\\.0-flash|gemini-flash/i,\n family: 'Gemini Flash',\n strengths: ['speed', 'low cost', 'long context'],\n weaknesses: ['deep reasoning'],\n bestFor: ['lightweight', 'docs', 'frontend', 'data'],\n avoidFor: ['planning', 'security'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n\n // \u2500\u2500 DeepSeek \u2500\u2500\n {\n provider: 'deepseek',\n pattern: /deepseek-v3|deepseek-r1/i,\n family: 'DeepSeek V3 / R1',\n strengths: ['coding', 'math', 'reasoning', 'cost-effective'],\n bestFor: ['coding', 'refactoring', 'debugging', 'general'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n\n // \u2500\u2500 OpenRouter / catch-all \u2500\u2500\n {\n provider: 'openrouter',\n pattern: /.*/,\n family: 'OpenRouter (routed)',\n strengths: ['model variety', 'fallback'],\n bestFor: ['general'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n];\n\n/** Map task types to the agent roles that handle them. */\nexport const TASK_TO_ROLE: Record<TaskType, string[]> = {\n coding: ['executor', 'architect', 'bug-hunter'],\n planning: ['planner', 'architect', 'refactor-planner'],\n security: ['security-scanner', 'security-reviewer'],\n docs: ['document', 'simplifier'],\n testing: ['verifier', 'test', 'e2e'],\n refactoring: ['refactor-planner', 'refactor', 'simplifier'],\n debugging: ['debugger', 'bug-hunter', 'tracer'],\n data: ['analyst', 'data', 'database'],\n frontend: ['frontend', 'designer'],\n backend: ['backend', 'api', 'auth'],\n review: ['reviewer', 'code-reviewer', 'critic'],\n lightweight: ['executor', 'simplifier'],\n general: ['executor', 'analyst'],\n};\n\n/**\n * Infer the most likely task type from a task description and target role.\n * Uses keyword matching; falls back to the role's primary task type.\n */\nexport function inferTaskType(description: string, role?: string): TaskType {\n const d = description.toLowerCase();\n\n // Keyword \u2192 task type mapping\n const keywords: [RegExp, TaskType][] = [\n [/plan|architect|design|strategy|blueprint|system\\s*design/i, 'planning'],\n [/security|vuln|exploit|injection|auth/i, 'security'],\n [/doc|readme|explain|write\\s*(a|the)\\s*doc|tutorial/i, 'docs'],\n [/test|spec|assert|mock|coverage/i, 'testing'],\n [/refactor|clean\\s*up|restructure|simplify/i, 'refactoring'],\n [/bug|fix|debug|trace|crash|error/i, 'debugging'],\n [/data|json|sql|query|analyze|parse/i, 'data'],\n [/frontend|react|ui|css|component|jsx/i, 'frontend'],\n [/backend|api|server|endpoint|route|middleware/i, 'backend'],\n [/review|audit|inspect|check/i, 'review'],\n [/simple|quick|one-liner|trivial/i, 'lightweight'],\n ];\n\n for (const [re, taskType] of keywords) {\n if (re.test(d)) return taskType;\n }\n\n // Fallback: map role to best task type\n if (role) {\n for (const [taskType, roles] of Object.entries(TASK_TO_ROLE)) {\n if (roles.includes(role)) return taskType as TaskType;\n }\n }\n\n return 'general';\n}\n\n/**\n * Find the best matching model profile for a given provider + model id.\n */\nexport function findModelProfile(provider: string, modelId: string): ModelProfile | undefined {\n const candidates = MODEL_PROFILES.filter((p) => p.provider === provider);\n // Try exact match first, then pattern match\n for (const p of candidates) {\n if (p.pattern.test(modelId)) return p;\n }\n return undefined;\n}\n\n/**\n * Score a model for a given task type. Higher = better fit.\n * Returns 0-100.\n */\nexport function scoreModelForTask(profile: ModelProfile | undefined, taskType: TaskType): number {\n if (!profile) return 50; // unknown model \u2014 neutral\n\n // Explicitly avoided \u2192 very low score\n if (profile.avoidFor?.includes(taskType)) return 10;\n\n // Best-for match \u2192 high score\n const bestIdx = profile.bestFor.indexOf(taskType);\n if (bestIdx >= 0) {\n // Earlier in the list = better fit\n return 90 - bestIdx * 10;\n }\n\n // Cost/speed adjustments\n let score = 50;\n if (taskType === 'lightweight') {\n score += profile.costTier === 'budget' ? 30 : profile.costTier === 'standard' ? 15 : 0;\n score += profile.speedTier === 'fast' ? 20 : 0;\n }\n if (taskType === 'planning' || taskType === 'security') {\n score += profile.costTier === 'premium' ? 20 : 0;\n score += profile.speedTier === 'slow' ? 10 : 0; // slow = thorough\n }\n\n return score;\n}\n", "/**\n * ModelRouter \u2014 intelligent model selection for subagent delegation.\n *\n * Combines:\n * 1. User-configured model matrix (/setmodel overrides)\n * 2. Model intelligence profiles (strengths/weaknesses per model family)\n * 3. Provider availability (which models have API keys configured)\n * 4. Per-model cost tracking\n *\n * Usage:\n * const router = new ModelRouter({ matrix, config, profiles: MODEL_PROFILES });\n * const pick = router.pickForTask('security-scanner', 'Audit the auth module');\n * // \u2192 { provider: 'anthropic', model: 'matched-model', reason: 'best-for security' }\n */\n\nimport type { ModelMatrixEntry, ProviderConfig } from '../types/config.js';\n\nexport interface ModelIntelligenceEntry {\n provider: string;\n pattern: RegExp;\n family: string;\n strengths: string[];\n weaknesses?: string[];\n bestFor: string[];\n avoidFor?: string[];\n costTier: 'budget' | 'standard' | 'premium';\n speedTier: 'fast' | 'normal' | 'slow';\n minContext?: number;\n}\n\nexport interface RouterConfig {\n /** User-configured model matrix (from /setmodel). */\n matrix?: Record<string, ModelMatrixEntry> | undefined;\n /** Provider configurations (to check API key availability). */\n config: {\n provider: string;\n model: string;\n providers?: Record<string, ProviderConfig>;\n };\n /** Known model intelligence profiles. */\n profiles?: ModelIntelligenceEntry[] | undefined;\n}\n\nexport interface ModelPick {\n provider: string;\n model: string;\n /** Why this model was chosen. */\n reason: string;\n /** Whether this came from the user's matrix (true) or auto-detected (false). */\n fromMatrix: boolean;\n}\n\nexport interface RouterCosts {\n /** Cumulative cost per provider/model key. */\n byModel: Record<\n string,\n { cost: number; tokens: { input: number; output: number }; calls: number }\n >;\n /** Grand total. */\n totalCost: number;\n}\n\n/**\n * Default profiles used when none are provided. Kept inline so the router\n * is self-contained \u2014 callers can override with richer data from model-intelligence.\n */\nconst DEFAULT_PROFILES: ModelIntelligenceEntry[] = [\n {\n provider: 'anthropic',\n pattern: /claude-opus/i,\n family: 'Claude Opus',\n strengths: ['reasoning', 'planning'],\n bestFor: ['planning', 'security', 'debugging'],\n costTier: 'premium',\n speedTier: 'slow',\n },\n {\n provider: 'anthropic',\n pattern: /claude-haiku/i,\n family: 'Claude Haiku',\n strengths: ['speed'],\n bestFor: ['lightweight', 'docs'],\n avoidFor: ['planning'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n {\n provider: 'openai',\n pattern: /gpt-5|o3|o4/i,\n family: 'GPT-5/o3/o4',\n strengths: ['reasoning', 'coding'],\n bestFor: ['planning', 'coding', 'debugging'],\n costTier: 'premium',\n speedTier: 'normal',\n },\n {\n provider: 'openai',\n pattern: /gpt-4/i,\n family: 'GPT-4',\n strengths: ['coding'],\n bestFor: ['coding', 'docs'],\n costTier: 'standard',\n speedTier: 'fast',\n },\n {\n provider: 'openai',\n pattern: /gpt-4o-mini/i,\n family: 'GPT-4o Mini',\n strengths: ['speed'],\n bestFor: ['lightweight', 'docs'],\n avoidFor: ['planning'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n {\n provider: 'google',\n pattern: /gemini-(?:2\\.5|3)/i,\n family: 'Gemini 2.5/3',\n strengths: ['context', 'coding'],\n bestFor: ['coding', 'data'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n {\n provider: 'google',\n pattern: /gemini.*flash/i,\n family: 'Gemini Flash',\n strengths: ['speed'],\n bestFor: ['lightweight', 'docs'],\n avoidFor: ['planning'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n {\n provider: 'deepseek',\n pattern: /deepseek/i,\n family: 'DeepSeek',\n strengths: ['coding', 'cost-effective'],\n bestFor: ['coding', 'general'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n];\n\n/** Map common task keywords to categories for model matching. */\nconst TASK_CATEGORIES: Record<string, string> = {\n plan: 'planning',\n architect: 'planning',\n design: 'planning',\n strategy: 'planning',\n security: 'security',\n vuln: 'security',\n exploit: 'security',\n auth: 'security',\n doc: 'docs',\n readme: 'docs',\n explain: 'docs',\n write: 'docs',\n test: 'testing',\n spec: 'testing',\n assert: 'testing',\n coverage: 'testing',\n refactor: 'refactoring',\n cleanup: 'refactoring',\n restructure: 'refactoring',\n bug: 'debugging',\n fix: 'debugging',\n debug: 'debugging',\n trace: 'debugging',\n crash: 'debugging',\n data: 'data',\n json: 'data',\n sql: 'data',\n query: 'data',\n analyze: 'data',\n parse: 'data',\n frontend: 'frontend',\n react: 'frontend',\n ui: 'frontend',\n css: 'frontend',\n component: 'frontend',\n backend: 'backend',\n api: 'backend',\n server: 'backend',\n endpoint: 'backend',\n review: 'review',\n audit: 'review',\n inspect: 'review',\n simple: 'lightweight',\n quick: 'lightweight',\n trivial: 'lightweight',\n};\n\nexport class ModelRouter {\n private profiles: ModelIntelligenceEntry[];\n private matrix: Record<string, ModelMatrixEntry>;\n private config: RouterConfig['config'];\n private costs: RouterCosts = { byModel: {}, totalCost: 0 };\n\n constructor(opts: RouterConfig) {\n this.profiles = opts.profiles ?? DEFAULT_PROFILES;\n this.matrix = opts.matrix ?? {};\n this.config = opts.config;\n }\n\n /** Look up the model matrix resolution for a role (role \u2192 phase \u2192 * \u2192 leader). */\n private resolveMatrix(role: string): ModelMatrixEntry | undefined {\n // Exact role match\n if (this.matrix[role]) return this.matrix[role];\n // Phase match\n const phase = this.roleToPhase(role);\n if (phase && this.matrix[phase]) return this.matrix[phase];\n // Wildcard\n if (this.matrix['*']) return this.matrix['*'];\n return undefined;\n }\n\n /** Simplified phase lookup for common roles. */\n private roleToPhase(role: string): string | undefined {\n const phaseMap: Record<string, string> = {\n planner: 'plan',\n architect: 'plan',\n 'refactor-planner': 'plan',\n executor: 'code',\n refactor: 'code',\n simplifier: 'code',\n migration: 'code',\n 'bug-hunter': 'code',\n debugger: 'code',\n tracer: 'code',\n test: 'code',\n verifier: 'code',\n e2e: 'code',\n performance: 'code',\n chaos: 'code',\n 'security-scanner': 'review',\n 'security-reviewer': 'review',\n 'code-reviewer': 'review',\n reviewer: 'review',\n critic: 'review',\n accessibility: 'review',\n compliance: 'review',\n analyst: 'code',\n data: 'code',\n database: 'code',\n frontend: 'code',\n backend: 'code',\n api: 'code',\n auth: 'code',\n designer: 'code',\n document: 'code',\n researcher: 'plan',\n explore: 'plan',\n search: 'plan',\n };\n return phaseMap[role];\n }\n\n /**\n * Pick the best model for a task.\n *\n * Priority:\n * 1. User's /setmodel matrix entry for this role (explicit override)\n * 2. Best-matching model from intelligence profiles\n * 3. Leader model (fallback)\n */\n pickForTask(role: string, description: string): ModelPick {\n // 1. Check user matrix\n const matrixEntry = this.resolveMatrix(role);\n if (matrixEntry?.model) {\n const provider = matrixEntry.provider ?? this.config.provider;\n return {\n provider,\n model: matrixEntry.model,\n reason: `matrix override for role ${role}`,\n fromMatrix: true,\n };\n }\n\n // 2. Auto-detect from task description + role\n const category = this.inferCategory(description, role);\n const best = this.findBestModel(category);\n if (best) {\n return { ...best, reason: `best-for ${category} (auto-detected)`, fromMatrix: false };\n }\n\n // 3. Leader fallback\n return {\n provider: this.config.provider,\n model: this.config.model,\n reason: 'leader fallback',\n fromMatrix: false,\n };\n }\n\n /** Infer task category from description keywords + role. */\n private inferCategory(description: string, role: string): string {\n const d = description.toLowerCase();\n for (const [keyword, category] of Object.entries(TASK_CATEGORIES)) {\n if (d.includes(keyword)) return category;\n }\n // Map role to category\n const roleMap: Record<string, string> = {\n 'security-scanner': 'security',\n 'security-reviewer': 'security',\n 'bug-hunter': 'debugging',\n debugger: 'debugging',\n planner: 'planning',\n architect: 'planning',\n 'refactor-planner': 'refactoring',\n refactor: 'refactoring',\n test: 'testing',\n e2e: 'testing',\n verifier: 'testing',\n document: 'docs',\n simplifier: 'docs',\n 'code-reviewer': 'review',\n reviewer: 'review',\n critic: 'review',\n frontend: 'frontend',\n backend: 'backend',\n };\n return roleMap[role] ?? 'general';\n }\n\n /** Find the best available model for a task category. */\n private findBestModel(category: string): { provider: string; model: string } | undefined {\n // Score each profile for this category\n const scored = this.profiles\n .filter((p) => this.hasKey(p.provider))\n .map((p) => {\n let score = 50;\n if (p.bestFor.includes(category)) score += 40;\n if (p.avoidFor?.includes(category)) score -= 50;\n if (category === 'lightweight' && p.costTier === 'budget') score += 20;\n if (category === 'planning' && p.costTier === 'premium') score += 15;\n if (category === 'planning' && p.speedTier === 'slow') score += 10; // slow = thorough\n return { profile: p, score };\n })\n .filter((s) => s.score > 0)\n .sort((a, b) => b.score - a.score);\n\n if (scored.length === 0) return undefined;\n\n // Guarded by scored.length === 0 check above \u2014 but use optional\n // chaining instead of `!` to satisfy the non-null assertion lint rule.\n const best = scored[0]?.profile;\n if (!best) return undefined;\n // Find the actual model id from the provider's configured models\n const models = this.getProviderModels(best.provider);\n const match = models.find((m) => best.pattern.test(m)) ?? models[0];\n if (!match) return undefined;\n\n return { provider: best.provider, model: match };\n }\n\n /** Check if a provider has an API key configured. */\n private hasKey(providerId: string): boolean {\n const pc = this.config.providers?.[providerId];\n if (!pc) return providerId === this.config.provider;\n if (typeof pc.apiKey === 'string' && pc.apiKey.length > 0) return true;\n if (Array.isArray(pc.apiKeys) && pc.apiKeys.some((k) => k?.apiKey)) return true;\n return false;\n }\n\n /** Get the configured model list for a provider. */\n private getProviderModels(providerId: string): string[] {\n const pc = this.config.providers?.[providerId];\n return pc?.models ?? [];\n }\n\n /** Record cost for a model. */\n recordCost(\n provider: string,\n model: string,\n cost: number,\n tokens?: { input: number; output: number },\n ): void {\n const key = `${provider}/${model}`;\n const entry = this.costs.byModel[key] ?? { cost: 0, tokens: { input: 0, output: 0 }, calls: 0 };\n entry.cost += cost;\n if (tokens) {\n entry.tokens.input += tokens.input;\n entry.tokens.output += tokens.output;\n }\n entry.calls += 1;\n this.costs.byModel[key] = entry;\n this.costs.totalCost += cost;\n }\n\n /** Get cumulative costs. */\n getCosts(): RouterCosts {\n return { ...this.costs, byModel: { ...this.costs.byModel } };\n }\n\n /** Reset cost tracking. */\n resetCosts(): void {\n this.costs = { byModel: {}, totalCost: 0 };\n }\n\n /** List all available providers with their best model for each task category. */\n suggestMatrix(): Record<string, ModelPick> {\n const matrix: Record<string, ModelPick> = {};\n const roles = [\n 'security-scanner',\n 'bug-hunter',\n 'planner',\n 'architect',\n 'refactor-planner',\n 'test',\n 'document',\n 'code-reviewer',\n 'reviewer',\n 'verifier',\n 'executor',\n 'debugger',\n 'analyst',\n ];\n\n for (const role of roles) {\n // Don't override explicit user matrix entries\n if (this.matrix[role]) continue;\n\n const pick = this.pickForTask(role, role);\n if (!pick.fromMatrix) {\n matrix[role] = pick;\n }\n }\n\n return matrix;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["fs", "path", "stat", "resolve", "fs", "path", "path", "stat", "readFileSync", "statSync", "path", "fileURLToPath", "fileURLToPath", "isDirectory", "statSync"]
3
+ "sources": ["../../src/models/models-registry.ts", "../../src/utils/term.ts", "../../src/utils/color.ts", "../../src/infrastructure/logger.ts", "../../src/utils/atomic-write.ts", "../../src/types/blocks.ts", "../../src/utils/error.ts", "../../src/utils/expect-defined.ts", "../../src/utils/token-estimate.ts", "../../src/utils/merge-models-payload.ts", "../../src/types/errors.ts", "../../src/models/mode-store.ts", "../../src/types/mode-prompts.ts", "../../src/types/mode.ts", "../../src/utils/compaction-preview.ts", "../../src/utils/instruction-file.ts", "../../src/models/llm-selector.ts", "../../src/models/codex-catalog.ts", "../../src/models/provider-model-resolve.ts", "../../src/models/alibaba-token-plan-catalog.ts", "../../src/models/model-intelligence.ts", "../../src/models/model-router.ts"],
4
+ "sourcesContent": ["import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type {\n ModelsDevPayload,\n ModelsDevProvider,\n ModelsRegistry,\n ResolvedModel,\n ResolvedProvider,\n WireFamily,\n} from '../types/models-registry.js';\nimport type { Logger } from '../types/logger.js';\nimport { noOpLogger } from '../infrastructure/logger.js';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { mergeModelsPayload } from '../utils/merge-models-payload.js';\nimport { FetchError } from '../types/errors.js';\n\nconst DEFAULT_URL = 'https://models.dev/api.json';\n/** Env var to override the models.dev base URL (e.g. for self-hosted mirrors). */\nconst ENV_URL_KEY = 'WRONGSTACK_MODELS_DEV_URL';\nconst DEFAULT_TTL_SECONDS = 24 * 3600;\nconst DEFAULT_REFRESH_TIMEOUT_MS = 15_000;\n\ninterface CacheEnvelope {\n fetchedAt: string;\n url: string;\n payload: ModelsDevPayload;\n}\n\nexport interface DefaultModelsRegistryOptions {\n cacheFile: string;\n url?: string | undefined;\n ttlSeconds?: number | undefined;\n fetchImpl?: typeof fetch | undefined;\n /** Pre-seeded payload \u2014 useful for offline scenarios and tests. */\n seed?: ModelsDevPayload | undefined;\n /**\n * Maximum age in seconds for stale cache fallback when network fails.\n * Defaults to 7 days. Set to `Infinity` for full offline resilience\n * (risk: deprecated models, wrong pricing). Set to `0` to disable\n * stale fallback entirely.\n */\n maxStaleAgeSeconds?: number | undefined;\n /**\n * Timeout in milliseconds for the models.dev network fetch. When exceeded,\n * the fetch is aborted and cache/stale fallback is used instead.\n * Defaults to 15 seconds. Set to `0` to disable (infinite wait).\n */\n refreshTimeoutMs?: number | undefined;\n /**\n * Curated override payload deep-merged ON TOP of the models.dev base via\n * `mergeModelsPayload` \u2014 adds providers/models the base lacks and overrides\n * fields it gets wrong. Resolution order (first non-empty wins): this\n * in-memory `overlay` \u2192 `overlayUrl` (fetched, cached) \u2192 `overlayFile`\n * (bundled, read from disk). A missing/broken overlay degrades to `{}` and\n * never throws, so the base alone still works.\n */\n overlay?: ModelsDevPayload | undefined;\n /** GitHub-raw (or any) URL serving the curated overlay `providers.json`. */\n overlayUrl?: string | undefined;\n /** Path to the bundled overlay `providers.json` (offline floor). */\n overlayFile?: string | undefined;\n /** Cache file for the fetched `overlayUrl`. Defaults next to `cacheFile`. */\n overlayCacheFile?: string | undefined;\n /**\n * Structured logger. Defaults to noOpLogger (silent).\n * Callers pass a Logger to capture operator-visible diagnostics (cache\n * fallback warnings, overlay unavailability, etc.).\n */\n logger?: Logger | undefined;\n}\n\n/**\n * The npm package each models.dev provider declares determines which wire\n * family WrongStack speaks. Anything not listed falls into `unsupported` and\n * can be enabled by registering a custom provider factory via a plugin.\n */\nconst FAMILY_BY_NPM: Record<string, WireFamily> = {\n '@ai-sdk/anthropic': 'anthropic',\n '@ai-sdk/google-vertex/anthropic': 'anthropic',\n '@ai-sdk/openai': 'openai',\n '@ai-sdk/openai-compatible': 'openai-compatible',\n '@ai-sdk/groq': 'openai-compatible',\n '@ai-sdk/xai': 'openai-compatible',\n '@ai-sdk/cerebras': 'openai-compatible',\n '@ai-sdk/togetherai': 'openai-compatible',\n '@ai-sdk/mistral': 'openai-compatible',\n '@ai-sdk/perplexity': 'openai-compatible',\n '@ai-sdk/deepinfra': 'openai-compatible',\n '@openrouter/ai-sdk-provider': 'openai-compatible',\n 'ai-gateway-provider': 'openai-compatible',\n '@ai-sdk/vercel': 'openai-compatible',\n '@ai-sdk/gateway': 'openai-compatible',\n '@aihubmix/ai-sdk-provider': 'openai-compatible',\n 'venice-ai-sdk-provider': 'openai-compatible',\n '@ai-sdk/deepseek': 'openai-compatible',\n '@ai-sdk/google': 'google',\n};\n\nconst FAMILY_BY_PROVIDER_ID: Partial<Record<string, WireFamily>> = {\n 'anthropic-oauth': 'anthropic-oauth',\n 'github-copilot': 'github-copilot',\n 'openai-codex': 'openai-codex',\n};\n\nexport function classifyFamily(npm: string | undefined): WireFamily {\n if (!npm) return 'unsupported';\n return FAMILY_BY_NPM[npm] ?? 'unsupported';\n}\n\nfunction classifyProviderFamily(p: ModelsDevProvider): WireFamily {\n const byNpm = classifyFamily(p.npm);\n return byNpm !== 'unsupported' ? byNpm : (FAMILY_BY_PROVIDER_ID[p.id] ?? 'unsupported');\n}\n\nexport class DefaultModelsRegistry implements ModelsRegistry {\n /** Merged (base + overlay) payload \u2014 what every reader sees. */\n private payload?: ModelsDevPayload | undefined;\n /** Memoised overlay payload (in-memory / fetched / file). */\n private overlayPayload?: ModelsDevPayload | undefined;\n /**\n * Extra providers injected at runtime via `mergeOverlay()` \u2014 e.g. an\n * openai-compatible server (omniroute, LiteLLM, \u2026) auto-discovered from its\n * `/v1/models` endpoint at boot. Applied LAST (on top of base + curated\n * overlay) and re-applied across `refresh()` so the discovered catalog\n * survives a models.dev refetch.\n */\n private extraOverlay?: ModelsDevPayload | undefined;\n private fetchedAt?: Date | undefined;\n private readonly cacheFile: string;\n private readonly url: string;\n private readonly ttlMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly seed?: ModelsDevPayload | undefined;\n private readonly maxStaleAgeMs: number;\n private readonly refreshTimeoutMs: number;\n private readonly overlay?: ModelsDevPayload | undefined;\n private readonly overlayUrl?: string | undefined;\n private readonly overlayFile?: string | undefined;\n private readonly overlayCacheFile?: string | undefined;\n private readonly logger: Logger;\n\n constructor(opts: DefaultModelsRegistryOptions) {\n this.cacheFile = opts.cacheFile;\n this.url = opts.url ?? process.env[ENV_URL_KEY] ?? DEFAULT_URL;\n this.ttlMs = (opts.ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1000;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n this.seed = opts.seed;\n // Default max stale age: 7 days\n const maxStaleSeconds = opts.maxStaleAgeSeconds ?? 7 * 24 * 3600;\n this.maxStaleAgeMs = maxStaleSeconds * 1000;\n this.refreshTimeoutMs = opts.refreshTimeoutMs ?? DEFAULT_REFRESH_TIMEOUT_MS;\n this.overlay = opts.overlay;\n this.overlayUrl = opts.overlayUrl;\n this.overlayFile = opts.overlayFile;\n this.overlayCacheFile =\n opts.overlayCacheFile ??\n (opts.overlayUrl\n ? path.join(path.dirname(opts.cacheFile), 'models-overlay-cache.json')\n : undefined);\n this.logger = opts.logger ?? noOpLogger;\n }\n\n async load(opts: { force?: boolean | undefined } = {}): Promise<ModelsDevPayload> {\n if (this.payload && !opts.force) return this.payload;\n // A `seed` is treated as the complete, final payload \u2014 used for offline\n // scenarios and tests. It bypasses both the base fetch and the overlay.\n if (this.seed) {\n this.payload = this.seed;\n this.fetchedAt = new Date();\n return this.payload;\n }\n // Load the overlay first so base degradation can tell whether there is\n // actually curated data to serve when models.dev is unreachable.\n const overlay = await this.loadOverlay(opts);\n const base = await this.loadBase(opts, Object.keys(overlay).length > 0);\n this.payload = this.withExtraOverlay(mergeModelsPayload(base, overlay));\n return this.payload;\n }\n\n /**\n * Merge an additional provider payload on top of the resolved catalog. Used\n * for runtime-discovered openai-compatible providers. Remembered so it is\n * re-applied across `refresh()`. A no-op for an empty payload.\n */\n mergeOverlay(payload: ModelsDevPayload): void {\n if (!hasEntries(payload)) return;\n this.extraOverlay = this.extraOverlay\n ? mergeModelsPayload(this.extraOverlay, payload)\n : payload;\n if (this.payload) this.payload = mergeModelsPayload(this.payload, this.extraOverlay);\n }\n\n private withExtraOverlay(payload: ModelsDevPayload): ModelsDevPayload {\n return this.extraOverlay ? mergeModelsPayload(payload, this.extraOverlay) : payload;\n }\n\n /**\n * Load the models.dev base payload: fresh cache \u2192 network \u2192 stale cache.\n * On total failure, degrade to `{}` (so a non-empty overlay still drives\n * the catalog) rather than throwing \u2014 unless there's no curated overlay to\n * fall back on, in which case the original error propagates so pure-\n * models.dev setups still surface the problem.\n */\n private async loadBase(\n opts: { force?: boolean | undefined } = {},\n overlayAvailable = false,\n ): Promise<ModelsDevPayload> {\n if (!opts.force) {\n const cached = await this.readCacheAt(this.cacheFile);\n if (cached && this.isFresh(cached.fetchedAt)) {\n this.fetchedAt = new Date(cached.fetchedAt);\n return cached.payload;\n }\n }\n try {\n return await this.refreshBase();\n } catch (err) {\n // Network failed \u2014 fall back to stale cache if within maxStaleAgeMs.\n const cached = await this.readCacheAt(this.cacheFile);\n if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {\n this.fetchedAt = new Date(cached.fetchedAt);\n const ageSeconds = Math.floor((Date.now() - this.fetchedAt.getTime()) / 1000);\n this.logger.warn(\n `ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); ` +\n `using stale cache from ${formatAge(ageSeconds)} ago. Run \\`wstack models refresh\\` to retry.`,\n { event: 'models_registry.stale_cache_fallback' },\n );\n return cached.payload;\n }\n if (overlayAvailable) {\n this.logger.warn(\n `ModelsRegistry: models.dev unavailable (${toErrorMessage(\n err,\n )}); serving curated overlay only.`,\n { event: 'models_registry.overlay_only_fallback' },\n );\n return {};\n }\n throw err;\n }\n }\n\n /** Fetch + cache the models.dev base. Throws on failure (used by `refresh`). */\n private async refreshBase(): Promise<ModelsDevPayload> {\n const controller = new AbortController();\n /* v8 ignore next -- timing: the abort callback only fires if the real fetch exceeds the timeout */\n const timeout = setTimeout(() => controller.abort(), this.refreshTimeoutMs);\n try {\n const res = await this.fetchImpl(this.url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n signal: controller.signal,\n });\n clearTimeout(timeout);\n if (!res.ok) {\n throw new FetchError({\n message: `ModelsRegistry: HTTP ${res.status} fetching ${this.url}`,\n status: res.status,\n context: { url: this.url, op: 'refreshModels' },\n });\n }\n const json = (await res.json()) as ModelsDevPayload;\n this.fetchedAt = new Date();\n const envelope: CacheEnvelope = {\n fetchedAt: this.fetchedAt.toISOString(),\n url: this.url,\n payload: json,\n };\n await atomicWrite(this.cacheFile, JSON.stringify(envelope));\n return json;\n } catch (err) {\n clearTimeout(timeout);\n if (err instanceof Error && err.name === 'AbortError') {\n throw new FetchError({\n message: `ModelsRegistry: fetch timed out after ${this.refreshTimeoutMs}ms`,\n status: 408,\n context: { url: this.url, op: 'refreshModels', timedOut: true },\n });\n }\n throw err;\n }\n }\n\n /**\n * Resolve the curated overlay, memoised. Order: in-memory `overlay` \u2192\n * fetched `overlayUrl` (cached, same TTL/stale rules) \u2192 `overlayFile` on\n * disk. Never throws \u2014 a missing/broken overlay yields `{}`.\n */\n private async loadOverlay(opts: { force?: boolean | undefined } = {}): Promise<ModelsDevPayload> {\n /* v8 ignore next -- unreachable: load() caches `payload` and short-circuits before re-calling loadOverlay non-forced */\n if (this.overlayPayload && !opts.force) return this.overlayPayload;\n if (hasEntries(this.overlay)) {\n this.overlayPayload = this.overlay;\n return this.overlayPayload;\n }\n const fetched = await this.loadOverlayFromUrl(opts);\n if (hasEntries(fetched)) {\n this.overlayPayload = fetched;\n return fetched;\n }\n const fromFile = await this.readOverlayFile();\n this.overlayPayload = fromFile ?? {};\n return this.overlayPayload;\n }\n\n private async loadOverlayFromUrl(opts: {\n force?: boolean | undefined;\n }): Promise<ModelsDevPayload | undefined> {\n if (!this.overlayUrl || !this.overlayCacheFile) return undefined;\n if (!opts.force) {\n const cached = await this.readCacheAt(this.overlayCacheFile);\n if (cached && this.isFresh(cached.fetchedAt)) return cached.payload;\n }\n try {\n const res = await this.fetchImpl(this.overlayUrl, {\n method: 'GET',\n headers: { accept: 'application/json' },\n });\n if (!res.ok)\n throw new FetchError({\n message: `HTTP ${res.status}`,\n status: res.status,\n context: { url: this.url, op: 'refreshModels' },\n });\n const json = (await res.json()) as ModelsDevPayload;\n const envelope: CacheEnvelope = {\n fetchedAt: new Date().toISOString(),\n url: this.overlayUrl,\n payload: json,\n };\n /* v8 ignore next -- best-effort: overlay-cache write failure is intentionally ignored */\n await atomicWrite(this.overlayCacheFile, JSON.stringify(envelope)).catch(() => {});\n return json;\n } catch {\n // Network/parse failure \u2014 fall back to stale overlay cache, then the\n // bundled file (handled by the caller).\n const cached = await this.readCacheAt(this.overlayCacheFile);\n if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {\n const ageSeconds = Math.floor((Date.now() - new Date(cached.fetchedAt).getTime()) / 1000);\n this.logger.warn(\n `ModelsRegistry: overlay unavailable; using stale overlay from ${formatAge(ageSeconds)} ago.`,\n { event: 'models_registry.overlay_stale_fallback', ageSeconds },\n );\n return cached.payload;\n }\n return undefined;\n }\n }\n\n private async readOverlayFile(): Promise<ModelsDevPayload | undefined> {\n if (!this.overlayFile) return undefined;\n try {\n const raw = await fs.readFile(this.overlayFile, 'utf8');\n return JSON.parse(raw) as ModelsDevPayload;\n } catch {\n return undefined;\n }\n }\n\n async refresh(): Promise<ModelsDevPayload> {\n // Refresh the models.dev base (throws on failure so `wstack models refresh`\n // can report it), then recompute the merged payload with a fresh overlay.\n const base = await this.refreshBase();\n const overlay = await this.loadOverlay({ force: true });\n this.payload = this.withExtraOverlay(mergeModelsPayload(base, overlay));\n return this.payload;\n }\n\n async listProviders(): Promise<ResolvedProvider[]> {\n const payload = await this.load();\n return Object.values(payload).map((p) => this.resolveProvider(p));\n }\n\n async getProvider(id: string): Promise<ResolvedProvider | undefined> {\n const payload = await this.load();\n const p = payload[id];\n return p ? this.resolveProvider(p) : undefined;\n }\n\n async getModel(providerId: string, modelId: string): Promise<ResolvedModel | undefined> {\n const provider = await this.getProvider(providerId);\n if (!provider) return undefined;\n const model = provider.models.find((m) => m.id === modelId);\n if (!model) return undefined;\n return {\n providerId,\n modelId,\n capabilities: {\n tools: model.tool_call ?? false,\n vision: Boolean(model.modalities?.input?.includes('image')),\n reasoning: model.reasoning ?? model.reasoningConfig !== undefined,\n maxContext: model.limit?.context ?? 0,\n maxOutput: model.limit?.output,\n knowledge: model.knowledge,\n reasoningConfig: model.reasoningConfig,\n },\n cost: model.cost,\n };\n }\n\n async suggestModel(providerId: string): Promise<string | undefined> {\n const provider = await this.getProvider(providerId);\n if (!provider || provider.models.length === 0) return undefined;\n const ranked = [...provider.models].sort((a, b) => {\n const at = a.release_date ?? a.last_updated ?? '';\n const bt = b.release_date ?? b.last_updated ?? '';\n return bt.localeCompare(at);\n });\n return ranked[0]?.id;\n }\n\n async ageSeconds(): Promise<number> {\n if (!this.fetchedAt) {\n const cached = await this.readCacheAt(this.cacheFile);\n if (!cached) return Number.POSITIVE_INFINITY;\n return (Date.now() - new Date(cached.fetchedAt).getTime()) / 1000;\n }\n return (Date.now() - this.fetchedAt.getTime()) / 1000;\n }\n\n private resolveProvider(p: ModelsDevProvider): ResolvedProvider {\n return {\n id: p.id,\n name: p.name,\n family: classifyProviderFamily(p),\n apiBase: p.api,\n envVars: p.env ?? [],\n doc: p.doc,\n models: Object.values(p.models ?? {}),\n npm: p.npm,\n };\n }\n\n private isFresh(fetchedAtIso: string): boolean {\n return Date.now() - new Date(fetchedAtIso).getTime() < this.ttlMs;\n }\n\n private isWithinMaxStaleAge(fetchedAtIso: string): boolean {\n return Date.now() - new Date(fetchedAtIso).getTime() < this.maxStaleAgeMs;\n }\n\n private async readCacheAt(file: string): Promise<CacheEnvelope | undefined> {\n try {\n const raw = await fs.readFile(file, 'utf8');\n return JSON.parse(raw) as CacheEnvelope;\n } catch {\n return undefined;\n }\n }\n\n /** Used by `wstack models refresh` to expose where the cache lives. */\n cacheLocation(): string {\n return path.resolve(this.cacheFile);\n }\n}\n\n/** Render a seconds-duration as a human-friendly \"Xh Ym\" or \"Xd\" string. */\nfunction formatAge(seconds: number): string {\n if (seconds < 60) return '<1m';\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;\n if (seconds < 86400) {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n return m > 0 ? `${h}h ${m}m` : `${h}h`;\n }\n return `${Math.floor(seconds / 86400)}d`;\n}\n\nfunction hasEntries(payload: ModelsDevPayload | undefined): payload is ModelsDevPayload {\n return payload !== undefined && Object.keys(payload).length > 0;\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 fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { LogLevel, Logger } from '../types/logger.js';\nimport { color } from '../utils/color.js';\nimport { writeErr } from '../utils/term.js';\n\nconst LEVEL_RANK: Record<LogLevel, number> = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n trace: 4,\n};\n\nconst COLORS: Record<LogLevel, (s: string) => string> = {\n error: color.red,\n warn: color.yellow,\n info: color.cyan,\n debug: color.gray,\n trace: color.dim,\n};\n\nconst LOG_LEVELS = new Set<LogLevel>(['error', 'warn', 'info', 'debug', 'trace']);\nconst LOG_FORMATS = new Set<string>(['pretty', 'json']);\n\nexport type LogFormat = 'pretty' | 'json';\n\nexport interface DefaultLoggerOptions {\n level?: LogLevel | undefined;\n file?: string | undefined;\n /**\n * @deprecated Use `format: 'json'` instead. Kept for backward compat\n * with existing callers but has no effect on output \u2014 the `format`\n * option controls whether stderr receives pretty-printed or JSON lines.\n */\n pretty?: boolean | undefined;\n /** Output format for stderr. `pretty` (colored, human-readable) or `json` (machine-parseable). Defaults to `WRONGSTACK_LOG_FORMAT` env var, falling back to `pretty`. */\n format?: LogFormat | undefined;\n bindings?: Record<string, unknown>;\n /**\n * When false, suppress stderr output entirely \u2014 only write to the log\n * file (if configured). Use this in TUI mode so plugin/library log\n * messages don't interleave with Ink's terminal rendering.\n * Default: true (stderr output is enabled).\n */\n stderr?: boolean | undefined;\n /**\n * Rotate the log file once it exceeds this many bytes: the current file is\n * renamed to `<file>.1` (replacing any previous one) and a fresh file\n * starts. Bounds total disk to ~2\u00D7 this value. Default 10 MB.\n */\n maxFileBytes?: number | undefined;\n}\n\nexport class DefaultLogger implements Logger {\n /** How many file writes between rotation size checks (statSync is not free). */\n private static readonly ROTATE_CHECK_EVERY = 100;\n\n level: LogLevel;\n private file?: string | undefined;\n private bindings: Record<string, unknown>;\n private format: LogFormat;\n private stderr: boolean;\n private maxFileBytes: number;\n private writesSinceRotateCheck = 0;\n /**\n * Serialized async tail for file writes. Every appendFile (and any\n * chained rotation) is awaited through this promise so file I/O\n * never overlaps itself \u2014 preserving the per-line ordering the\n * sync version had, but without blocking the caller thread. Any\n * rejection is swallowed (`catch(() => {})`) because logging must\n * never crash the host.\n *\n * Children share the parent's tail: `child.tail === parent.tail`\n * for the lifetime of the chain. Read/write access goes through\n * `_tail` so that, when a child has been wired to a parent, both\n * `enqueueRotate` and `log` always observe the parent's current tail\n * rather than a stale snapshot taken at `child()` time.\n */\n private tail: Promise<void> = Promise.resolve();\n private parent: DefaultLogger | null = null;\n\n /**\n * Resolve the current tail. For the root logger this is the field;\n * for a child logger we always read through the parent so that a\n * child's appends land on the parent's most recent tail, and a\n * parent's `flush()` waits for everything the child chained.\n */\n private get _tail(): Promise<void> {\n return this.parent ? this.parent._tail : this.tail;\n }\n private set _tail(next: Promise<void>) {\n if (this.parent) this.parent.tail = next;\n else this.tail = next;\n }\n\n constructor(opts: DefaultLoggerOptions = {}) {\n this.level = opts.level ?? parseLogLevel(process.env.WRONGSTACK_LOG_LEVEL);\n this.file = opts.file;\n this.bindings = opts.bindings ?? {};\n this.format = opts.format ?? parseLogFormat(process.env.WRONGSTACK_LOG_FORMAT);\n this.stderr = opts.stderr !== false; // default true\n this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;\n if (this.file) {\n // Chain mkdir onto the file-write tail so the first append can't\n // race a still-pending mkdir (especially under tests that call\n // `flush()` immediately after `info()`). mkdir is best-effort;\n // a rejection only blocks subsequent appends in the chain that\n // observed it via the rejected promise, which would skip the\n // append \u2014 that is acceptable because ENOENT/EEXIST/EPERM all\n // either are no-ops or indicate an unrecoverable environment.\n const dir = path.dirname(this.file);\n this._tail = this._tail\n .then(async () => {\n await fsp.mkdir(dir, { recursive: true });\n })\n .catch(() => undefined);\n }\n }\n\n error(msg: string, ctx?: unknown): void {\n this.log('error', msg, ctx);\n }\n warn(msg: string, ctx?: unknown): void {\n this.log('warn', msg, ctx);\n }\n info(msg: string, ctx?: unknown): void {\n this.log('info', msg, ctx);\n }\n debug(msg: string, ctx?: unknown): void {\n this.log('debug', msg, ctx);\n }\n trace(msg: string, ctx?: unknown): void {\n this.log('trace', msg, ctx);\n }\n\n child(bindings: Record<string, unknown>): Logger {\n // Construct without invoking the class constructor (which would mkdir\n // again and create a separate file-write tail). The parent and child\n // must share the same tail so `parent.flush()` waits for child\n // appends too \u2014 otherwise a test that flushes the parent after\n // `child.info(...)` would race the child append and observe an empty\n // file. Sharing the tail preserves the order the parent originally\n // had via its serialised file-write queue.\n const child = Object.create(DefaultLogger.prototype) as DefaultLogger;\n child.level = this.level;\n child.file = this.file;\n child.bindings = { ...this.bindings, ...bindings };\n child.format = this.format;\n child.stderr = this.stderr;\n child.maxFileBytes = this.maxFileBytes;\n child.parent = this;\n child.writesSinceRotateCheck = this.writesSinceRotateCheck;\n return child;\n }\n\n /**\n * Wait until all queued file writes (and any pending rotation) have\n * completed. `log()` is fire-and-forget by design \u2014 the caller never\n * blocks on disk \u2014 so tests, shutdown handlers, and processes that\n * need a deterministic \"everything is on disk now\" guarantee should\n * `await logger.flush()` before reading the file or exiting.\n */\n flush(): Promise<void> {\n return this._tail;\n }\n\n /**\n * Size-based rotation: when the file outgrows `maxFileBytes`, rename it to\n * `<file>.1` (dropping the previous `.1`) so the live file restarts empty.\n * Checked on the first write and every ROTATE_CHECK_EVERY writes after.\n * Best-effort: a rename can fail on Windows while another process holds\n * the file \u2014 the next check retries. Multiple processes appending to the\n * same log all run this check; whoever crosses the threshold first wins.\n *\n * Async: the rotation runs on the file-write tail (so its writes don't\n * interleave with the next append), and the caller never blocks on a\n * statSync / renameSync syscall on the hot log path.\n */\n private enqueueRotate(file: string): void {\n if (this.writesSinceRotateCheck++ % DefaultLogger.ROTATE_CHECK_EVERY !== 0) return;\n this._tail = this._tail\n .then(async () => {\n let st;\n try {\n st = await fsp.stat(file);\n } catch {\n return; // file missing \u2014 nothing to rotate\n }\n if (st.size < this.maxFileBytes) return;\n try {\n await fsp.rm(`${file}.1`, { force: true });\n await fsp.rename(file, `${file}.1`);\n } catch {\n // file locked, or raced by another process \u2014 ignore\n }\n })\n .catch(() => undefined);\n }\n\n private log(level: LogLevel, msg: string, ctx?: unknown): void {\n const r = LEVEL_RANK[level];\n const allowed = LEVEL_RANK[this.level];\n if (r > allowed) return;\n const ts = new Date().toISOString();\n const entry: Record<string, unknown> = { ts, level, msg, ...this.bindings };\n if (ctx !== undefined) {\n entry.ctx = ctx instanceof Error ? { message: ctx.message, stack: ctx.stack } : ctx;\n }\n // Disk: JSON line. Serialized through `_tail` so concurrent log\n // calls preserve per-line order without blocking the caller on\n // sync file I/O. Children route through their parent's tail, so\n // a parent's `flush()` waits for every chained child append.\n if (this.file) {\n this.enqueueRotate(this.file);\n const line = `${JSON.stringify(entry)}\\n`;\n this._tail = this._tail\n .then(() => fsp.appendFile(this.file!, line))\n .catch(() => undefined);\n }\n // Stderr: pretty or json. Suppressed when this.stderr is false (TUI mode)\n // so plugin/library log messages don't interleave with Ink's rendering.\n if (!this.stderr) return;\n if (this.format === 'json') {\n writeErr(`${JSON.stringify(entry)}\\n`);\n } else {\n const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;\n if (ctx !== undefined) {\n writeErr(`${head} ${formatCtx(ctx)}\\n`);\n } else {\n writeErr(`${head}\\n`);\n }\n }\n }\n}\n\nfunction parseLogLevel(raw: string | undefined): LogLevel {\n return raw && LOG_LEVELS.has(raw as LogLevel) ? (raw as LogLevel) : 'info';\n}\n\nfunction parseLogFormat(raw: string | undefined): LogFormat {\n return raw && LOG_FORMATS.has(raw) ? (raw as LogFormat) : 'pretty';\n}\n\nfunction formatCtx(ctx: unknown): string {\n if (ctx instanceof Error) return color.dim(ctx.message);\n if (typeof ctx === 'string') return color.dim(ctx);\n try {\n return color.dim(JSON.stringify(ctx));\n } catch {\n return color.dim(String(ctx));\n }\n}\n\n/**\n * A logger that silently discards all messages. Used during boot before\n * the real logger is configured, and in test contexts where logging\n * would be noise.\n */\nexport const noOpLogger: Logger = {\n // 'error' is the quietest level the Logger contract offers; the methods\n // discard everything regardless, this only matters to level checks.\n level: 'error',\n error: () => {},\n warn: () => {},\n info: () => {},\n debug: () => {},\n trace: () => {},\n child: () => noOpLogger,\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", "export interface TextBlock {\n type: 'text';\n text: string;\n cache_control?: { type: 'ephemeral' | undefined };\n}\n\nexport interface ToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n /**\n * Provider-specific opaque metadata captured from the wire response.\n * Echoed back verbatim in the next request so providers that bind\n * extra state to function calls keep working. Example: Gemini's\n * `thoughtSignature` \u2014 required for tool-use turns with thinking\n * models, otherwise the next request fails with 400 \"Function call\n * is missing a thought_signature in functionCall parts\".\n *\n * Keys are namespaced by intent so multiple wires can coexist:\n * - `google.thoughtSignature` \u2014 Gemini signed-thought blob\n * Other providers can add their own keys without colliding.\n */\n providerMeta?: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock {\n type: 'tool_result';\n tool_use_id: string;\n /**\n * The original tool name. Useful for providers like Google Gemini that\n * need the tool name in `functionResponse.name` \u2014 the tool_use_id is\n * only a session-local identifier and is not stable across replays.\n * Always set by ToolExecutor; may be absent on manually-constructed blocks.\n */\n name?: string | undefined;\n content: string;\n is_error?: boolean | undefined;\n}\n\nexport interface ImageBlock {\n type: 'image';\n source: {\n type: 'base64' | 'url';\n media_type?: string | undefined;\n data?: string | undefined;\n url?: string | undefined;\n };\n}\n\n/**\n * Chain-of-thought / extended-thinking content emitted by the model.\n *\n * Both Anthropic extended thinking (`{type:'thinking', thinking, signature}`)\n * and DeepSeek reasoning mode (top-level `reasoning_content` on the assistant\n * message) require this content to be echoed back verbatim on the next\n * request, otherwise the provider returns 400:\n * - Anthropic: \"The `content[].thinking` in the thinking mode must be passed back\"\n * - DeepSeek: \"The `reasoning_content` in the thinking mode must be passed back\"\n *\n * `signature` is Anthropic-specific (an opaque integrity blob). DeepSeek\n * doesn't issue a signature \u2014 the field is absent for that provider.\n *\n * Per Anthropic, thinking blocks MUST appear before any text/tool_use blocks\n * in an assistant message. Stream builders preserve that order.\n */\nexport interface ThinkingBlock {\n type: 'thinking';\n thinking: string;\n signature?: string | undefined;\n providerMeta?: Record<string, unknown>;\n}\n\nexport type ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | ImageBlock | ThinkingBlock;\n\nexport function isTextBlock(b: ContentBlock): b is TextBlock {\n return b.type === 'text';\n}\nexport function isToolUseBlock(b: ContentBlock): b is ToolUseBlock {\n return b.type === 'tool_use';\n}\nexport function isToolResultBlock(b: ContentBlock): b is ToolResultBlock {\n return b.type === 'tool_result';\n}\nexport function isImageBlock(b: ContentBlock): b is ImageBlock {\n return b.type === 'image';\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 { 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 type {\n ModelsDevModel,\n ModelsDevProvider,\n ModelsDevPayload,\n} from '../types/models-registry.js';\n\n// Magic keys in the overlay payload that signal deletions rather than additions.\nconst REMOVE_PROVIDERS_KEY = '_removeProviders';\nconst REMOVE_MODELS_KEY = '_removeModels';\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 * NEW: The overlay also supports two magic keys for removal:\n *\n * `_removeProviders` (string[]) \u2014 provider ids to DELETE from the result.\n * `_removeModels` (Record<string, string[]>) \u2014 per-provider model ids to\n * delete from that provider's model list.\n *\n * Removals are applied AFTER the merge pass so the overlay can add and then\n * selectively strip away unwanted entries from the base catalog.\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 // Step 1: extract removal directives before the merge loop\n const removeProviders: string[] = Array.isArray(overlay[REMOVE_PROVIDERS_KEY])\n ? (overlay[REMOVE_PROVIDERS_KEY] as string[])\n : [];\n const removeModels: Record<string, string[]> =\n overlay[REMOVE_MODELS_KEY] && typeof overlay[REMOVE_MODELS_KEY] === 'object'\n ? (overlay[REMOVE_MODELS_KEY] as unknown as Record<string, string[]>)\n : {};\n\n // Step 2: conventional deep-merge (add/override only)\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 // Skip magic keys\n if (id === REMOVE_PROVIDERS_KEY || id === REMOVE_MODELS_KEY) continue;\n const existing = out[id];\n out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);\n }\n\n // Step 3: apply removals\n for (const providerId of removeProviders) {\n delete out[providerId];\n }\n for (const [providerId, modelIds] of Object.entries(removeModels)) {\n const provider = out[providerId];\n if (!provider || !provider.models) continue;\n for (const modelId of modelIds) {\n delete provider.models[modelId];\n }\n }\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", "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", "import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport type { Mode, ModeConfig, ModeManifest, ModeStore } from '../types/mode.js';\nimport { DEFAULT_MODES } from '../types/mode.js';\n\nexport class DefaultModeStore implements ModeStore {\n private activeModeId: string | null = null;\n private modes: Mode[];\n private configDir: string;\n\n constructor(config: ModeConfig) {\n this.configDir = config.directory;\n this.modes = [...DEFAULT_MODES];\n }\n\n async getActiveMode(): Promise<Mode | null> {\n if (!this.activeModeId) {\n await this.loadActiveMode();\n }\n if (!this.activeModeId) return null;\n return this.modes.find((m) => m.id === this.activeModeId) ?? null;\n }\n\n async setActiveMode(modeId: string | null): Promise<void> {\n this.activeModeId = modeId;\n await this.saveActiveMode();\n }\n\n async listModes(): Promise<Mode[]> {\n return [...this.modes];\n }\n\n async getMode(modeId: string): Promise<Mode | null> {\n return this.modes.find((m) => m.id === modeId) ?? null;\n }\n\n async addMode(mode: Mode): Promise<void> {\n const idx = this.modes.findIndex((m) => m.id === mode.id);\n if (idx >= 0) {\n this.modes[idx] = mode;\n } else {\n this.modes.push(mode);\n }\n }\n\n async removeMode(modeId: string): Promise<void> {\n const builtIn = DEFAULT_MODES.find((m) => m.id === modeId);\n if (builtIn) {\n throw new Error(`Cannot remove built-in mode \"${modeId}\"`);\n }\n this.modes = this.modes.filter((m) => m.id !== modeId);\n if (this.activeModeId === modeId) {\n this.activeModeId = null;\n await this.saveActiveMode();\n }\n }\n\n private async loadActiveMode(): Promise<void> {\n try {\n const configPath = path.join(this.configDir, 'mode.json');\n const content = await fs.readFile(configPath, 'utf8');\n const data = JSON.parse(content);\n this.activeModeId = data.activeMode ?? null;\n } catch {\n this.activeModeId = 'default';\n }\n }\n\n private async saveActiveMode(): Promise<void> {\n try {\n await fs.mkdir(this.configDir, { recursive: true });\n const configPath = path.join(this.configDir, 'mode.json');\n // atomicWrite: torn write would leave mode.json malformed and the\n // next load would silently reset to \"default\".\n await atomicWrite(\n configPath,\n JSON.stringify({ activeMode: this.activeModeId }, null, 2),\n );\n } catch {\n // ignore save errors\n }\n }\n}\n\nexport interface ModeLoaderOptions {\n projectModesDir?: string | undefined;\n userModesDir?: string | undefined;\n}\n\nexport async function loadProjectModes(modesDir: string): Promise<Mode[]> {\n const modes: Mode[] = [];\n try {\n const entries = await fs.readdir(modesDir);\n for (const entry of entries) {\n if (!entry.endsWith('.md') && !entry.endsWith('.txt')) continue;\n const filePath = path.join(modesDir, entry);\n const stat = await fs.stat(filePath);\n if (!stat.isFile()) continue;\n const content = await fs.readFile(filePath, 'utf8');\n const id = path.basename(entry, path.extname(entry));\n modes.push({\n id,\n name: id.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()),\n description: content.split('\\n')[0] ?? id,\n prompt: content,\n tags: ['project'],\n });\n }\n } catch {\n // no project modes\n }\n return modes;\n}\n\nexport async function loadUserModes(modesDir: string): Promise<Mode[]> {\n const modes: Mode[] = [];\n try {\n const manifestPath = path.join(modesDir, 'modes.json');\n const content = await fs.readFile(manifestPath, 'utf8');\n const manifest: ModeManifest = JSON.parse(content);\n for (const mode of manifest.modes) {\n modes.push(mode);\n }\n } catch {\n // no user modes\n }\n return modes;\n}\n", "import { readFileSync, statSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport function modePrompt(id: string): string {\n for (const dir of modePromptDirCandidates()) {\n try {\n return readFileSync(path.join(dir, `${id}.md`), 'utf8').trimEnd();\n } catch {\n // try next candidate\n }\n }\n return '';\n}\n\nfunction modePromptDirCandidates(): string[] {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, '../../instructions/modes'),\n path.resolve(here, '../instructions/modes'),\n path.resolve(here, 'instructions/modes'),\n ];\n return candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));\n}\n\nfunction isDirectory(candidate: string): boolean {\n try {\n return statSync(candidate).isDirectory();\n } catch {\n return false;\n }\n}\n", "import { modePrompt } from './mode-prompts.js';\nexport interface Mode {\n id: string;\n name: string;\n description: string;\n /** Additional prompt text injected into system prompt when mode is active */\n prompt: string;\n /** Tags for tool_search filtering */\n tags?: string[] | undefined;\n /** Tools that should be prioritized/highlighted when this mode is active */\n toolPreferences?: string[] | undefined;\n /**\n * Skill names that are particularly relevant to this mode. The system\n * prompt builder appends a \"Suggested skills\" note so the model knows\n * which domain knowledge to leverage first. Skill must exist in the\n * loaded skill set to appear.\n */\n suggestedSkills?: string[] | undefined;\n}\n\nexport interface ModeManifest {\n modes: Mode[];\n defaultMode?: string | undefined;\n}\n\nexport interface ModeStore {\n getActiveMode(): Promise<Mode | null>;\n setActiveMode(modeId: string | null): Promise<void>;\n listModes(): Promise<Mode[]>;\n getMode(modeId: string): Promise<Mode | null>;\n}\n\nexport interface ModeConfig {\n directory: string;\n}\n\nexport const DEFAULT_MODES: Mode[] = [\n {\n id: 'default',\n name: 'Default',\n description: 'Balanced general-purpose mode; use when no special token/coverage trade-off is needed',\n prompt: '',\n tags: ['general', 'balanced'],\n },\n {\n id: 'brief',\n name: 'Brief',\n description: 'Ultra-compact responses for low-context, high-speed work',\n prompt: modePrompt('brief'),\n tags: ['lite', 'fast', 'concise', 'token-saving'],\n toolPreferences: ['read', 'edit', 'bash'],\n suggestedSkills: [],\n },\n {\n id: 'review-lite',\n name: 'Review Lite',\n description: 'Token-saving code review: changed files only, top correctness/security risks',\n prompt: modePrompt('review-lite'),\n tags: ['lite', 'review', 'quality', 'token-saving'],\n toolPreferences: ['git', 'diff', 'read', 'grep'],\n suggestedSkills: ['bug-hunter', 'typescript-strict'],\n },\n {\n id: 'audit-lite',\n name: 'Audit Lite',\n description: 'Token-saving security triage for a small diff or named file',\n prompt: modePrompt('audit-lite'),\n tags: ['lite', 'security', 'audit', 'token-saving'],\n toolPreferences: ['grep', 'read', 'git'],\n suggestedSkills: ['security-scanner'],\n },\n {\n id: 'plan-lite',\n name: 'Plan Lite',\n description: 'Token-saving planning: 3-6 actionable steps, minimal design debate',\n prompt: modePrompt('plan-lite'),\n tags: ['lite', 'planning', 'architecture', 'token-saving'],\n toolPreferences: ['tree', 'glob', 'read', 'grep'],\n suggestedSkills: ['refactor-planner'],\n },\n {\n id: 'debug-lite',\n name: 'Debug Lite',\n description: 'Token-saving bug triage: one hypothesis, nearest evidence, narrow check',\n prompt: modePrompt('debug-lite'),\n tags: ['lite', 'debug', 'triage', 'token-saving'],\n toolPreferences: ['read', 'grep', 'test', 'logs'],\n suggestedSkills: ['bug-hunter'],\n },\n {\n id: 'test-lite',\n name: 'Test Lite',\n description: 'Token-saving tests: one focused regression or narrow verification target',\n prompt: modePrompt('test-lite'),\n tags: ['lite', 'testing', 'qa', 'token-saving'],\n toolPreferences: ['test', 'read', 'grep'],\n suggestedSkills: ['testing'],\n },\n {\n id: 'refactor-lite',\n name: 'Refactor Lite',\n description: 'Token-saving cleanup: small scoped behavior-preserving changes',\n prompt: modePrompt('refactor-lite'),\n tags: ['lite', 'refactor', 'token-saving'],\n toolPreferences: ['read', 'edit', 'test'],\n suggestedSkills: ['typescript-strict'],\n },\n {\n id: 'research-lite',\n name: 'Research Lite',\n description: 'Token-saving web research: one search, one authoritative fetch, short answer',\n prompt: modePrompt('research-lite'),\n tags: ['lite', 'research', 'web', 'token-saving'],\n toolPreferences: ['search', 'fetch'],\n suggestedSkills: ['research-web'],\n },\n {\n id: 'code-reviewer',\n name: 'Review Deep',\n description: 'Comprehensive code review across contracts, edge cases, lifecycle, errors, concurrency',\n prompt: modePrompt('code-reviewer'),\n tags: ['deep', 'review', 'quality', 'security'],\n toolPreferences: ['read', 'grep', 'git', 'diff', 'test'],\n suggestedSkills: ['bug-hunter', 'security-scanner', 'typescript-strict', 'testing'],\n },\n {\n id: 'code-auditor',\n name: 'Audit Deep',\n description: 'Comprehensive security audit with category coverage and exploitability notes',\n prompt: modePrompt('code-auditor'),\n tags: ['deep', 'security', 'audit', 'compliance'],\n toolPreferences: ['grep', 'read', 'audit', 'bash'],\n suggestedSkills: ['security-scanner', 'bug-hunter', 'audit-log'],\n },\n {\n id: 'architect',\n name: 'Architecture Deep',\n description: 'Comprehensive architecture and cross-module contract analysis',\n prompt: modePrompt('architect'),\n tags: ['deep', 'architecture', 'design', 'scalability'],\n toolPreferences: ['read', 'glob', 'tree', 'diff'],\n suggestedSkills: ['api-design', 'refactor-planner', 'node-modern', 'docker-deploy'],\n },\n {\n id: 'debugger',\n name: 'Debug Deep',\n description: 'Comprehensive root-cause analysis with traces, logs, assumptions, and verification',\n prompt: modePrompt('debugger'),\n tags: ['deep', 'debug', 'investigation', 'error-resolution'],\n toolPreferences: ['read', 'grep', 'bash', 'logs', 'test'],\n suggestedSkills: ['bug-hunter', 'audit-log', 'observability'],\n },\n {\n id: 'tester',\n name: 'Test Deep',\n description: 'Comprehensive QA mode for coverage, boundaries, isolation, and integration gaps',\n prompt: modePrompt('tester'),\n tags: ['deep', 'testing', 'qa', 'quality'],\n toolPreferences: ['read', 'grep', 'test', 'bash'],\n suggestedSkills: ['testing', 'bug-hunter', 'typescript-strict'],\n },\n {\n id: 'devops',\n name: 'DevOps Deep',\n description: 'Comprehensive infrastructure, deployment, observability, and operations review',\n prompt: modePrompt('devops'),\n tags: ['deep', 'devops', 'infrastructure', 'operations'],\n toolPreferences: ['read', 'bash', 'grep', 'logs', 'git'],\n suggestedSkills: ['docker-deploy', 'observability', 'security-scanner'],\n },\n {\n id: 'refactorer',\n name: 'Refactor Deep',\n description: 'Comprehensive modernization/refactor mode with contracts and verification discipline',\n prompt: modePrompt('refactorer'),\n tags: ['deep', 'refactor', 'modernization', 'improvement'],\n toolPreferences: ['read', 'edit', 'test', 'git', 'grep'],\n suggestedSkills: ['refactor-planner', 'typescript-strict', 'node-modern', 'testing'],\n },\n {\n id: 'ui-design',\n name: 'UI Design Deep',\n description: 'Comprehensive design-first frontend/mobile UI work with kit, tokens, and accessibility',\n prompt: modePrompt('ui-design'),\n tags: ['deep', 'ui', 'frontend', 'mobile', 'design'],\n toolPreferences: ['design', 'write', 'edit', 'read', 'scaffold'],\n suggestedSkills: ['react-modern'],\n },\n {\n id: 'teach',\n name: 'Teach Deep',\n description: 'Mentor mode with explanations, mental models, trade-offs, and takeaways',\n prompt: modePrompt('teach'),\n tags: ['deep', 'teaching', 'mentor', 'learning'],\n toolPreferences: ['read', 'edit', 'explain'],\n suggestedSkills: ['prompt-engineering', 'skill-creator', 'node-modern', 'typescript-strict'],\n },\n {\n id: 'research-web',\n name: 'Research Deep',\n description: 'Comprehensive current-data research with cross-checking and reusable findings',\n prompt: modePrompt('research-web'),\n tags: ['deep', 'research', 'web', 'current-data', 'up-to-date'],\n toolPreferences: ['search', 'fetch', 'context_manager'],\n suggestedSkills: ['research-web', 'tech-stack', 'node-modern', 'security-scanner', 'react-modern'],\n },\n];\n", "import { isTextBlock } from '../types/blocks.js';\nimport type { Message } from '../types/messages.js';\n\nconst PATH_HINT_PATTERN =\n /(?:(?:[A-Za-z]:)?[./\\\\]?[\\w@.-]+(?:[\\\\/][\\w@(). -]+)+\\.[A-Za-z0-9]{1,12})/g;\nconst PATH_BACKSLASH_PATTERN = /\\\\/g;\nconst PATH_TRIM_PATTERN = /^[\"'`]+|[\"'`),;:]+$/g;\nconst ERROR_LINE_PATTERN =\n /\\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\\b/i;\nconst NEWLINE_SPLIT_PATTERN = /\\r?\\n/;\nconst WHITESPACE_COLLAPSE_PATTERN = /\\s+/g;\n\n/**\n * Compact, information-dense representation of one message for selector and\n * summarizer calls. Kept in the neutral utils layer so models do not depend\n * on execution at runtime.\n */\nexport function buildCompactionPreview(message: Message, maxChars = 600): string {\n if (maxChars <= 0) return '';\n if (typeof message.content === 'string') {\n return truncatePreview(message.content, maxChars);\n }\n\n const parts: string[] = [];\n for (const block of message.content) {\n if (isTextBlock(block)) {\n const text = truncatePreview(block.text, maxChars);\n if (text) parts.push(text);\n continue;\n }\n if (block.type === 'tool_use') {\n const input = truncatePreview(safePreviewString(block.input), 220);\n parts.push(`[tool_use: ${block.name}${input ? `; input=${input}` : ''}]`);\n continue;\n }\n if (block.type === 'tool_result') {\n const raw = safePreviewString(block.content);\n const details: string[] = [block.is_error ? 'error' : 'ok'];\n if (block.name) details.push(`tool=${block.name}`);\n const files = extractPathHints(raw).slice(0, 5);\n if (files.length > 0) details.push(`files=${files.join(', ')}`);\n const error = firstErrorLine(raw);\n if (error) details.push(`error=${error}`);\n const sample = truncatePreview(raw, 260);\n if (sample) details.push(`sample=${sample}`);\n parts.push(`[tool_result: ${details.join('; ')}]`);\n continue;\n }\n parts.push(`[${block.type}]`);\n }\n return truncatePreview(parts.join(' '), maxChars);\n}\n\nfunction safePreviewString(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction truncatePreview(value: string, maxChars: number): string {\n const compact = value.replace(WHITESPACE_COLLAPSE_PATTERN, ' ').trim();\n if (compact.length <= maxChars) return compact;\n return `${compact.slice(0, Math.max(0, maxChars - 1))}\u2026`;\n}\n\nfunction extractPathHints(content: unknown): string[] {\n const text = safePreviewString(content);\n const out = new Set<string>();\n for (const match of text.matchAll(PATH_HINT_PATTERN)) {\n const clean = match[0]?.replace(PATH_BACKSLASH_PATTERN, '/').replace(PATH_TRIM_PATTERN, '');\n if (clean) out.add(clean);\n }\n return [...out];\n}\n\nfunction firstErrorLine(content: unknown): string | undefined {\n const text = safePreviewString(content);\n for (const line of text.split(NEWLINE_SPLIT_PATTERN)) {\n if (!ERROR_LINE_PATTERN.test(line)) continue;\n const trimmed = line.replace(WHITESPACE_COLLAPSE_PATTERN, ' ').trim();\n if (trimmed) return truncatePreview(trimmed, 220);\n }\n return undefined;\n}\n", "import { readFileSync, statSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Cache of resolved instruction text, keyed by the relative path. Bundled\n * instruction files are immutable for the process lifetime, so re-reading them\n * on every call (some callers do so per LLM request) is wasted disk I/O.\n */\nconst textCache = new Map<string, string>();\n\n/** Resolved once \u2014 the candidate roots only depend on this module's location. */\nlet rootCandidates: string[] | undefined;\n\nexport function readBundledInstructionText(relativePath: string): string {\n const cached = textCache.get(relativePath);\n if (cached !== undefined) return cached;\n\n let resolved = '';\n for (const root of instructionRootCandidates()) {\n try {\n resolved = readFileSync(path.join(root, relativePath), 'utf8').trimEnd();\n break;\n } catch {\n // try next candidate\n }\n }\n textCache.set(relativePath, resolved);\n return resolved;\n}\n\nexport function renderInstructionTemplate(\n template: string,\n values: Record<string, string>,\n): string {\n return template.replace(/\\{\\{\\s*([a-zA-Z0-9_.-]+)\\s*\\}\\}/g, (match, key: string) =>\n Object.hasOwn(values, key) ? (values[key] ?? '') : match,\n );\n}\n\nfunction instructionRootCandidates(): string[] {\n if (rootCandidates !== undefined) return rootCandidates;\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, '../../instructions'),\n path.resolve(here, '../instructions'),\n path.resolve(here, 'instructions'),\n ];\n rootCandidates = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));\n return rootCandidates;\n}\n\nfunction isDirectory(candidate: string): boolean {\n try {\n return statSync(candidate).isDirectory();\n } catch {\n return false;\n }\n}\n", "import { expectDefined } from '../utils/expect-defined.js';\nimport { estimateMessageTokens, estimateTextTokens } from '../utils/token-estimate.js';\nimport { isTextBlock } from '../types/blocks.js';\nimport type { Logger } from '../types/logger.js';\nimport { noOpLogger } from '../infrastructure/logger.js';\nimport type { Message } from '../types/messages.js';\nimport type { Provider, Request } from '../types/provider.js';\nimport type { MessageSelector, SelectorResult } from '../types/selector.js';\nimport type { OneShotOrchestrator } from '../execution/one-shot-llm.js';\nimport { buildCompactionPreview } from '../utils/compaction-preview.js';\nimport { readBundledInstructionText } from '../utils/instruction-file.js';\nexport interface LLMSelectorOptions {\n /** Provider used for the selector LLM call. Required. */\n provider: Provider;\n /** Model for the selector. Defaults to the provider's default model. */\n model?: string | undefined;\n /**\n * Maximum tokens to keep in context (target budget).\n * Selector will aim to keep total content below this.\n */\n maxContextTokens?: number | undefined;\n /**\n * Prompt instructing the selector how to behave.\n * Should guide the LLM on importance tiers and output format.\n */\n systemPrompt?: string | undefined;\n /**\n * Maximum output tokens for the selector LLM call.\n * Controls both the JSON response budget and the token reservation for the\n * history text budget calculation (default: 1024).\n */\n maxOutputTokens?: number | undefined;\n /**\n * OneShotOrchestrator for the selector LLM call. When set, uses it\n * instead of direct provider.complete(), gaining fallback chain support.\n */\n oneShotOrchestrator?: OneShotOrchestrator | undefined;\n /** Structured logger. Defaults to noOpLogger (silent). */\n logger?: Logger | undefined;\n}\n\nconst DEFAULT_SYSTEM_PROMPT = readBundledInstructionText('llm/llm-selector.md');\n\n/**\n * Format messages as a compact text dump for the selector LLM.\n * Uses token estimation (not character count) to budget the output,\n * so long sessions don't silently truncate the selector's view of history.\n */\nfunction formatMessages(messages: Message[], maxTokens = 2048): string {\n const previewChars = Math.max(\n 120,\n Math.min(600, Math.floor((maxTokens * 3.2) / Math.max(1, messages.length))),\n );\n const lines: Array<{ index: number; text: string; tokens: number }> = [];\n for (let i = 0; i < messages.length; i++) {\n const m = expectDefined(messages[i]);\n const role = m.role.padEnd(10, ' ');\n const line = `[${i}][${role}]: ${buildCompactionPreview(m, previewChars)}`;\n lines.push({ index: i, text: line, tokens: estimateTextTokens(line) });\n }\n\n const total = lines.reduce((sum, line) => sum + line.tokens, 0);\n if (total <= maxTokens) return lines.map((line) => line.text).join('\\n');\n\n // A selector must always see the live working tail. Fill the remainder from\n // the oldest side so long histories retain both the original constraints\n // and the current task, with an explicit marker for any omitted middle.\n const recentCount = Math.min(8, lines.length);\n const recentStart = lines.length - recentCount;\n const tail: typeof lines = [];\n let usedTokens = 0;\n for (let i = lines.length - 1; i >= recentStart; i--) {\n const line = expectDefined(lines[i]);\n if (tail.length >= 2 && usedTokens + line.tokens > Math.floor(maxTokens * 0.45)) continue;\n tail.unshift(line);\n usedTokens += line.tokens;\n }\n\n const head: typeof lines = [];\n for (let i = 0; i < recentStart; i++) {\n const line = expectDefined(lines[i]);\n if (usedTokens + line.tokens > maxTokens) break;\n head.push(line);\n usedTokens += line.tokens;\n }\n const omitted = Math.max(0, recentStart - head.length);\n const marker = omitted > 0 ? [`[\u2026 ${omitted} middle message(s) omitted from selector preview \u2026]`] : [];\n return [...head.map((line) => line.text), ...marker, ...tail.map((line) => line.text)].join('\\n');\n}\n\n/**\n * LLM-powered message selector. Calls a sub-LLM to analyze the\n * message history and produce a keep/collapse plan \u2014 more surgical\n * than fixed-window rules.\n */\nexport class LLMSelector implements MessageSelector {\n private readonly provider: Provider;\n private readonly model: string;\n private readonly maxContextTokens: number;\n private readonly systemPrompt: string;\n private readonly maxOutputTokens: number;\n private readonly oneShotOrchestrator?: OneShotOrchestrator | undefined;\n private readonly logger: Logger;\n\n constructor(opts: LLMSelectorOptions) {\n this.provider = opts.provider;\n this.model = opts.model ?? 'unknown';\n this.logger = opts.logger ?? noOpLogger;\n if (\n this.model === 'unknown' &&\n (process.env['NODE_ENV'] === 'development' || process.env['WRONGSTACK_DEBUG'] === '1')\n ) {\n this.logger.warn(\n '[LLMSelector] model not set \u2014 selector will use the provider default. Set `model` explicitly in LLMSelectorOptions to silence this warning.',\n );\n }\n this.maxContextTokens = opts.maxContextTokens ?? 40_000;\n this.systemPrompt = opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n this.maxOutputTokens = opts.maxOutputTokens ?? 1024;\n this.oneShotOrchestrator = opts.oneShotOrchestrator;\n }\n\n async select(messages: Message[], maxToKeep: number): Promise<SelectorResult> {\n const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);\n\n const totalTokens = estimateMessageTokens(messages);\n const systemText = `${this.systemPrompt}\\n\\nConversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):\\n`;\n // Reserve tokens for the system prefix and output (maxOutputTokens), then give the\n // rest to the formatted history so the selector sees the maximum possible context.\n const systemTokens = estimateTextTokens(systemText);\n const historyBudget = Math.max(512, effectiveBudget - systemTokens - this.maxOutputTokens);\n\n // Build a concise representation of the conversation within the token budget\n const historyText = formatMessages(messages, historyBudget);\n\n // Add instruction to stay within budget\n const budgetInstruction =\n totalTokens > effectiveBudget\n ? `\\n\\nIMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiveBudget}). You MUST collapse enough to fit. Prefer collapsing older/lower-importance ranges.`\n : '';\n\n const req: Request = {\n model: this.model,\n system: [{ type: 'text', text: systemText + budgetInstruction }],\n messages: [{ role: 'user', content: historyText }],\n maxTokens: this.maxOutputTokens,\n };\n\n let raw: string;\n const ac = new AbortController();\n try {\n if (this.oneShotOrchestrator) {\n const result = await this.oneShotOrchestrator.call({\n system: systemText + budgetInstruction,\n userPrompt: historyText,\n maxTokens: this.maxOutputTokens,\n timeoutMs: 30_000,\n });\n if (result.error) throw new Error(result.error);\n raw = result.text;\n } else {\n // 30-second timeout so a stuck selector LLM call can't hang the compactor.\n const timeoutSignal = AbortSignal.timeout(30_000);\n const res = await this.provider.complete(req, {\n signal: AbortSignal.any([ac.signal, timeoutSignal]),\n });\n const textBlocks = res.content.filter(isTextBlock);\n raw = textBlocks\n .map((b) => b.text)\n .join('\\n')\n .trim();\n }\n } catch (err) {\n if (err instanceof Error) {\n this.logger.warn(`selector call failed, using recency fallback: ${err.message}`, {\n event: 'llm_selector.call_failed',\n });\n }\n return this.fallbackSelect(messages, effectiveBudget);\n } finally {\n ac.abort();\n }\n\n return this.parseSelectorOutput(raw, messages);\n }\n\n private fallbackSelect(messages: Message[], budget: number): SelectorResult {\n // Simple fallback: keep from the end until we hit budget\n const toKeep: SelectorResult['kept'] = [];\n const toCollapse: SelectorResult['collapsed'] = [];\n\n let tokenCount = 0;\n let startIdx = 0;\n\n // Scan from the end backwards\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = expectDefined(messages[i]);\n const cost = estimateMessageTokens([m]);\n\n if (tokenCount + cost <= budget) {\n tokenCount += cost;\n } else {\n startIdx = i + 1;\n break;\n }\n }\n\n if (startIdx > 0) {\n toCollapse.push({ from: 0, to: startIdx - 1 });\n }\n toKeep.push({ from: startIdx, to: messages.length - 1, importance: 'high' });\n\n return {\n kept: toKeep,\n collapsed: toCollapse,\n reasoning: `Fallback: kept last ${messages.length - startIdx} messages within ${budget} token budget`,\n };\n }\n\n /**\n * Parse and validate the raw LLM output into a SelectorResult.\n * Falls back to recency-based selection if the LLM output is malformed,\n * out-of-bounds, or internally inconsistent.\n */\n private parseSelectorOutput(raw: string, messages: Message[]): SelectorResult {\n const messageCount = messages.length;\n if (messageCount === 0) {\n return { kept: [], collapsed: [], reasoning: 'empty session' };\n }\n\n // Try to extract JSON from the response\n const jsonStart = raw.indexOf('{');\n const jsonEnd = raw.lastIndexOf('}');\n if (jsonStart === -1 || jsonEnd === -1) {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw.slice(jsonStart, jsonEnd + 1));\n } catch {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n\n const obj = parsed as Record<string, unknown>;\n const keptRaw =\n (obj.kept as Array<{ from: number; to: number; importance: string }> | undefined) ?? [];\n const collapsedRaw =\n (obj.collapsed as Array<{ from: number; to: number; summary?: string | undefined }> | undefined) ?? [];\n\n // Validate kept ranges \u2014 must be within [0, messageCount), from <= to\n const kept: SelectorResult['kept'] = [];\n for (const k of keptRaw) {\n if (\n typeof k.from !== 'number' ||\n typeof k.to !== 'number' ||\n k.from < 0 ||\n k.to >= messageCount ||\n k.from > k.to\n ) {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n kept.push({\n from: k.from,\n to: k.to,\n importance: (k.importance ?? 'medium') as 'critical' | 'high' | 'medium',\n });\n }\n\n // Validate collapsed ranges \u2014 same bounds check\n const collapsed: SelectorResult['collapsed'] = [];\n for (const c of collapsedRaw) {\n if (\n typeof c.from !== 'number' ||\n typeof c.to !== 'number' ||\n c.from < 0 ||\n c.to >= messageCount ||\n c.from > c.to\n ) {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n collapsed.push({ from: c.from, to: c.to, summary: c.summary });\n }\n\n // Check for overlaps: kept ranges must not overlap with each other or with collapsed ranges\n const allRanges: Array<{ from: number; to: number }> = [...kept, ...collapsed];\n for (let i = 0; i < allRanges.length; i++) {\n const a = allRanges[i];\n if (!a) continue;\n for (let j = i + 1; j < allRanges.length; j++) {\n const b = allRanges[j];\n if (!b) continue;\n // Overlap: a starts before b ends AND a ends after b starts\n if (a.from <= b.to && a.to >= b.from) {\n return this.fallbackSelect(messages, this.maxContextTokens);\n }\n }\n }\n\n return {\n kept,\n collapsed,\n reasoning: typeof obj.reasoning === 'string' ? obj.reasoning : '',\n };\n }\n}\n", "/**\n * Offline **floor** for the ChatGPT \"Sign in with ChatGPT\" (`openai-codex`)\n * models, mirroring what the official Codex CLI / ChatGPT model picker shows.\n *\n * Source-of-truth precedence (live path wins):\n * 1. The curated overlay `packages/cli/data/providers.json` \u2014 provider\n * `openai-codex` \u2014 which is **synced from raw GitHub** + cached + bundled\n * offline by `DefaultModelsRegistry` (overlayUrl/overlayFile). Editing that\n * JSON and pushing updates every client without a code release; this is the\n * authoritative list whenever the registry overlay is wired (the CLI path).\n * 2. This hardcoded table \u2014 the last-resort floor used where no overlay is\n * available (a standalone registry without the bundled file) and by the CLI\n * auth-menu login flow, which needs a synchronous id list.\n *\n * Keep this in lockstep with the `openai-codex` block in `providers.json`; the\n * drift-guard test `packages/cli/tests/codex-catalog-overlay-sync.test.ts`\n * asserts they agree.\n *\n * Layer note: this lives in `core` (not the CLI auth-menu) so the model-list\n * resolver can read it without `core` depending on `cli`. The CLI's\n * `FALLBACK_CODEX_MODELS` is derived from this list.\n */\n\nexport interface CodexModelMeta {\n /** The wire id sent to the backend and stored in config (e.g. `gpt-5.5`). */\n id: string;\n /** Human-readable display name shown in pickers. */\n name: string;\n /** One-line capability blurb, matching the official Codex picker copy. */\n description: string;\n /** The recommended / latest model \u2014 tagged \"(current)\" in the official UI. */\n current?: boolean;\n}\n\n/**\n * Current ChatGPT sign-in models, newest first. The first entry is the\n * recommended default (`current`). Order is significant: callers that need a\n * default model id use `CODEX_MODELS[0]`.\n */\nexport const CODEX_MODELS: ReadonlyArray<CodexModelMeta> = [\n {\n id: 'gpt-5.6-sol',\n name: 'GPT-5.6 Sol',\n description: 'Latest frontier agentic coding model.',\n current: true,\n },\n {\n id: 'gpt-5.5',\n name: 'GPT-5.5',\n description: 'Frontier model for complex coding, research, and real-world work.',\n },\n {\n id: 'gpt-5.6-terra',\n name: 'GPT-5.6 Terra',\n description: 'Balanced agentic coding model for everyday work.',\n },\n {\n id: 'gpt-5.6-luna',\n name: 'GPT-5.6 Luna',\n description: 'Fast and affordable agentic coding model.',\n },\n {\n id: 'gpt-5.4',\n name: 'GPT-5.4',\n description: 'Strong model for everyday coding.',\n },\n {\n id: 'gpt-5.4-mini',\n name: 'GPT-5.4 Mini',\n description: 'Small, fast, and cost-efficient model for simpler coding tasks.',\n },\n {\n id: 'gpt-5.3-codex-spark',\n name: 'GPT-5.3 Codex Spark',\n description: 'Ultra-fast coding model.',\n },\n];\n\nconst BY_ID: ReadonlyMap<string, CodexModelMeta> = new Map(CODEX_MODELS.map((m) => [m.id, m]));\n\n/** Look up the canonical metadata for a Codex model id, or `undefined`. */\nexport function codexModelMeta(id: string): CodexModelMeta | undefined {\n return BY_ID.get(id);\n}\n", "import type { ModelsDevModel, ResolvedProvider } from '../types/models-registry.js';\nimport { CODEX_MODELS, codexModelMeta } from './codex-catalog.js';\n\n/**\n * A model descriptor shaped for the WebUI `provider.models` message. All\n * metadata fields are optional because OAuth / subscription providers that\n * models.dev doesn't list contribute only a bare id.\n */\nexport interface ProviderModelDescriptor {\n id: string;\n name: string;\n /** One-line capability blurb, when known (e.g. the Codex subscription models). */\n description?: string | undefined;\n releaseDate?: string | undefined;\n contextWindow?: number | undefined;\n inputCost?: number | undefined;\n outputCost?: number | undefined;\n capabilities: string[];\n}\n\n/** Map a models.dev catalog model to the WebUI descriptor shape. */\nexport function describeCatalogModel(m: ModelsDevModel): ProviderModelDescriptor {\n return {\n id: m.id,\n name: m.name,\n ...(m.description !== undefined ? { description: m.description } : {}),\n releaseDate: m.release_date,\n contextWindow: m.limit?.context,\n inputCost: m.cost?.input,\n outputCost: m.cost?.output,\n capabilities: [\n ...(m.tool_call ? ['tools'] : []),\n ...(m.reasoning ? ['reasoning'] : []),\n ...(m.modalities?.input?.includes('image') ? ['vision'] : []),\n ...(m.open_weights ? ['open_weights'] : []),\n ],\n };\n}\n\n/**\n * Resolve the model list to offer for a provider, merging a saved-config\n * allowlist with optional models.dev catalog metadata.\n *\n * The result is a **union** of all available sources \u2014 the saved allowlist\n * (if any), the catalog models (base + curated overlay), the sibling\n * catalog models (e.g. `openai` for `openai-codex`), and the offline Codex\n * catalog (when `providerHint === 'openai-codex'`). Each source is\n * deduplicated by model id; the saved allowlist ids appear first so the\n * user's explicit preferences float to the top.\n *\n * Priority:\n * 1. The saved `models` allowlist (if non-empty) \u2014 enriched with catalog\n * metadata or the offline Codex catalog name+description when a same-id\n * entry exists. These ids always come first in the result.\n * 2. Catalog models (base + layered `providers.json` overlay) not already\n * in the saved list \u2014 this ensures the curated overlay is *never* hidden\n * by a stale saved model list. An empty `[]` saved list (not `undefined`)\n * still shows the full catalog.\n * 3. Sibling catalog models (when `siblingCatalog` is provided) \u2014 e.g. the\n * `openai` models from models.dev for `openai-codex` subscribers. Merged\n * only when the sibling is a different provider from the primary catalog.\n * 4. Offline Codex catalog (`CODEX_MODELS`) for `openai-codex` \u2014 fallback\n * when both saved list and catalog are empty/unavailable. This guarantees\n * ChatGPT sign-in users always see the canonical model list even fully\n * offline.\n * 5. Otherwise an empty list \u2014 *never* an error. A provider the user saved\n * that is neither in the catalog, the curated overlay, nor the offline\n * Codex catalog simply has no suggestions yet; callers must not raise a\n * toast for that case (doing so produced the \"not found in catalog\"\n * notification flood when the WebUI model switcher lazy-loaded every\n * saved provider).\n */\nexport function resolveProviderModelList(\n savedModels: string[] | undefined,\n catalog: ResolvedProvider | undefined,\n /**\n * Provider id / wire family the list is being resolved for. Lets the\n * resolver fall back to a known offline catalog (currently the ChatGPT\n * `openai-codex` models) when the saved allowlist is empty AND the\n * models.dev catalog is unavailable \u2014 so deleting the models from config\n * never leaves the provider showing zero models.\n */\n providerHint?: string | undefined,\n /**\n * Optional sibling catalog \u2014 e.g. the `openai` catalog when resolving\n * `openai-codex` models. Merged alongside the primary `catalog` so users\n * of subscription/OAuth providers also see the wire-family models they\n * may have access to (OpenAI models for ChatGPT Codex subscribers).\n */\n siblingCatalog?: ResolvedProvider | undefined,\n): ProviderModelDescriptor[] {\n const byId = new Map((catalog?.models ?? []).map((m) => [m.id, m]));\n const seen = new Set<string>();\n const out: ProviderModelDescriptor[] = [];\n\n // 1. Saved model allowlist \u2014 user's explicit preferences come first.\n if (savedModels && savedModels.length > 0) {\n for (const id of savedModels) {\n seen.add(id);\n // OAuth / subscription ids (openai-codex) are absent from the models.dev\n // catalog, so layer their canonical name + description on top: enrich a\n // catalog hit with the blurb, or synthesize a full descriptor from it.\n const codex = codexModelMeta(id);\n const hit = byId.get(id);\n if (hit) {\n const described = describeCatalogModel(hit);\n out.push(codex ? { ...described, description: codex.description } : described);\n } else if (codex) {\n out.push({ id, name: codex.name, description: codex.description, capabilities: [] });\n } else {\n out.push({ id, name: id, capabilities: [] });\n }\n }\n }\n\n // 2. Catalog models (base + overlay) \u2014 merge in models not already listed,\n // so the curated overlay (e.g. openai-codex from providers.json) is\n // never hidden by a stale saved model list. This also ensures the\n // \"fetch models from server\" action in the WebUI never loses models.\n for (const [id, m] of byId) {\n if (!seen.has(id)) {\n out.push(describeCatalogModel(m));\n seen.add(id);\n }\n }\n\n // 2b. Sibling catalog models \u2014 e.g. OpenAI models for openai-codex users\n // who may have access through their subscription. Merged only when\n // the sibling provider is different from the primary catalog.\n if (siblingCatalog && siblingCatalog !== catalog) {\n for (const m of siblingCatalog.models) {\n if (!seen.has(m.id)) {\n out.push(describeCatalogModel(m));\n seen.add(m.id);\n }\n }\n }\n\n // 3. Offline fallback \u2014 when both saved list and catalog are empty, surface\n // the known Codex catalog so ChatGPT sign-in users always see models.\n if (providerHint === 'openai-codex') {\n for (const m of CODEX_MODELS) {\n if (!seen.has(m.id)) {\n out.push({ id: m.id, name: m.name, description: m.description, capabilities: [] });\n seen.add(m.id);\n }\n }\n }\n\n return out;\n}\n", "/**\n * Offline **floor** for the Alibaba Cloud Model Studio Token Plan (Personal\n * Edition) models, sourced from the official documentation at:\n * https://www.alibabacloud.com/help/en/model-studio/token-plan-personal-overview\n *\n * Source-of-truth precedence (live path wins):\n * 1. The curated overlay `packages/cli/data/providers.json` \u2014 provider\n * `alibaba-token-plan` \u2014 synced from the official doc + cached + bundled\n * offline by `DefaultModelsRegistry` (overlayUrl/overlayFile). Editing\n * that JSON and pushing updates every client without a code release;\n * this is the authoritative list whenever the registry overlay is wired\n * (the CLI path).\n * 2. This hardcoded table \u2014 the last-resort floor used where no overlay is\n * available (a standalone registry without the bundled file) and by the\n * CLI auth-menu login flow, which needs a synchronous id list.\n *\n * Keep this in lockstep with the `alibaba-token-plan` block in\n * `providers.json`; the drift-guard test\n * `packages/cli/tests/alibaba-token-plan-catalog-overlay-sync.test.ts`\n * asserts they agree.\n *\n * \u2500\u2500\u2500 Correction context: models.dev vs Token Plan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * The models.dev catalog (fetched by DefaultModelsRegistry) reports the\n * FULL Alibaba Cloud Model Studio model inventory \u2014 hundreds of models\n * across pay-per-use, legacy snapshots, Team Edition, and Personal Edition.\n * The Token Plan (Personal Edition) is an exact-string allowlist that\n * covers only **11 models** from 5 brands. The following models.dev\n * entries are **NOT in the Personal Edition** and must NOT appear in the\n * provider's model list:\n *\n * \u274C qwen3.6-plus Team Edition only\n * \u274C deepseek-v4-flash Team Edition only\n * \u274C deepseek-v3.2 Team Edition only\n * \u274C qwen-image-2.0 Team Edition only (Qwen image gen)\n * \u274C qwen-image-2.0-pro Team Edition only\n * \u274C kimi-k2.7-code Team Edition only (Moonshot AI)\n * \u274C kimi-k2.6 Team Edition only\n * \u274C kimi-k2.5 Team Edition only\n * \u274C glm-5.1 Team Edition only\n * \u274C glm-5 Team Edition only\n * \u274C MiniMax-M2.5 Team Edition only\n * \u274C qwen3-max Legacy pay-per-use (not in any Token Plan)\n * \u274C qwen3.5-plus / flash Legacy pay-per-use\n * \u274C qwen3-coder-plus Legacy pay-per-use\n * \u274C qwen-long 10M-token model, not in Token Plan\n * \u274C qwen3-math-plus Specialised model, not in Token Plan\n * \u274C qwen3-235b-a22b Open-weight, pay-per-use\n * \u274C wan2.7-t2v-turbo Different Wanx variant, verify availability\n *\n * See packages/cli/data/providers.json for the authoritative model\n * allowlist.\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\n */\n\nexport interface AlibabaTokenPlanModelMeta {\n /** The wire id sent to the backend (e.g. `qwen3.7-max`). */\n id: string;\n /** Human-readable display name shown in pickers. */\n name: string;\n /** One-line capability blurb. */\n description: string;\n /** Capability tags \u2014 which modality/feature this model supports. */\n capabilities: string[];\n /** Context window in tokens (where applicable). */\n contextWindow?: number;\n /** Max output tokens (where documented). */\n maxOutput?: number;\n /** The recommended / latest model \u2014 tagged \"(current)\" in the official UI. */\n current?: boolean;\n /** Explicit input modality declaration (e.g. image-conditioned video models). */\n inputs?: ReadonlyArray<'text' | 'image'>;\n}\n\n/**\n * Token Plan Personal Edition models, newest-first. The first entry is the\n * recommended default (`current`). Order is significant: callers that need a\n * default model id use `ALIBABA_TOKEN_PLAN_MODELS[0]`.\n *\n * Corresponds to the \"Supported models\" table on:\n * https://www.alibabacloud.com/help/en/model-studio/token-plan-personal-overview\n *\n * Last verified: 2026-07-20 (official doc last updated Jul 19, 2026)\n */\nexport const ALIBABA_TOKEN_PLAN_MODELS: ReadonlyArray<AlibabaTokenPlanModelMeta> = [\n // \u2500\u2500 Qwen (flagship) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 id: 'qwen3.8-max-preview',\n name: 'Qwen3.8 Max Preview',\n description:\n '2.4T-parameter multimodal reasoning \u2014 text, image, video, documents. 10% Credits rate during preview. Night discount (22:00-08:00 UTC+8).',\n capabilities: ['reasoning', 'visual-understanding', 'text-generation'],\n contextWindow: 1_000_000, // speculative \u2014 matches Qwen3.7 Max; official docs pending\n maxOutput: 65_536,\n current: true,\n },\n {\n id: 'qwen3.7-max',\n name: 'Qwen3.7 Max',\n description:\n 'Top-tier pure-text reasoning model \u2014 strongest in the Qwen3.7 series. 1M context, thinking mode, function calling.',\n capabilities: ['reasoning', 'text-generation'],\n contextWindow: 1_000_000,\n maxOutput: 65_536,\n },\n {\n id: 'qwen3.7-plus',\n name: 'Qwen3.7 Plus',\n description:\n 'Balanced flagship \u2014 reasoning, visual understanding, text generation. 1M context, built-in tools.',\n capabilities: ['reasoning', 'visual-understanding', 'text-generation'],\n contextWindow: 1_000_000,\n maxOutput: 65_536,\n },\n {\n id: 'qwen3.6-flash',\n name: 'Qwen3.6 Flash',\n description:\n 'Cost-effective reasoning model \u2014 vision + text, 1M context, same context length as flagship at lower cost.',\n capabilities: ['reasoning', 'visual-understanding', 'text-generation'],\n contextWindow: 1_000_000,\n maxOutput: 65_536,\n },\n\n // \u2500\u2500 Zhipu AI \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 id: 'glm-5.2',\n name: 'GLM-5.2',\n description:\n 'Zhipu AI flagship text-generation model \u2014 reasoning, text, 198K context, function calling, structured output.',\n capabilities: ['reasoning', 'text-generation'],\n contextWindow: 198_000,\n maxOutput: 65_536,\n },\n\n // \u2500\u2500 DeepSeek \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 id: 'deepseek-v4-pro',\n name: 'DeepSeek V4 Pro',\n description:\n 'Frontier reasoning + text generation model. 1M context, always-thinking mode.',\n capabilities: ['reasoning', 'text-generation'],\n contextWindow: 1_000_000,\n maxOutput: 65_536,\n },\n\n // \u2500\u2500 Wanx (image generation) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 id: 'wan2.7-image',\n name: 'Wan2.7 Image',\n description: 'Text-to-image generation model by Wanx.',\n capabilities: ['image-generation'],\n },\n {\n id: 'wan2.7-image-pro',\n name: 'Wan2.7 Image Pro',\n description: 'Higher-quality text-to-image generation model by Wanx.',\n capabilities: ['image-generation'],\n },\n\n // \u2500\u2500 HappyHorse (video generation) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 id: 'happyhorse-1.1-t2v',\n name: 'HappyHorse 1.1 T2V',\n description: 'Text-to-video generation model by HappyHorse.',\n capabilities: ['video-generation'],\n },\n {\n id: 'happyhorse-1.1-i2v',\n name: 'HappyHorse 1.1 I2V',\n description: 'Image-to-video generation model by HappyHorse.',\n capabilities: ['video-generation'],\n inputs: ['text', 'image'],\n },\n {\n id: 'happyhorse-1.1-r2v',\n name: 'HappyHorse 1.1 R2V',\n description: 'Reference-to-video generation model by HappyHorse.',\n capabilities: ['video-generation'],\n inputs: ['text', 'image'],\n },\n];\n\nconst BY_ID: ReadonlyMap<string, AlibabaTokenPlanModelMeta> = new Map(\n ALIBABA_TOKEN_PLAN_MODELS.map((m) => [m.id, m]),\n);\n\n/** Look up the canonical metadata for a Token Plan model id, or `undefined`. */\nexport function alibabaTokenPlanModelMeta(\n id: string,\n): AlibabaTokenPlanModelMeta | undefined {\n return BY_ID.get(id);\n}\n", "/**\n * Model Intelligence \u2014 knowledge base of model capabilities, strengths,\n * and task-type suitability. Powers automatic agent \u2192 model routing.\n *\n * Each entry records:\n * - Known strengths (what this model family excels at)\n * - Known weaknesses (what it struggles with)\n * - Best-for task types (coding, planning, security, docs, etc.)\n * - Cost tier (budget / standard / premium)\n *\n * This is a curated dataset updated as new models release. Falls back\n * gracefully for unknown models.\n */\n\nexport interface ModelProfile {\n /** Provider id (e.g. \"anthropic\", \"openai\"). */\n provider: string;\n /** Model id regex \u2014 matches partial ids for a model family. */\n pattern: RegExp;\n /** Human-readable model family name. */\n family: string;\n /** What this model is particularly good at. */\n strengths: string[];\n /** Known limitations. */\n weaknesses?: string[];\n /** Task types this model is the best choice for. Ordered by preference. */\n bestFor: TaskType[];\n /** Task types to avoid with this model (use a different one). */\n avoidFor?: TaskType[];\n /** Approximate cost tier. */\n costTier: 'budget' | 'standard' | 'premium';\n /** Approximate speed tier (relative to other models). */\n speedTier: 'fast' | 'normal' | 'slow';\n /** Minimum recommended context window. */\n minContext?: number;\n}\n\n/** Task categories that map to agent roles. */\nexport type TaskType =\n | 'coding' // general software development\n | 'planning' // architecture, design, strategy\n | 'security' // vulnerability scanning, security review\n | 'docs' // documentation, writing, explanation\n | 'testing' // test writing, test review\n | 'refactoring' // code restructuring, cleanup\n | 'debugging' // bug hunting, error tracing\n | 'data' // data analysis, JSON, DB queries\n | 'frontend' // React, UI, CSS\n | 'backend' // API, server, infrastructure\n | 'review' // code review, PR review\n | 'lightweight' // simple tasks, quick answers\n | 'general'; // fallback \u2014 any task\n\n/** Known model profiles. Patterns are tried in order; first match wins. */\nexport const MODEL_PROFILES: ModelProfile[] = [\n // \u2500\u2500 Anthropic \u2500\u2500\n {\n provider: 'anthropic',\n pattern: /claude-opus/i,\n family: 'Claude Opus',\n strengths: [\n 'complex reasoning',\n 'multi-step planning',\n 'nuanced judgment',\n 'long-form analysis',\n ],\n weaknesses: ['latency', 'cost'],\n bestFor: ['planning', 'security', 'debugging', 'review'],\n costTier: 'premium',\n speedTier: 'slow',\n },\n {\n provider: 'anthropic',\n pattern: /claude-haiku/i,\n family: 'Claude Haiku',\n strengths: ['speed', 'low cost', 'simple tasks'],\n weaknesses: ['complex reasoning', 'long context'],\n bestFor: ['lightweight', 'docs', 'frontend'],\n avoidFor: ['planning', 'security'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n\n // \u2500\u2500 OpenAI \u2500\u2500\n {\n provider: 'openai',\n pattern: /gpt-5|o3|o4/i,\n family: 'GPT-5 / o3 / o4',\n strengths: ['complex reasoning', 'coding', 'multi-modal'],\n bestFor: ['planning', 'coding', 'debugging', 'general'],\n costTier: 'premium',\n speedTier: 'normal',\n },\n {\n provider: 'openai',\n pattern: /gpt-4\\.1|gpt-4o/i,\n family: 'GPT-4.1 / 4o',\n strengths: ['coding', 'balanced reasoning', 'fast'],\n bestFor: ['coding', 'refactoring', 'backend', 'docs'],\n costTier: 'standard',\n speedTier: 'fast',\n },\n {\n provider: 'openai',\n pattern: /gpt-4o-mini/i,\n family: 'GPT-4o Mini',\n strengths: ['speed', 'low cost', 'simple tasks'],\n weaknesses: ['complex reasoning'],\n bestFor: ['lightweight', 'frontend', 'docs'],\n avoidFor: ['planning', 'security'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n\n // \u2500\u2500 Google \u2500\u2500\n {\n provider: 'google',\n pattern: /gemini-(?:2\\.5|3)/i,\n family: 'Gemini 2.5 / 3',\n strengths: ['long context', 'multi-modal', 'coding', 'reasoning'],\n bestFor: ['coding', 'planning', 'data', 'general'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n {\n provider: 'google',\n pattern: /gemini-2\\.0-flash|gemini-flash/i,\n family: 'Gemini Flash',\n strengths: ['speed', 'low cost', 'long context'],\n weaknesses: ['deep reasoning'],\n bestFor: ['lightweight', 'docs', 'frontend', 'data'],\n avoidFor: ['planning', 'security'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n\n // \u2500\u2500 DeepSeek \u2500\u2500\n {\n provider: 'deepseek',\n pattern: /deepseek-v3|deepseek-r1/i,\n family: 'DeepSeek V3 / R1',\n strengths: ['coding', 'math', 'reasoning', 'cost-effective'],\n bestFor: ['coding', 'refactoring', 'debugging', 'general'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n\n // \u2500\u2500 OpenRouter / catch-all \u2500\u2500\n {\n provider: 'openrouter',\n pattern: /.*/,\n family: 'OpenRouter (routed)',\n strengths: ['model variety', 'fallback'],\n bestFor: ['general'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n];\n\n/** Map task types to the agent roles that handle them. */\nexport const TASK_TO_ROLE: Record<TaskType, string[]> = {\n coding: ['executor', 'architect', 'bug-hunter'],\n planning: ['planner', 'architect', 'refactor-planner'],\n security: ['security-scanner', 'security-reviewer'],\n docs: ['document', 'simplifier'],\n testing: ['verifier', 'test', 'e2e'],\n refactoring: ['refactor-planner', 'refactor', 'simplifier'],\n debugging: ['debugger', 'bug-hunter', 'tracer'],\n data: ['analyst', 'data', 'database'],\n frontend: ['frontend', 'designer'],\n backend: ['backend', 'api', 'auth'],\n review: ['reviewer', 'code-reviewer', 'critic'],\n lightweight: ['executor', 'simplifier'],\n general: ['executor', 'analyst'],\n};\n\n/**\n * Infer the most likely task type from a task description and target role.\n * Uses keyword matching; falls back to the role's primary task type.\n */\nexport function inferTaskType(description: string, role?: string): TaskType {\n const d = description.toLowerCase();\n\n // Keyword \u2192 task type mapping\n const keywords: [RegExp, TaskType][] = [\n [/plan|architect|design|strategy|blueprint|system\\s*design/i, 'planning'],\n [/security|vuln|exploit|injection|auth/i, 'security'],\n [/doc|readme|explain|write\\s*(a|the)\\s*doc|tutorial/i, 'docs'],\n [/test|spec|assert|mock|coverage/i, 'testing'],\n [/refactor|clean\\s*up|restructure|simplify/i, 'refactoring'],\n [/bug|fix|debug|trace|crash|error/i, 'debugging'],\n [/data|json|sql|query|analyze|parse/i, 'data'],\n [/frontend|react|ui|css|component|jsx/i, 'frontend'],\n [/backend|api|server|endpoint|route|middleware/i, 'backend'],\n [/review|audit|inspect|check/i, 'review'],\n [/simple|quick|one-liner|trivial/i, 'lightweight'],\n ];\n\n for (const [re, taskType] of keywords) {\n if (re.test(d)) return taskType;\n }\n\n // Fallback: map role to best task type\n if (role) {\n for (const [taskType, roles] of Object.entries(TASK_TO_ROLE)) {\n if (roles.includes(role)) return taskType as TaskType;\n }\n }\n\n return 'general';\n}\n\n/**\n * Find the best matching model profile for a given provider + model id.\n */\nexport function findModelProfile(provider: string, modelId: string): ModelProfile | undefined {\n const candidates = MODEL_PROFILES.filter((p) => p.provider === provider);\n // Try exact match first, then pattern match\n for (const p of candidates) {\n if (p.pattern.test(modelId)) return p;\n }\n return undefined;\n}\n\n/**\n * Score a model for a given task type. Higher = better fit.\n * Returns 0-100.\n */\nexport function scoreModelForTask(profile: ModelProfile | undefined, taskType: TaskType): number {\n if (!profile) return 50; // unknown model \u2014 neutral\n\n // Explicitly avoided \u2192 very low score\n if (profile.avoidFor?.includes(taskType)) return 10;\n\n // Best-for match \u2192 high score\n const bestIdx = profile.bestFor.indexOf(taskType);\n if (bestIdx >= 0) {\n // Earlier in the list = better fit\n return 90 - bestIdx * 10;\n }\n\n // Cost/speed adjustments\n let score = 50;\n if (taskType === 'lightweight') {\n score += profile.costTier === 'budget' ? 30 : profile.costTier === 'standard' ? 15 : 0;\n score += profile.speedTier === 'fast' ? 20 : 0;\n }\n if (taskType === 'planning' || taskType === 'security') {\n score += profile.costTier === 'premium' ? 20 : 0;\n score += profile.speedTier === 'slow' ? 10 : 0; // slow = thorough\n }\n\n return score;\n}\n", "/**\n * ModelRouter \u2014 intelligent model selection for subagent delegation.\n *\n * Combines:\n * 1. User-configured model matrix (/setmodel overrides)\n * 2. Model intelligence profiles (strengths/weaknesses per model family)\n * 3. Provider availability (which models have API keys configured)\n * 4. Per-model cost tracking\n *\n * Usage:\n * const router = new ModelRouter({ matrix, config, profiles: MODEL_PROFILES });\n * const pick = router.pickForTask('security-scanner', 'Audit the auth module');\n * // \u2192 { provider: 'anthropic', model: 'matched-model', reason: 'best-for security' }\n */\n\nimport type { ModelMatrixEntry, ProviderConfig } from '../types/config.js';\n\nexport interface ModelIntelligenceEntry {\n provider: string;\n pattern: RegExp;\n family: string;\n strengths: string[];\n weaknesses?: string[];\n bestFor: string[];\n avoidFor?: string[];\n costTier: 'budget' | 'standard' | 'premium';\n speedTier: 'fast' | 'normal' | 'slow';\n minContext?: number;\n}\n\nexport interface RouterConfig {\n /** User-configured model matrix (from /setmodel). */\n matrix?: Record<string, ModelMatrixEntry> | undefined;\n /** Provider configurations (to check API key availability). */\n config: {\n provider: string;\n model: string;\n providers?: Record<string, ProviderConfig>;\n };\n /** Known model intelligence profiles. */\n profiles?: ModelIntelligenceEntry[] | undefined;\n}\n\nexport interface ModelPick {\n provider: string;\n model: string;\n /** Why this model was chosen. */\n reason: string;\n /** Whether this came from the user's matrix (true) or auto-detected (false). */\n fromMatrix: boolean;\n}\n\nexport interface RouterCosts {\n /** Cumulative cost per provider/model key. */\n byModel: Record<\n string,\n { cost: number; tokens: { input: number; output: number }; calls: number }\n >;\n /** Grand total. */\n totalCost: number;\n}\n\n/**\n * Default profiles used when none are provided. Kept inline so the router\n * is self-contained \u2014 callers can override with richer data from model-intelligence.\n */\nconst DEFAULT_PROFILES: ModelIntelligenceEntry[] = [\n {\n provider: 'anthropic',\n pattern: /claude-opus/i,\n family: 'Claude Opus',\n strengths: ['reasoning', 'planning'],\n bestFor: ['planning', 'security', 'debugging'],\n costTier: 'premium',\n speedTier: 'slow',\n },\n {\n provider: 'anthropic',\n pattern: /claude-haiku/i,\n family: 'Claude Haiku',\n strengths: ['speed'],\n bestFor: ['lightweight', 'docs'],\n avoidFor: ['planning'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n {\n provider: 'openai',\n pattern: /gpt-5|o3|o4/i,\n family: 'GPT-5/o3/o4',\n strengths: ['reasoning', 'coding'],\n bestFor: ['planning', 'coding', 'debugging'],\n costTier: 'premium',\n speedTier: 'normal',\n },\n {\n provider: 'openai',\n pattern: /gpt-4/i,\n family: 'GPT-4',\n strengths: ['coding'],\n bestFor: ['coding', 'docs'],\n costTier: 'standard',\n speedTier: 'fast',\n },\n {\n provider: 'openai',\n pattern: /gpt-4o-mini/i,\n family: 'GPT-4o Mini',\n strengths: ['speed'],\n bestFor: ['lightweight', 'docs'],\n avoidFor: ['planning'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n {\n provider: 'google',\n pattern: /gemini-(?:2\\.5|3)/i,\n family: 'Gemini 2.5/3',\n strengths: ['context', 'coding'],\n bestFor: ['coding', 'data'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n {\n provider: 'google',\n pattern: /gemini.*flash/i,\n family: 'Gemini Flash',\n strengths: ['speed'],\n bestFor: ['lightweight', 'docs'],\n avoidFor: ['planning'],\n costTier: 'budget',\n speedTier: 'fast',\n },\n {\n provider: 'deepseek',\n pattern: /deepseek/i,\n family: 'DeepSeek',\n strengths: ['coding', 'cost-effective'],\n bestFor: ['coding', 'general'],\n costTier: 'standard',\n speedTier: 'normal',\n },\n];\n\n/** Map common task keywords to categories for model matching. */\nconst TASK_CATEGORIES: Record<string, string> = {\n plan: 'planning',\n architect: 'planning',\n design: 'planning',\n strategy: 'planning',\n security: 'security',\n vuln: 'security',\n exploit: 'security',\n auth: 'security',\n doc: 'docs',\n readme: 'docs',\n explain: 'docs',\n write: 'docs',\n test: 'testing',\n spec: 'testing',\n assert: 'testing',\n coverage: 'testing',\n refactor: 'refactoring',\n cleanup: 'refactoring',\n restructure: 'refactoring',\n bug: 'debugging',\n fix: 'debugging',\n debug: 'debugging',\n trace: 'debugging',\n crash: 'debugging',\n data: 'data',\n json: 'data',\n sql: 'data',\n query: 'data',\n analyze: 'data',\n parse: 'data',\n frontend: 'frontend',\n react: 'frontend',\n ui: 'frontend',\n css: 'frontend',\n component: 'frontend',\n backend: 'backend',\n api: 'backend',\n server: 'backend',\n endpoint: 'backend',\n review: 'review',\n audit: 'review',\n inspect: 'review',\n simple: 'lightweight',\n quick: 'lightweight',\n trivial: 'lightweight',\n};\n\nexport class ModelRouter {\n private profiles: ModelIntelligenceEntry[];\n private matrix: Record<string, ModelMatrixEntry>;\n private config: RouterConfig['config'];\n private costs: RouterCosts = { byModel: {}, totalCost: 0 };\n\n constructor(opts: RouterConfig) {\n this.profiles = opts.profiles ?? DEFAULT_PROFILES;\n this.matrix = opts.matrix ?? {};\n this.config = opts.config;\n }\n\n /** Look up the model matrix resolution for a role (role \u2192 phase \u2192 * \u2192 leader). */\n private resolveMatrix(role: string): ModelMatrixEntry | undefined {\n // Exact role match\n if (this.matrix[role]) return this.matrix[role];\n // Phase match\n const phase = this.roleToPhase(role);\n if (phase && this.matrix[phase]) return this.matrix[phase];\n // Wildcard\n if (this.matrix['*']) return this.matrix['*'];\n return undefined;\n }\n\n /** Simplified phase lookup for common roles. */\n private roleToPhase(role: string): string | undefined {\n const phaseMap: Record<string, string> = {\n planner: 'plan',\n architect: 'plan',\n 'refactor-planner': 'plan',\n executor: 'code',\n refactor: 'code',\n simplifier: 'code',\n migration: 'code',\n 'bug-hunter': 'code',\n debugger: 'code',\n tracer: 'code',\n test: 'code',\n verifier: 'code',\n e2e: 'code',\n performance: 'code',\n chaos: 'code',\n 'security-scanner': 'review',\n 'security-reviewer': 'review',\n 'code-reviewer': 'review',\n reviewer: 'review',\n critic: 'review',\n accessibility: 'review',\n compliance: 'review',\n analyst: 'code',\n data: 'code',\n database: 'code',\n frontend: 'code',\n backend: 'code',\n api: 'code',\n auth: 'code',\n designer: 'code',\n document: 'code',\n researcher: 'plan',\n explore: 'plan',\n search: 'plan',\n };\n return phaseMap[role];\n }\n\n /**\n * Pick the best model for a task.\n *\n * Priority:\n * 1. User's /setmodel matrix entry for this role (explicit override)\n * 2. Best-matching model from intelligence profiles\n * 3. Leader model (fallback)\n */\n pickForTask(role: string, description: string): ModelPick {\n // 1. Check user matrix\n const matrixEntry = this.resolveMatrix(role);\n if (matrixEntry?.model) {\n const provider = matrixEntry.provider ?? this.config.provider;\n return {\n provider,\n model: matrixEntry.model,\n reason: `matrix override for role ${role}`,\n fromMatrix: true,\n };\n }\n\n // 2. Auto-detect from task description + role\n const category = this.inferCategory(description, role);\n const best = this.findBestModel(category);\n if (best) {\n return { ...best, reason: `best-for ${category} (auto-detected)`, fromMatrix: false };\n }\n\n // 3. Leader fallback\n return {\n provider: this.config.provider,\n model: this.config.model,\n reason: 'leader fallback',\n fromMatrix: false,\n };\n }\n\n /** Infer task category from description keywords + role. */\n private inferCategory(description: string, role: string): string {\n const d = description.toLowerCase();\n for (const [keyword, category] of Object.entries(TASK_CATEGORIES)) {\n if (d.includes(keyword)) return category;\n }\n // Map role to category\n const roleMap: Record<string, string> = {\n 'security-scanner': 'security',\n 'security-reviewer': 'security',\n 'bug-hunter': 'debugging',\n debugger: 'debugging',\n planner: 'planning',\n architect: 'planning',\n 'refactor-planner': 'refactoring',\n refactor: 'refactoring',\n test: 'testing',\n e2e: 'testing',\n verifier: 'testing',\n document: 'docs',\n simplifier: 'docs',\n 'code-reviewer': 'review',\n reviewer: 'review',\n critic: 'review',\n frontend: 'frontend',\n backend: 'backend',\n };\n return roleMap[role] ?? 'general';\n }\n\n /** Find the best available model for a task category. */\n private findBestModel(category: string): { provider: string; model: string } | undefined {\n // Score each profile for this category\n const scored = this.profiles\n .filter((p) => this.hasKey(p.provider))\n .map((p) => {\n let score = 50;\n if (p.bestFor.includes(category)) score += 40;\n if (p.avoidFor?.includes(category)) score -= 50;\n if (category === 'lightweight' && p.costTier === 'budget') score += 20;\n if (category === 'planning' && p.costTier === 'premium') score += 15;\n if (category === 'planning' && p.speedTier === 'slow') score += 10; // slow = thorough\n return { profile: p, score };\n })\n .filter((s) => s.score > 0)\n .sort((a, b) => b.score - a.score);\n\n if (scored.length === 0) return undefined;\n\n // Guarded by scored.length === 0 check above \u2014 but use optional\n // chaining instead of `!` to satisfy the non-null assertion lint rule.\n const best = scored[0]?.profile;\n if (!best) return undefined;\n // Find the actual model id from the provider's configured models\n const models = this.getProviderModels(best.provider);\n const match = models.find((m) => best.pattern.test(m)) ?? models[0];\n if (!match) return undefined;\n\n return { provider: best.provider, model: match };\n }\n\n /** Check if a provider has an API key configured. */\n private hasKey(providerId: string): boolean {\n const pc = this.config.providers?.[providerId];\n if (!pc) return providerId === this.config.provider;\n if (typeof pc.apiKey === 'string' && pc.apiKey.length > 0) return true;\n if (Array.isArray(pc.apiKeys) && pc.apiKeys.some((k) => k?.apiKey)) return true;\n return false;\n }\n\n /** Get the configured model list for a provider. */\n private getProviderModels(providerId: string): string[] {\n const pc = this.config.providers?.[providerId];\n return pc?.models ?? [];\n }\n\n /** Record cost for a model. */\n recordCost(\n provider: string,\n model: string,\n cost: number,\n tokens?: { input: number; output: number },\n ): void {\n const key = `${provider}/${model}`;\n const entry = this.costs.byModel[key] ?? { cost: 0, tokens: { input: 0, output: 0 }, calls: 0 };\n entry.cost += cost;\n if (tokens) {\n entry.tokens.input += tokens.input;\n entry.tokens.output += tokens.output;\n }\n entry.calls += 1;\n this.costs.byModel[key] = entry;\n this.costs.totalCost += cost;\n }\n\n /** Get cumulative costs. */\n getCosts(): RouterCosts {\n return { ...this.costs, byModel: { ...this.costs.byModel } };\n }\n\n /** Reset cost tracking. */\n resetCosts(): void {\n this.costs = { byModel: {}, totalCost: 0 };\n }\n\n /** List all available providers with their best model for each task category. */\n suggestMatrix(): Record<string, ModelPick> {\n const matrix: Record<string, ModelPick> = {};\n const roles = [\n 'security-scanner',\n 'bug-hunter',\n 'planner',\n 'architect',\n 'refactor-planner',\n 'test',\n 'document',\n 'code-reviewer',\n 'reviewer',\n 'verifier',\n 'executor',\n 'debugger',\n 'analyst',\n ];\n\n for (const role of roles) {\n // Don't override explicit user matrix entries\n if (this.matrix[role]) continue;\n\n const pick = this.pickForTask(role, role);\n if (!pick.fromMatrix) {\n matrix[role] = pick;\n }\n }\n\n return matrix;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["fs", "path", "open", "stat", "resolve", "fs", "path", "path", "stat", "readFileSync", "statSync", "path", "fileURLToPath", "fileURLToPath", "isDirectory", "statSync", "BY_ID"]
7
7
  }