@agimon-ai/doompi-cache 0.0.1-alpha.2 → 0.0.1-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -22
- package/dist/extensions/pi.cjs +1 -2
- package/dist/extensions/pi.cjs.map +1 -1
- package/dist/extensions/pi.d.cts.map +1 -1
- package/dist/extensions/pi.d.mts.map +1 -1
- package/dist/extensions/pi.mjs +1 -2
- package/dist/extensions/pi.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -2
- package/dist/index.d.mts +1 -2
- package/dist/index.mjs +1 -1
- package/dist/pi-cache-optimizer-D5_7O2Zu.mjs +59 -0
- package/dist/pi-cache-optimizer-D5_7O2Zu.mjs.map +1 -0
- package/dist/pi-cache-optimizer-DXfBHbvW.cjs +59 -0
- package/dist/pi-cache-optimizer-DXfBHbvW.cjs.map +1 -0
- package/llms.txt +1 -1
- package/package.json +16 -8
- package/src/prompts/doompi-use-cache/SKILL.md +1 -2
- package/dist/pi-cache-optimizer-B7B911Pz.cjs +0 -54
- package/dist/pi-cache-optimizer-B7B911Pz.cjs.map +0 -1
- package/dist/pi-cache-optimizer-CjPVk06Q.mjs +0 -54
- package/dist/pi-cache-optimizer-CjPVk06Q.mjs.map +0 -1
- package/dist/services/status.cjs +0 -3
- package/dist/services/status.cjs.map +0 -1
- package/dist/services/status.d.cts +0 -6
- package/dist/services/status.d.cts.map +0 -1
- package/dist/services/status.d.mts +0 -6
- package/dist/services/status.d.mts.map +0 -1
- package/dist/services/status.mjs +0 -3
- package/dist/services/status.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pi-cache-optimizer-CjPVk06Q.mjs","names":["fsConstants"],"sources":["../../../../node_modules/.pnpm/pi-cache-optimizer@2.8.3_@earendil-works+pi-coding-agent@0.84.2_zod@4.4.3_/node_modules/pi-cache-optimizer/index.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { chmod, copyFile, mkdir, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { constants as fsConstants, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport {\n getAgentDir,\n type BuildSystemPromptOptions,\n type ExtensionAPI,\n type ExtensionCommandContext,\n type ExtensionContext,\n} from \"@earendil-works/pi-coding-agent\";\n\ntype MutableEnv = Record<string, string | undefined>;\n\ntype CacheRetentionEnvSnapshot = {\n wasSet: boolean;\n value?: string;\n};\n\nconst PI_CACHE_RETENTION_ENV = \"PI_CACHE_RETENTION\";\nconst LONG_CACHE_RETENTION_VALUE = \"long\";\nconst PI_CACHE_RETENTION_BASELINE_SYMBOL = Symbol.for(\"pi.cache.optimizer.retention-baseline.v1\");\n\ntype CacheRetentionBaselineV1 = {\n version: 1;\n snapshot: CacheRetentionEnvSnapshot;\n};\n\nfunction captureCacheRetentionEnv(env: MutableEnv = process.env): CacheRetentionEnvSnapshot {\n return {\n wasSet: Object.prototype.hasOwnProperty.call(env, PI_CACHE_RETENTION_ENV),\n value: env[PI_CACHE_RETENTION_ENV],\n };\n}\n\nfunction requestLongCacheRetention(env: MutableEnv = process.env): void {\n if (!env[PI_CACHE_RETENTION_ENV] || env[PI_CACHE_RETENTION_ENV] !== LONG_CACHE_RETENTION_VALUE) {\n env[PI_CACHE_RETENTION_ENV] = LONG_CACHE_RETENTION_VALUE;\n }\n}\n\nfunction restoreCacheRetentionEnv(snapshot: CacheRetentionEnvSnapshot, env: MutableEnv = process.env): void {\n if (snapshot.wasSet) {\n env[PI_CACHE_RETENTION_ENV] = snapshot.value;\n } else {\n delete env[PI_CACHE_RETENTION_ENV];\n }\n}\n\nfunction isCacheRetentionBaselineV1(value: unknown): value is CacheRetentionBaselineV1 {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as { version?: unknown; snapshot?: unknown };\n if (record.version !== 1 || typeof record.snapshot !== \"object\" || record.snapshot === null) return false;\n const snapshot = record.snapshot as { wasSet?: unknown; value?: unknown };\n return typeof snapshot.wasSet === \"boolean\" &&\n (snapshot.value === undefined || typeof snapshot.value === \"string\");\n}\n\nfunction getOrCaptureCacheRetentionBaseline(\n env: MutableEnv = process.env,\n globals: Record<symbol, unknown> = globalThis as Record<symbol, unknown>,\n): CacheRetentionEnvSnapshot {\n const existing = globals[PI_CACHE_RETENTION_BASELINE_SYMBOL];\n if (isCacheRetentionBaselineV1(existing)) return { ...existing.snapshot };\n\n const snapshot = captureCacheRetentionEnv(env);\n globals[PI_CACHE_RETENTION_BASELINE_SYMBOL] = { version: 1, snapshot: { ...snapshot } };\n return snapshot;\n}\n\nconst STARTUP_CACHE_RETENTION_ENV = getOrCaptureCacheRetentionBaseline();\n\n/**\n * Pi Cache Optimizer (formerly pi-deepseek-cache-optimizer)\n *\n * What it does:\n * 1. Reorders Pi's system prompt so stable content is sent before dynamic context.\n * 2. Sets PI_CACHE_RETENTION=long at extension load time.\n * 3. Warns once for provider/model cache compat gaps where the signal is conservative.\n * 4. Shows lightweight persisted provider-specific cache stats in Pi's footer.\n *\n * Provider prompt/KV caches are provider-side and best-effort. This extension improves\n * the odds of cache hits; it cannot guarantee hits, especially through proxies.\n */\n\n// ============================================================\n// Automatically request long prompt-cache retention when Pi supports it.\n// /cache-optimizer disable restores the startup value for this Pi process.\n// ============================================================\nrequestLongCacheRetention();\n\ntype PiModel = NonNullable<ExtensionContext[\"model\"]>;\ntype ModelIdentity = Pick<PiModel, \"provider\" | \"id\">;\ntype UnknownRecord = Record<string, unknown>;\ntype CacheProviderId = \"deepseek\" | \"openai\" | \"claude\" | \"gemini\";\n\nconst LOG_PREFIX = \"pi-cache-optimizer\";\nconst STATUS_KEY = \"pi-cache-stats\";\n\n/** Use Pi core's resolver so rebranded config names and env semantics stay aligned. */\nconst STATE_DIR = getAgentDir();\nconst STATE_FILE_PATH = join(STATE_DIR, \"pi-cache-optimizer-stats.json\");\nconst LEGACY_STATE_FILE_PATH = join(STATE_DIR, \"deepseek-cache-optimizer-stats.json\");\nconst CONFIG_FILE_PATH = join(STATE_DIR, \"pi-cache-optimizer-config.json\");\nconst CACHE_PROVIDER_IDS: CacheProviderId[] = [\"deepseek\", \"openai\", \"claude\", \"gemini\"];\nconst OPENAI_CACHE_KEY_ENV = \"PI_CACHE_OPTIMIZER_OPENAI_CACHE_KEY\";\nconst NO_OPENAI_CACHE_KEY_ENV = \"PI_CACHE_OPTIMIZER_NO_OPENAI_CACHE_KEY\";\nconst OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;\nconst NO_SKILL_COMPRESSION_ENV = \"PI_CACHE_OPTIMIZER_NO_SKILL_COMPRESSION\";\nconst NO_PROMPT_REWRITE_ENV = \"PI_CACHE_OPTIMIZER_NO_PROMPT_REWRITE\";\nconst FOOTER_MODE_ENV = \"PI_CACHE_OPTIMIZER_FOOTER_MODE\";\ntype FooterStatsMode = \"session\" | \"total\" | \"process\";\ntype FooterStatsModeSource = \"config\" | \"env\" | \"default\";\ntype PersistedCacheOptimizerConfigV1 = {\n version: 1;\n footerMode?: FooterStatsMode;\n};\nconst PI_ROUTING_REGISTRY_SYMBOL = Symbol.for(\"pi.routing.registry.v1\");\nconst PI_CACHE_HINTS_SYMBOL = Symbol.for(\"pi.cache.hints.v1\");\nconst PI_CACHE_HINTS_OWNER_SYMBOL = Symbol.for(\"pi.cache.optimizer.hints-owner.v1\");\nconst ANTHROPIC_TTL_FALLBACK_SYMBOL = Symbol.for(\"pi.cache.optimizer.anthropic-ttl-fallback.v1\");\n\ntype AnthropicTtlFallbackStateV1 = {\n version: 1;\n modelKeys: Set<string>;\n warnedModelKeys: Set<string>;\n};\n\nfunction getAnthropicTtlFallbackState(): AnthropicTtlFallbackStateV1 {\n const globals = globalThis as Record<symbol, unknown>;\n const existing = globals[ANTHROPIC_TTL_FALLBACK_SYMBOL] as Partial<AnthropicTtlFallbackStateV1> | undefined;\n if (\n existing?.version === 1 &&\n existing.modelKeys instanceof Set &&\n existing.warnedModelKeys instanceof Set\n ) {\n return existing as AnthropicTtlFallbackStateV1;\n }\n const state: AnthropicTtlFallbackStateV1 = {\n version: 1,\n modelKeys: new Set<string>(),\n warnedModelKeys: new Set<string>(),\n };\n globals[ANTHROPIC_TTL_FALLBACK_SYMBOL] = state;\n return state;\n}\n\nlet runtimeOptimizerEnabled = true;\n\n// WORM-flag: if optimizeSystemPrompt ever detects that candidate extraction\n// has accidentally truncated a structural marker (any XML tag or\n// HTML comment boundary marker present in the original prompt), we flip\n// this. publishStatus reads it once, appends a footer warning, then\n// resets it. The flag surface is kept separate from the regular\n// cache-stats counter so that a one-turn glitch doesn't poison the\n// persisted metrics.\nlet promptTruncationDetected = false;\n\n// Timestamp (ms) of the most recent integrity truncation event.\n// Used by /cache-optimizer doctor to surface recovery guidance.\n// Reset to 0 on reload.\nlet lastPromptIntegrityWarningAt = 0;\n\n/** Getter for lastPromptIntegrityWarningAt (exported for tests via __internals_for_tests). */\nfunction getLastPromptIntegrityWarningAt(): number {\n return lastPromptIntegrityWarningAt;\n}\n\n// Minimum count of skills before compression is worth applying.\n// Below this, pi's verbose XML block is small enough that the overhead of\n// an additional one-line index isn't worth the loss of per-skill\n// description hints. The 31-skill snapshot in this repo was 13.3 KB; one\n// or two skills is well under 1 KB and not worth touching.\nconst SKILL_COMPRESSION_MIN_COUNT = 4;\n\n// Minimum trimmed length for a candidate to qualify as a stable-prefix \"part\".\n//\n// `optimizeSystemPrompt` removes each uniquely occurring accepted candidate\n// from the dynamic remainder. Short or character-class candidates (think: `S`,\n// `- u`, `- (`, `- }`) are too likely to match unrelated prompt text, so they\n// are never eligible for lifting.\n//\n// The threshold also caps the upstream string-vs-array regression we saw with\n// trellis 0.5.16 / 0.6.0-beta.17 (subagent tool registration passing\n// `promptGuidelines: \"<long string>\"` instead of `[\"<long string>\"]`, which\n// pi then iterates char-by-char). Even if a similar bug recurs upstream, this\n// extension will not lift its single-character byproducts into the stable\n// prefix candidate list.\n//\n// 8 chars is comfortably above all single-bullet (`- X` = 3 chars) and\n// short-token noise while leaving every legitimate guideline / tool snippet /\n// context-file payload above the bar. If a real future guideline is shorter\n// than 8 chars, the cost is that it is not lifted into the stable prefix; the\n// dynamic-remainder path still includes it untouched.\nconst MIN_STABLE_CANDIDATE_LENGTH = 8;\n\nconst ASSISTANT_MESSAGE_MODEL_TOKEN_KEYS = [\"model\", \"name\"];\nconst OPENAI_REASONING_MODEL_PATTERN = /(^|[/\\s:_-])o[1345]($|[-_.:/\\s])/;\nconst XAI_MODEL_PATTERN = /(^|[/\\s:_-])xai($|[-_.:/\\s])/;\nconst MIMO_MODEL_PATTERN = /(^|[/\\s:_-])mi-?mo($|[-_.:/\\s])/i;\nconst PPLX_MODEL_PATTERN = /(^|[/\\s:_-])pplx($|[-_.:/\\s])/i;\nconst NOVA_MODEL_PATTERN = /(^|[/\\s:_-])nova($|[-_.:/\\s])/i;\nconst MPT_MODEL_PATTERN = /(^|[/\\s:_-])mpt($|[-_.:/\\s])/i;\nconst ALEPH_MODEL_PATTERN = /(^|[/\\s:_-])aleph($|[-_.:/\\s])/i;\n\n// Safe-boundary patterns for models with short or ambiguous tokens\nconst ARCTIC_MODEL_PATTERN = /(^|[\\/\\s:_-])arctic($|[\\-_.:\\/\\s])/i;\nconst AYA_MODEL_PATTERN = /(^|[\\/\\s:_-])aya($|[\\-_.:\\/\\s])/i;\nconst ORION_MODEL_PATTERN = /(^|[\\/\\s:_-])orion($|[\\-_.:\\/\\s])/i;\n\ntype CacheCompat = {\n supportsStore?: boolean;\n supportsDeveloperRole?: boolean;\n supportsReasoningEffort?: boolean;\n supportsUsageInStreaming?: boolean;\n supportsStrictMode?: boolean;\n maxTokensField?: \"max_completion_tokens\" | \"max_tokens\";\n sendSessionAffinityHeaders?: boolean;\n sessionAffinityFormat?: \"openai\" | \"openai-nosession\" | \"openrouter\";\n supportsLongCacheRetention?: boolean;\n thinkingFormat?: string;\n requiresReasoningContentOnAssistantMessages?: boolean;\n cacheControlFormat?: string;\n forceAdaptiveThinking?: boolean;\n allowEmptySignature?: boolean;\n};\n\ntype CacheStats = {\n day: string;\n totalRequests: number;\n hitRequests: number;\n cachedInputTokens: number;\n cacheWriteInputTokens: number;\n totalInputTokens: number;\n};\n\ntype PersistedCacheStatsV2 = {\n version: 2;\n statsByProvider: Partial<Record<CacheProviderId, CacheStats>>;\n};\n\n/** Per-model-key scoped state. Used in memory and for v3 persistence. */\ntype PersistedRoutedModelRef = {\n provider: string;\n id: string;\n name?: string;\n};\n\ntype PiRouteSnapshot = {\n virtualProvider: string;\n virtualModelId: string;\n provider: string;\n modelId: string;\n api?: string;\n canonicalModelId?: string;\n routeLabel?: string;\n status?: \"planned\" | \"trying\" | \"selected\" | \"success\" | \"failed\";\n sessionIdHash?: string;\n requestId?: string;\n timestamp: number;\n};\n\ntype PiRouteResolveHint = {\n sessionIdHash?: string;\n requestId?: string;\n};\n\ntype PiRouterAdapterV1 = {\n virtualProvider: string;\n resolveActiveRoute(\n virtualModelId: string,\n hint?: PiRouteResolveHint,\n ): PiRouteSnapshot | undefined;\n resolveCandidateRoutes?(virtualModelId: string): PiRouteSnapshot[];\n subscribe?(listener: (event: PiRouteSnapshot) => void): () => void;\n};\n\ntype PiRoutingRegistryV1 = {\n version: 1;\n registerRouter(adapter: PiRouterAdapterV1): () => void;\n getRouter(virtualProvider: string): PiRouterAdapterV1 | undefined;\n};\n\ntype PiCacheHintsInput = {\n sessionIdHash?: string;\n virtualProvider?: string;\n virtualModelId?: string;\n upstreamProvider?: string;\n upstreamModelId?: string;\n api?: string;\n};\n\ntype PiCacheHintsOutput = {\n systemPrompt?: string;\n promptCacheKey?: string;\n cacheRetention?: \"long\";\n};\n\ntype PiCacheHintSnapshot = PiCacheHintsInput & PiCacheHintsOutput & {\n timestamp: number;\n};\n\ntype PiCacheHintsV1 = {\n version: 1;\n getHints(input: PiCacheHintsInput): PiCacheHintsOutput | undefined;\n};\n\ntype ProtocolGlobal = typeof globalThis & Record<symbol, unknown> & {\n __piCacheOptimizerRouter?: unknown;\n __piCacheOptimizerCacheKey__?: unknown;\n};\n\ntype ModelRegistryLike = {\n find?(provider: string, modelId: string): PiModel | undefined;\n getAvailable?(): PiModel[];\n getAll?(): PiModel[];\n};\n\ntype ContextWithOptionalModelRegistry = Pick<ExtensionContext, \"sessionManager\"> & {\n modelRegistry?: ModelRegistryLike;\n};\n\ntype CacheStatsState = {\n statsByModel: Record<string, CacheStats>;\n totalsByModel: Record<string, CacheStats>;\n legacyFamily: Partial<Record<CacheProviderId, CacheStats>>;\n lastRoutedModelBySession?: Record<string, PersistedRoutedModelRef>;\n};\n\ntype PersistedCacheStatsV3 = {\n version: 3;\n statsByModel: Record<string, CacheStats>;\n legacyFamily: Partial<Record<CacheProviderId, CacheStats>>;\n};\n\n/**\n * V4 format: session-scoped stats buckets.\n * Each Pi process/session gets its own stats isolated by a hashed session id.\n *\n * sessions: sessionHash → modelKey (provider/id) → CacheStats\n * legacyFamily: unchanged from v3 (migration/fallback when ctx.model is unknown)\n */\ntype PersistedCacheStatsV4 = {\n version: 4;\n sessions: Record<string, Record<string, CacheStats>>;\n legacyFamily: Partial<Record<CacheProviderId, CacheStats>>;\n};\n\ntype PersistedCacheStatsV5 = {\n version: 5;\n sessions: Record<string, Record<string, CacheStats>>;\n legacyFamily: Partial<Record<CacheProviderId, CacheStats>>;\n lastRoutedModelBySession?: Record<string, PersistedRoutedModelRef>;\n};\n\ntype PersistedCacheStatsV6 = {\n version: 6;\n sessions: Record<string, Record<string, CacheStats>>;\n totalsByModel: Record<string, CacheStats>;\n legacyFamily: Partial<Record<CacheProviderId, CacheStats>>;\n lastRoutedModelBySession?: Record<string, PersistedRoutedModelRef>;\n};\n\ntype UsageSnapshot = {\n cacheRead: number;\n cacheWrite: number;\n totalInput: number;\n};\n\ntype OptimizedSystemPrompt = {\n systemPrompt: string;\n stablePrefix: string;\n changed: boolean;\n};\n\n/**\n * Per-request sample stored for trend analysis and usage-field-missing detection.\n * Contains only numeric counters and booleans — never message content, prompts,\n * payloads, headers, API keys, or model outputs.\n */\ntype CacheUsageSample = {\n timestamp: number;\n hit: boolean;\n cachedInputTokens: number;\n cacheWriteInputTokens: number;\n totalInputTokens: number;\n missingUsageFields: boolean;\n};\n\n/** Maximum number of recent samples kept per model key (in-memory only, not persisted). */\nconst MAX_RECENT_SAMPLES = 50;\n\ntype CacheProviderAdapter = {\n id: CacheProviderId;\n label: string;\n showCacheWrite?: boolean;\n matchesModel(model: PiModel | undefined): boolean;\n matchesAssistantMessage(message: unknown, model: PiModel | undefined): boolean;\n normalizeUsage(message: unknown): UsageSnapshot | undefined;\n warningText?(model: PiModel): string | undefined;\n};\n\nfunction escapeXml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\\\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction isStableContextFilePath(filePath: string): boolean {\n const normalized = filePath.replace(/\\\\/g, \"/\").toLowerCase();\n const name = normalized.split(\"/\").pop();\n\n return (\n name === \"agents.md\" ||\n name === \"claude.md\" ||\n name === \"gemini.md\" ||\n name === \"cursor.md\" ||\n normalized.startsWith(\".trellis/spec/\") ||\n normalized.includes(\"/.trellis/spec/\")\n );\n}\n\nfunction formatSkillsForPrompt(skills: NonNullable<BuildSystemPromptOptions[\"skills\"]>): string {\n const visibleSkills = skills.filter((skill) => !skill.disableModelInvocation);\n if (visibleSkills.length === 0) return \"\";\n\n const lines = [\n \"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n \"Use the read tool to load a skill's file when the task matches its description.\",\n \"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\",\n \"\",\n \"<available_skills>\",\n ];\n\n for (const skill of visibleSkills) {\n lines.push(\" <skill>\");\n lines.push(` <name>${escapeXml(skill.name)}</name>`);\n lines.push(` <description>${escapeXml(skill.description)}</description>`);\n lines.push(` <location>${escapeXml(skill.filePath)}</location>`);\n lines.push(\" </skill>\");\n }\n\n lines.push(\"</available_skills>\");\n return lines.join(\"\\n\");\n}\n\n/**\n * Compressed alternative to `formatSkillsForPrompt`.\n *\n * Pi emits a four-line XML block per skill (`<name>`, `<description>`,\n * `<location>`) plus a three-sentence preamble. With 31 skills active in\n * this repo that block measured 13.3 KB — 61.5 % of the total system\n * prompt. The full description text matters when the model has to decide\n * which skill to load, but the model can read SKILL.md on demand: the\n * names alone plus a known location pattern is enough to identify\n * candidates.\n *\n * This compressed form preserves:\n * 1. The instruction to read SKILL.md when a task matches a skill name.\n * 2. The relative-path resolution rule (parent of SKILL.md is the\n * skill directory).\n * 3. Discoverability of every skill: name + location prefix per skill.\n *\n * It drops:\n * - Per-skill description text (model loads it via `read` when a name\n * matches a task).\n * - The `<available_skills>` XML envelope and per-skill XML overhead\n * (~110 bytes per skill of pure structure, plus the location path).\n *\n * Output shape is a single text block grouped by skill-root directory so\n * the model can compute each skill's full path by name. Names are sorted\n * alphabetically within each group for determinism (cache stability).\n */\nfunction formatSkillsForPromptCompressed(\n skills: NonNullable<BuildSystemPromptOptions[\"skills\"]>,\n): string {\n const visibleSkills = skills.filter((skill) => !skill.disableModelInvocation);\n if (visibleSkills.length === 0) return \"\";\n\n const groups = new Map<string, string[]>();\n for (const skill of visibleSkills) {\n // skill.filePath = .../<skill-name>/SKILL.md, so dirname is the\n // skill directory and dirname-of-dirname is the skills root.\n const skillDir = dirname(skill.filePath);\n const root = dirname(skillDir);\n const list = groups.get(root) ?? [];\n list.push(skill.name);\n groups.set(root, list);\n }\n\n // Sort group entries by root for determinism: same skill set under the\n // same roots must always produce the same string, otherwise the\n // provider prompt-prefix cache loses on prompt builder runs that\n // happened to iterate the underlying Map in different orders.\n const sortedGroups = [...groups.entries()].sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0,\n );\n\n const lines: string[] = [\n \"\",\n \"\",\n \"The following skills provide specialized instructions for specific tasks. When a skill name matches the task you are doing, read the SKILL.md at the listed location to load the full instructions. When a SKILL.md references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\",\n ];\n\n for (const [root, names] of sortedGroups) {\n names.sort();\n lines.push(\"\");\n lines.push(`Skills under ${root}/<name>/SKILL.md:`);\n // Wrap the name list at ~80 columns for readability without\n // affecting determinism. Each line is ` name1, name2, name3,`.\n let buf = \" \";\n for (let i = 0; i < names.length; i++) {\n const name = names[i];\n const piece = (buf === \" \" ? \"\" : \", \") + name;\n if (buf.length > 2 && buf.length + piece.length > 80) {\n lines.push(`${buf},`);\n buf = ` ${name}`;\n } else {\n buf += piece;\n }\n }\n if (buf.length > 2) lines.push(buf);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Replace pi's verbose `<available_skills>` block in `prompt` with the\n * compressed one-index form. Idempotent: if the verbose form is not\n * present (compression already applied, or skill count below threshold),\n * the prompt is returned unchanged.\n *\n * Opt-out: set `PI_CACHE_OPTIMIZER_NO_SKILL_COMPRESSION=1`.\n *\n * Pre-conditions for compression to fire:\n * - opts.skills present and visible-skill count >= SKILL_COMPRESSION_MIN_COUNT\n * - Verbose block (built from the same `opts.skills`) is found in\n * `prompt` (substring match, no regex). This anchors the substitution\n * to pi's own emitter; if pi changes the format, we no-op rather\n * than mangle.\n */\nfunction compressSkillsInSystemPrompt(\n prompt: string,\n opts: BuildSystemPromptOptions,\n): string {\n if (isEnabledEnv(process.env[NO_SKILL_COMPRESSION_ENV])) return prompt;\n if (!opts.skills || opts.skills.length === 0) return prompt;\n\n const visible = opts.skills.filter((skill) => !skill.disableModelInvocation);\n if (visible.length < SKILL_COMPRESSION_MIN_COUNT) return prompt;\n\n const verbose = formatSkillsForPrompt(opts.skills);\n if (!verbose || !prompt.includes(verbose)) return prompt;\n\n const compressed = formatSkillsForPromptCompressed(opts.skills);\n if (!compressed || compressed.length >= verbose.length) return prompt;\n\n return prompt.replace(verbose, compressed);\n}\n\nfunction buildStableCandidates(opts: BuildSystemPromptOptions): string[] {\n const candidates: string[] = [];\n\n if (opts.customPrompt) candidates.push(opts.customPrompt);\n if (opts.appendSystemPrompt) candidates.push(opts.appendSystemPrompt);\n\n const tools = opts.selectedTools ?? [\"read\", \"bash\", \"edit\", \"write\"];\n const toolLines = tools\n .filter((name) => opts.toolSnippets?.[name])\n .map((name) => `- ${name}: ${opts.toolSnippets?.[name]}`);\n if (toolLines.length > 0) {\n candidates.push(`Available tools:\\n${toolLines.join(\"\\n\")}`);\n }\n\n for (const guideline of opts.promptGuidelines ?? []) {\n const normalized = guideline.trim();\n if (normalized.length > 0) candidates.push(`- ${normalized}`);\n }\n\n for (const file of opts.contextFiles ?? []) {\n // Provider caches work best when stable instructions are part of the earliest prefix.\n // Only lift known-stable project/spec instruction files. Dynamic task/session context\n // can be large too, so size alone must never make a context file cache-prefix material.\n if (!isStableContextFilePath(file.path)) continue;\n candidates.push(`## ${file.path}\\n\\n${file.content}`);\n candidates.push(file.content);\n }\n\n if (opts.skills && opts.skills.length > 0) {\n // Push BOTH forms so `optimizeSystemPrompt` finds whichever is\n // actually present in the prompt. The `rest.includes(part)`\n // short-circuit skips the form that isn't there. The two strings\n // are mutually distinguishable (the verbose form contains the\n // literal `<available_skills>` envelope; the compressed form\n // contains `Skills under ` and no XML tags) so they cannot\n // accidentally match each other.\n candidates.push(formatSkillsForPrompt(opts.skills));\n candidates.push(formatSkillsForPromptCompressed(opts.skills));\n }\n\n return candidates;\n}\n\n/**\n * Strip per-turn churn from trellis `<session-overview>` block.\n *\n * Trellis injects a session-overview that includes `RECENT COMMITS`\n * (shifts on every git commit), `Working directory: Clean/N uncommitted`\n * (shifts on every edit/commit), and `Line count: N / 2000` (shifts on\n * every journal append). These fields are at the tail of the\n * session-overview and poison the prompt-prefix cache for everything\n * that follows.\n *\n * This function surgically removes those three churn fields from the\n * `<session-overview>...</session-overview>` block. The remaining\n * fields (DEVELOPER, GIT STATUS branch-only, CURRENT TASK, ACTIVE\n * TASKS, MY TASKS, JOURNAL FILE active-file-only, PACKAGES, PATHS)\n * are stable within a session and become cache-friendlier.\n *\n * No-op when the `<session-overview>` tag is not present (e.g.\n * trellis hook chose not to inject it, or a different extension\n * owns the prompt).\n */\nfunction stripSessionOverviewChurn(prompt: string): string {\n const startTag = \"<session-overview>\";\n const endTag = \"</session-overview>\";\n\n const startIdx = prompt.indexOf(startTag);\n if (startIdx === -1) return prompt;\n\n const endIdx = prompt.indexOf(endTag, startIdx + startTag.length);\n if (endIdx === -1) return prompt;\n\n const before = prompt.slice(0, startIdx + startTag.length);\n const inner = prompt.slice(startIdx + startTag.length, endIdx);\n const after = prompt.slice(endIdx);\n\n let cleaned = inner\n // Drop the RECENT COMMITS section (from the heading through the\n // next heading or end of inner). The model sees commit history\n // via `git log`; carrying it in every system prompt is redundant.\n .replace(/\\n## RECENT COMMITS\\n[\\s\\S]*?(?=\\n## |$)/, \"\")\n // Drop \"Working directory: ...\" (Git status tail churn).\n .replace(/\\nWorking directory:[^\\n]*/g, \"\")\n // Drop \"Line count: N / NNNN\" (Journal tail churn).\n .replace(/\\nLine count:[^\\n]*/g, \"\");\n\n return before + cleaned + after;\n}\n\n/**\n * Extract structural markers from a prompt for the integrity guard.\n *\n * The guard runs in `optimizeSystemPrompt` to catch cases where the\n * candidate extraction accidentally eats text inside\n * an extension-injected structural block (e.g., trellis\n * `<workflow-state>`, a hypothetical `<task-tracker>`, or AGENTS.md\n * `<!-- TRELLIS:START -->` markers). When the original prompt contains\n * a marker that the result is missing, we fall back to the original\n * prompt rather than ship a corrupted one.\n *\n * Three marker categories are recognized (covers ~99% of real-world\n * extension injection patterns in the pi ecosystem):\n *\n * 1. XML-style opening tags `<tagname>` (lowercase, alpha-num + `_`/`-`)\n * 2. XML-style closing tags `</tagname>`\n * 3. HTML comment START/END `<!-- NAME:START -->` / `<!-- NAME:END -->`\n *\n * Tags with attributes (e.g., `<task id=\"42\">`) are not currently emitted\n * by any pi extension we know of and are skipped to keep the regex tight.\n * Markdown headers, horizontal rules, and timestamp patterns are not\n * usable as guards because they have no closing form to verify.\n *\n * The check is deliberately set-based (presence/absence) rather than\n * count-based: a single occurrence per request is the universal\n * convention, and a count drop with the same set of unique tags would\n * be a different class of bug not catchable here.\n */\nfunction extractStructuralMarkers(prompt: string): {\n openingTags: Set<string>;\n closingTags: Set<string>;\n commentMarkers: Set<string>;\n} {\n const openingTags = new Set<string>();\n const closingTags = new Set<string>();\n const commentMarkers = new Set<string>();\n\n // Opening tags: <tagname> with no attributes and no leading slash.\n // Tagname must start with a letter and contain only alpha-num, `-`, `_`.\n for (const match of prompt.matchAll(/<([a-z][a-z0-9_-]*)>/gi)) {\n openingTags.add(match[1].toLowerCase());\n }\n // Closing tags: </tagname>\n for (const match of prompt.matchAll(/<\\/([a-z][a-z0-9_-]*)>/gi)) {\n closingTags.add(match[1].toLowerCase());\n }\n // HTML comments with NAME:START or NAME:END inside.\n // Trellis emits `<!-- TRELLIS:START -->` / `<!-- TRELLIS:END -->` in\n // the AGENTS.md managed block; other extensions follow this convention.\n for (const match of prompt.matchAll(/<!--\\s*([A-Z][A-Z0-9_-]*):(START|END)\\s*-->/g)) {\n commentMarkers.add(`${match[1]}:${match[2]}`);\n }\n\n return { openingTags, closingTags, commentMarkers };\n}\n\nfunction optimizeSystemPrompt(\n original: string,\n opts: BuildSystemPromptOptions,\n): OptimizedSystemPrompt {\n const stableParts: string[] = [];\n const seen = new Set<string>();\n let rest = original;\n\n // Classify candidate ambiguity against one immutable snapshot. Candidates\n // can be nested (a full context-file block also contains its bare content),\n // so recomputing occurrence counts after each removal is unsafe: deleting\n // the full block can make the dynamic copy of its bare content appear unique.\n const candidates: string[] = [];\n for (const candidate of buildStableCandidates(opts)) {\n const part = candidate.trim();\n if (!part || part.length < MIN_STABLE_CANDIDATE_LENGTH || seen.has(part)) continue;\n seen.add(part);\n candidates.push(part);\n }\n\n const initialRemainder = rest;\n const occurrenceCount = new Map<string, number>();\n for (const part of candidates) {\n let count = 0;\n let searchFrom = 0;\n while (searchFrom < initialRemainder.length) {\n const occurrence = initialRemainder.indexOf(part, searchFrom);\n if (occurrence < 0) break;\n count++;\n if (count > 1) break;\n searchFrom = occurrence + 1;\n }\n occurrenceCount.set(part, count);\n }\n\n // Stable layer: content likely to be identical across sessions/turns.\n // Short / single-char candidates are dropped: see MIN_STABLE_CANDIDATE_LENGTH.\n for (const part of candidates) {\n if (occurrenceCount.get(part) !== 1) continue;\n\n const firstOccurrence = rest.indexOf(part);\n if (firstOccurrence < 0) continue;\n\n stableParts.push(part);\n rest = rest.slice(0, firstOccurrence) + rest.slice(firstOccurrence + part.length);\n }\n\n const stablePrefix = stableParts.join(\"\\n\\n\");\n\n // Dynamic layer: git status, active task context, recent session context, etc.\n const dynamicRemainder = rest.trim();\n\n if (stableParts.length === 0) {\n return { systemPrompt: original, stablePrefix: \"\", changed: false };\n }\n\n const systemPrompt =\n stablePrefix +\n (dynamicRemainder.length > 0 ? \"\\n\\n---\\n\\n\" + dynamicRemainder : \"\");\n\n // Sanity check: scan ALL structural markers (XML tags + HTML comment\n // boundary markers) in the original and verify each one survives the\n // reorder. If any marker drops, candidate extraction ate something it\n // shouldn't have — fall back to the original\n // prompt and flag the footer warning. This is provider-agnostic and\n // extension-agnostic: trellis `<workflow-state>`, a hypothetical\n // `<task-tracker>`, AGENTS.md `<!-- TRELLIS:START -->`, etc., are all\n // protected without code changes when new extensions ship.\n //\n // Our skills compression runs BEFORE optimizeSystemPrompt and replaces\n // pi's verbose `<available_skills>` block with a compressed text\n // section that has no XML tag. So `original` here (post-compression)\n // does not contain `<available_skills>` and the result doesn't either\n // — no false positive.\n const originalMarkers = extractStructuralMarkers(original);\n const resultMarkers = extractStructuralMarkers(systemPrompt);\n\n const missing =\n [...originalMarkers.openingTags].some((tag) => !resultMarkers.openingTags.has(tag)) ||\n [...originalMarkers.closingTags].some((tag) => !resultMarkers.closingTags.has(tag)) ||\n [...originalMarkers.commentMarkers].some((m) => !resultMarkers.commentMarkers.has(m));\n\n if (missing) {\n promptTruncationDetected = true;\n return { systemPrompt: original, stablePrefix: \"\", changed: false };\n }\n\n return {\n systemPrompt,\n stablePrefix,\n changed: true,\n };\n}\n\nfunction clampPromptCacheKey(key: string | undefined): string | undefined {\n const normalized = key?.trim();\n if (!normalized) return undefined;\n\n const chars = Array.from(normalized);\n if (chars.length <= OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH) return normalized;\n return chars.slice(0, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH).join(\"\");\n}\n\nfunction getSessionPromptCacheKey(ctx: ExtensionContext): string | undefined {\n return clampPromptCacheKey(ctx.sessionManager.getSessionId());\n}\n\n/**\n * Hash a session id for use as a non-reversible opaque scope key.\n * Returns a 16-character hex string (64 bits of SHA-256 digest prefix)\n * suitable for scoping stats buckets without exposing the raw session id.\n */\nfunction hashSessionId(sessionId: string): string {\n return createHash(\"sha256\").update(sessionId).digest(\"hex\").slice(0, 16);\n}\n\nfunction getProtocolGlobal(): ProtocolGlobal {\n return globalThis as ProtocolGlobal;\n}\n\nfunction firstNonEmptyString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (isNonEmptyString(value)) return value.trim();\n }\n return undefined;\n}\n\nfunction sessionHashFromContext(ctx: Pick<ExtensionContext, \"sessionManager\">): string | undefined {\n const sessionId = ctx.sessionManager.getSessionId();\n return sessionId ? hashSessionId(sessionId) : undefined;\n}\n\nfunction isPiRouterAdapterV1(value: unknown): value is PiRouterAdapterV1 {\n const record = asRecord(value);\n return !!record && isNonEmptyString(record.virtualProvider) && typeof record.resolveActiveRoute === \"function\";\n}\n\nfunction isRoutingRegistryV1(value: unknown): value is PiRoutingRegistryV1 {\n const record = asRecord(value);\n return !!record && record.version === 1 && typeof record.registerRouter === \"function\" && typeof record.getRouter === \"function\";\n}\n\nfunction createRoutingRegistry(): PiRoutingRegistryV1 {\n const routers = new Map<string, PiRouterAdapterV1>();\n return {\n version: 1,\n registerRouter(adapter: PiRouterAdapterV1): () => void {\n if (!isPiRouterAdapterV1(adapter)) return () => undefined;\n const key = adapter.virtualProvider.trim();\n routers.set(key, adapter);\n return () => {\n if (routers.get(key) === adapter) routers.delete(key);\n };\n },\n getRouter(virtualProvider: string): PiRouterAdapterV1 | undefined {\n return routers.get(virtualProvider);\n },\n };\n}\n\nfunction getRoutingRegistry(): PiRoutingRegistryV1 | undefined {\n const candidate = getProtocolGlobal()[PI_ROUTING_REGISTRY_SYMBOL];\n return isRoutingRegistryV1(candidate) ? candidate : undefined;\n}\n\nfunction ensureRoutingRegistry(): PiRoutingRegistryV1 {\n const existing = getRoutingRegistry();\n if (existing) return existing;\n\n const created = createRoutingRegistry();\n getProtocolGlobal()[PI_ROUTING_REGISTRY_SYMBOL] = created;\n return created;\n}\n\nfunction parseRouteStatus(value: unknown): PiRouteSnapshot[\"status\"] | undefined {\n return value === \"planned\" || value === \"trying\" || value === \"selected\" || value === \"success\" || value === \"failed\"\n ? value\n : undefined;\n}\n\nfunction parseRouteSnapshot(\n value: unknown,\n fallbackVirtualProvider?: string,\n fallbackVirtualModelId?: string,\n): PiRouteSnapshot | undefined {\n const record = asRecord(value);\n if (!record) return undefined;\n\n const virtualProvider = firstNonEmptyString(record.virtualProvider, fallbackVirtualProvider);\n const virtualModelId = firstNonEmptyString(record.virtualModelId, record.virtualModel, fallbackVirtualModelId);\n const provider = firstNonEmptyString(record.provider, record.upstreamProvider, record.targetProvider);\n const modelId = firstNonEmptyString(record.modelId, record.upstreamModelId, record.targetModelId, record.responseModel);\n if (!virtualProvider || !virtualModelId || !provider || !modelId) return undefined;\n\n const timestamp = getNumber(record.timestamp) ?? Date.now();\n return {\n virtualProvider,\n virtualModelId,\n provider,\n modelId,\n api: firstNonEmptyString(record.api),\n canonicalModelId: firstNonEmptyString(record.canonicalModelId),\n routeLabel: firstNonEmptyString(record.routeLabel, record.label),\n status: parseRouteStatus(record.status),\n sessionIdHash: firstNonEmptyString(record.sessionIdHash),\n requestId: firstNonEmptyString(record.requestId),\n timestamp,\n };\n}\n\nfunction resolveActiveRouteSnapshot(\n model: PiModel | undefined,\n ctx?: Pick<ExtensionContext, \"sessionManager\">,\n): PiRouteSnapshot | undefined {\n if (!model) return undefined;\n const hint: PiRouteResolveHint | undefined = ctx ? { sessionIdHash: sessionHashFromContext(ctx) } : undefined;\n\n const adapter = getRoutingRegistry()?.getRouter(model.provider);\n if (adapter) {\n try {\n const snapshot = parseRouteSnapshot(\n adapter.resolveActiveRoute(model.id, hint),\n model.provider,\n model.id,\n );\n if (snapshot) return snapshot;\n } catch (error) {\n console.warn(`${LOG_PREFIX}: routing registry adapter failed`, error);\n }\n }\n\n // Temporary migration shim for the prototype global used by early router PRs.\n // New integrations should use Symbol.for(\"pi.routing.registry.v1\") instead.\n const legacy = getProtocolGlobal().__piCacheOptimizerRouter;\n if (!legacy || !lower(model.provider).includes(\"router\")) return undefined;\n try {\n if (typeof legacy === \"function\") {\n return parseRouteSnapshot(legacy(model.provider, model.id, hint), model.provider, model.id);\n }\n const legacyRecord = asRecord(legacy);\n const resolver = legacyRecord?.resolveActiveRoute;\n if (typeof resolver === \"function\") {\n return parseRouteSnapshot(resolver.call(legacy, model.id, hint), model.provider, model.id);\n }\n return parseRouteSnapshot(legacy, model.provider, model.id);\n } catch (error) {\n console.warn(`${LOG_PREFIX}: legacy routing global failed`, error);\n return undefined;\n }\n}\n\nfunction routeSnapshotToPiModel(snapshot: PiRouteSnapshot, fallback?: PiModel): PiModel {\n return {\n ...(fallback ?? {}),\n id: snapshot.modelId,\n name: snapshot.canonicalModelId ?? snapshot.modelId,\n provider: snapshot.provider,\n api: snapshot.api ?? fallback?.api ?? \"\",\n baseUrl: fallback?.baseUrl ?? \"\",\n reasoning: fallback?.reasoning ?? false,\n input: fallback?.input ?? [\"text\"],\n cost: fallback?.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: fallback?.contextWindow ?? 0,\n maxTokens: fallback?.maxTokens ?? 0,\n compat: fallback?.compat,\n } as PiModel;\n}\n\nfunction findModelInRegistry(registry: ModelRegistryLike | undefined, provider: string, id: string): PiModel | undefined {\n const found = registry?.find?.(provider, id);\n if (found) return found;\n\n const available = registry?.getAvailable?.() ?? [];\n const availableMatch = available.find((candidate) => candidate.provider === provider && candidate.id === id);\n if (availableMatch) return availableMatch;\n\n const all = registry?.getAll?.() ?? [];\n return all.find((candidate) => candidate.provider === provider && candidate.id === id);\n}\n\nfunction resolveRouteModel(\n model: PiModel | undefined,\n ctx?: ContextWithOptionalModelRegistry,\n): PiModel | undefined {\n const snapshot = resolveActiveRouteSnapshot(model, ctx);\n if (!snapshot) return undefined;\n\n return findModelInRegistry(ctx?.modelRegistry, snapshot.provider, snapshot.modelId)\n ?? routeSnapshotToPiModel(snapshot, model);\n}\n\nfunction isVirtualRoutingModel(model: PiModel | undefined, ctx?: Pick<ExtensionContext, \"sessionManager\">): boolean {\n if (!model) return false;\n return isRouterModel(model) || !!getRoutingRegistry()?.getRouter(model.provider) || !!resolveActiveRouteSnapshot(model, ctx);\n}\n\nfunction isCacheHintsServiceV1(value: unknown): value is PiCacheHintsV1 {\n const record = asRecord(value);\n return !!record && record.version === 1 && typeof record.getHints === \"function\";\n}\n\nfunction getCacheHintsService(): PiCacheHintsV1 | undefined {\n const candidate = getProtocolGlobal()[PI_CACHE_HINTS_SYMBOL];\n return isCacheHintsServiceV1(candidate) ? candidate : undefined;\n}\n\nfunction markOptimizerOwnedCacheHintsService(service: PiCacheHintsV1): PiCacheHintsV1 {\n (service as PiCacheHintsV1 & Record<symbol, unknown>)[PI_CACHE_HINTS_OWNER_SYMBOL] = true;\n return service;\n}\n\nfunction isOptimizerOwnedCacheHintsService(value: unknown): boolean {\n return typeof value === \"object\" && value !== null &&\n (value as Record<symbol, unknown>)[PI_CACHE_HINTS_OWNER_SYMBOL] === true;\n}\n\nfunction installCacheHintsService(\n service: PiCacheHintsV1,\n options: { discardPrevious?: (value: unknown) => boolean } = {},\n): () => void {\n const globals = getProtocolGlobal();\n const previous = globals[PI_CACHE_HINTS_SYMBOL];\n globals[PI_CACHE_HINTS_SYMBOL] = service;\n return () => {\n if (globals[PI_CACHE_HINTS_SYMBOL] !== service) return;\n if (previous !== undefined && !options.discardPrevious?.(previous)) {\n globals[PI_CACHE_HINTS_SYMBOL] = previous;\n } else {\n delete globals[PI_CACHE_HINTS_SYMBOL];\n }\n };\n}\n\n/**\n * Build a session-scoped stats key from a session hash + provider/id.\n * Pure function (no closure dependency) for use by tests and internals.\n */\nfunction makeSessionModelKey(sessionHash: string, provider: string, id: string): string {\n return `${sessionHash}:${provider}/${id}`;\n}\n\n/**\n * Extract the user-facing model key from a session-scoped key.\n * \"abc123:otokapi/gpt-5.5\" → \"otokapi/gpt-5.5\"\n */\nfunction modelKeyFromSessionKey(sessionModelKey: string): string {\n const idx = sessionModelKey.indexOf(\":\");\n return idx >= 0 ? sessionModelKey.slice(idx + 1) : sessionModelKey;\n}\n\nfunction asRecord(value: unknown): UnknownRecord | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n return value as UnknownRecord;\n}\n\nfunction lower(value: unknown): string {\n return typeof value === \"string\" ? value.toLowerCase() : \"\";\n}\n\nfunction getNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction getNonNegativeNumber(record: UnknownRecord, key: string): number | undefined {\n const value = getNumber(record[key]);\n return value !== undefined && value >= 0 ? value : undefined;\n}\n\n/**\n * Get effective compat for a model by merging provider-level and model-level compat.\n * Model-level compat takes precedence over provider-level compat for overlapping keys.\n * This matches Pi's model-registry.js mergeCompat behavior.\n */\nfunction getCompat(model: PiModel | undefined): CacheCompat {\n if (!model) return {} as CacheCompat;\n\n // Pi merges provider.compat with model.compat (model wins on conflicts)\n // We approximate this by reading from ctx.model which should already have merged compat\n // However, for safety, we check both levels if available\n const modelCompat = (model.compat ?? {}) as CacheCompat;\n\n // Note: ctx.model from Pi should already contain merged compat,\n // but we document the two-level structure for clarity\n return modelCompat;\n}\n\n/** Join display-only path fragments without resolving them for I/O. */\nfunction joinDisplayPath(base: string, child: string, platform: string = process.platform): string {\n const sep = platform.startsWith(\"win\") ? \"\\\\\" : \"/\";\n return `${base.replace(/[\\\\/]+$/, \"\")}${sep}${child}`;\n}\n\n/**\n * Return a platform-friendly display path for Pi's agent directory.\n *\n * Derives the display path from Pi core's `getAgentDir()` result. Home-relative\n * paths use `%USERPROFILE%` on Windows and `~` on Unix-like systems; custom\n * absolute or relative agent directories remain visible as configured.\n */\nfunction getAgentDirDisplayPath(\n platform: string = process.platform,\n agentDir: string = getAgentDir(),\n homeDir: string = homedir(),\n): string {\n const sep = platform.startsWith(\"win\") ? \"\\\\\" : \"/\";\n const normalizedAgentDir = agentDir.replace(/[\\\\/]+/g, sep);\n const normalizedHomeDir = homeDir.replace(/[\\\\/]+/g, sep).replace(/[\\\\/]+$/, \"\");\n const homePrefix = `${normalizedHomeDir}${sep}`;\n\n if (normalizedAgentDir === normalizedHomeDir || normalizedAgentDir.startsWith(homePrefix)) {\n const relative = normalizedAgentDir.slice(normalizedHomeDir.length).replace(/^[\\\\/]+/, \"\");\n const homeLabel = platform.startsWith(\"win\") ? \"%USERPROFILE%\" : \"~\";\n return relative ? `${homeLabel}${sep}${relative}` : homeLabel;\n }\n\n return normalizedAgentDir;\n}\n\n/**\n * Return a platform-friendly display path for Pi's `models.json`.\n *\n * This is a DISPLAY helper only. Actual I/O uses `MODELS_JSON_PATH`, resolved\n * from the same custom agent/config directory rules as `STATE_DIR`.\n */\nfunction getModelsJsonDisplayPath(\n platform: string = process.platform,\n agentDir: string = getAgentDir(),\n homeDir: string = homedir(),\n): string {\n return joinDisplayPath(getAgentDirDisplayPath(platform, agentDir, homeDir), \"models.json\", platform);\n}\n\nfunction isEnabledEnv(value: string | undefined): boolean {\n if (!value) return false;\n const normalized = value.trim().toLowerCase();\n return normalized === \"1\" || normalized === \"true\" || normalized === \"yes\" || normalized === \"on\";\n}\n\nfunction parseFooterStatsMode(value: unknown): FooterStatsMode | undefined {\n if (typeof value !== \"string\") return undefined;\n const normalized = value.trim().toLowerCase();\n return normalized === \"session\" || normalized === \"total\" || normalized === \"process\" ? normalized : undefined;\n}\n\ntype CommandCompletionItem = {\n value: string;\n label: string;\n description?: string;\n};\n\nconst CACHE_OPTIMIZER_COMMANDS = [\n \"enable\",\n \"disable\",\n \"doctor\",\n \"stats\",\n \"config\",\n \"compat\",\n \"reset\",\n \"fix\",\n] as const;\nconst CACHE_OPTIMIZER_CONFIG_ARGUMENTS = [\"footer-mode\"] as const;\nconst CACHE_OPTIMIZER_FOOTER_MODES = [\"total\", \"session\", \"process\"] as const;\n\nfunction filterCommandCompletionItems(\n values: readonly string[],\n prefix: string,\n argumentPath = \"\",\n): CommandCompletionItem[] | null {\n const normalizedPrefix = prefix.trim().toLowerCase();\n const matches = values\n .filter((value) => value.startsWith(normalizedPrefix))\n .map((value) => ({\n // Pi replaces the complete argumentPrefix when applying a command\n // completion, so nested suggestions must include their full path.\n value: argumentPath ? `${argumentPath} ${value}` : value,\n label: value,\n }));\n return matches.length > 0 ? matches : null;\n}\n\nfunction getCacheOptimizerArgumentCompletions(argumentPrefix: string): CommandCompletionItem[] | null {\n if (typeof argumentPrefix !== \"string\") return null;\n const trimmed = argumentPrefix.trim();\n const parts = trimmed ? trimmed.split(/\\s+/) : [];\n\n if (parts.length === 0) {\n return filterCommandCompletionItems(CACHE_OPTIMIZER_COMMANDS, \"\");\n }\n\n if (parts.length === 1) {\n const subcommandPrefix = parts[0].toLowerCase();\n // `config` is the primary `c` completion; `compat` remains available\n // through its more specific `co` prefix. Surrounding whitespace is\n // ignored so ` c ` behaves the same as `c`.\n if (subcommandPrefix === \"c\") {\n return [{ value: \"config\", label: \"config\" }];\n }\n if (subcommandPrefix === \"config\") {\n return filterCommandCompletionItems(CACHE_OPTIMIZER_CONFIG_ARGUMENTS, \"\", \"config\");\n }\n return filterCommandCompletionItems(CACHE_OPTIMIZER_COMMANDS, parts[0]);\n }\n\n if (parts[0].toLowerCase() !== \"config\") return null;\n\n if (parts.length === 2) {\n const nestedPrefix = parts[1].toLowerCase();\n if (nestedPrefix === \"footer-mode\") {\n return filterCommandCompletionItems(CACHE_OPTIMIZER_FOOTER_MODES, \"\", \"config footer-mode\");\n }\n return filterCommandCompletionItems(CACHE_OPTIMIZER_CONFIG_ARGUMENTS, parts[1], \"config\");\n }\n\n if (parts.length === 3 && parts[1].toLowerCase() === \"footer-mode\") {\n return filterCommandCompletionItems(CACHE_OPTIMIZER_FOOTER_MODES, parts[2], \"config footer-mode\");\n }\n\n return null;\n}\n\nfunction parsePersistedCacheOptimizerConfig(value: unknown): PersistedCacheOptimizerConfigV1 | undefined {\n const record = asRecord(value);\n if (!record || record.version !== 1) return undefined;\n const footerMode = parseFooterStatsMode(record.footerMode);\n if (record.footerMode !== undefined && !footerMode) return undefined;\n return { version: 1, ...(footerMode ? { footerMode } : {}) };\n}\n\nfunction readPersistedFooterMode(configPath: string = CONFIG_FILE_PATH): FooterStatsMode | undefined {\n try {\n const parsed = parsePersistedCacheOptimizerConfig(JSON.parse(readFileSync(configPath, \"utf8\")));\n if (!parsed) throw new Error(\"invalid footer config schema\");\n return parsed.footerMode;\n } catch (error) {\n if (getErrorCode(error) !== \"ENOENT\") {\n console.warn(`${LOG_PREFIX}: failed to read footer config; using environment/default mode`, error);\n }\n return undefined;\n }\n}\n\nasync function writePersistedFooterMode(\n mode: FooterStatsMode,\n configPath: string = CONFIG_FILE_PATH,\n): Promise<void> {\n await mkdir(dirname(configPath), { recursive: true });\n const payload: PersistedCacheOptimizerConfigV1 = { version: 1, footerMode: mode };\n const tempPath = `${configPath}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(tempPath, JSON.stringify(payload, null, 2) + \"\\n\", \"utf8\");\n await rename(tempPath, configPath);\n}\n\nfunction resolveFooterStatsMode(\n configuredMode: FooterStatsMode | undefined,\n env: MutableEnv = process.env,\n): { mode: FooterStatsMode; source: FooterStatsModeSource } {\n if (configuredMode) return { mode: configuredMode, source: \"config\" };\n const envMode = parseFooterStatsMode(env[FOOTER_MODE_ENV]);\n return envMode ? { mode: envMode, source: \"env\" } : { mode: \"total\", source: \"default\" };\n}\n\nfunction footerStatsMode(\n env: MutableEnv = process.env,\n configuredMode: FooterStatsMode | undefined = persistedFooterStatsMode,\n): FooterStatsMode {\n return resolveFooterStatsMode(configuredMode, env).mode;\n}\n\nlet persistedFooterStatsMode = readPersistedFooterMode();\n\nfunction isDisabledEnv(value: string | undefined): boolean {\n if (!value) return false;\n const normalized = value.trim().toLowerCase();\n return normalized === \"0\" || normalized === \"false\" || normalized === \"no\" || normalized === \"off\";\n}\n\nfunction shouldInjectOpenAIPromptCacheKey(): boolean {\n if (!runtimeOptimizerEnabled) return false;\n if (isEnabledEnv(process.env[NO_OPENAI_CACHE_KEY_ENV])) return false;\n if (isDisabledEnv(process.env[OPENAI_CACHE_KEY_ENV])) return false;\n return true;\n}\n\nfunction setRuntimeOptimizerEnabled(enabled: boolean, env: MutableEnv = process.env): void {\n runtimeOptimizerEnabled = enabled;\n if (enabled) {\n requestLongCacheRetention(env);\n } else {\n restoreCacheRetentionEnv(STARTUP_CACHE_RETENTION_ENV, env);\n }\n}\n\nfunction isRuntimeOptimizerEnabled(): boolean {\n return runtimeOptimizerEnabled;\n}\n\nfunction getOptimizerRuntimeModeLines(): string[] {\n const state = runtimeOptimizerEnabled ? \"enabled\" : \"disabled\";\n const lines: string[] = [];\n lines.push(`Runtime state: ${state}`);\n lines.push(`• Prompt rewrite: ${runtimeOptimizerEnabled && !isEnabledEnv(process.env[NO_PROMPT_REWRITE_ENV]) ? \"on\" : \"off\"}`);\n lines.push(`• OpenAI prompt_cache_key fallback: ${shouldInjectOpenAIPromptCacheKey() ? \"on\" : \"off\"}`);\n lines.push(`• Footer cache stats: on${runtimeOptimizerEnabled ? \"\" : \" (comparison mode)\"}`);\n lines.push(`• Compat warnings: ${runtimeOptimizerEnabled ? \"on\" : \"off\"}`);\n lines.push(`• ${PI_CACHE_RETENTION_ENV}: ${process.env[PI_CACHE_RETENTION_ENV] ?? \"(unset)\"}`);\n if (!runtimeOptimizerEnabled) {\n lines.push(\"This is a current-process switch. Run /reload or restart Pi to return to startup behavior.\");\n } else if (isEnabledEnv(process.env[NO_PROMPT_REWRITE_ENV]) || !shouldInjectOpenAIPromptCacheKey()) {\n lines.push(\"Some features are still disabled by environment variables.\");\n }\n return lines;\n}\n\nfunction formatOptimizerRuntimeMode(): string {\n return getOptimizerRuntimeModeLines().join(\"\\n\");\n}\n\nfunction isAssistantMessage(message: unknown): boolean {\n return asRecord(message)?.role === \"assistant\";\n}\n\nfunction getAssistantRecord(message: unknown): UnknownRecord | undefined {\n const record = asRecord(message);\n return record?.role === \"assistant\" ? record : undefined;\n}\n\nfunction getModelIdNameTokenValues(model: PiModel | undefined): string[] {\n if (!model) return [];\n return [model.id, model.name].map(lower).filter(Boolean);\n}\n\nfunction getAssistantMessageModelTokenValues(message: unknown): string[] {\n const record = asRecord(message);\n if (!record) return [];\n\n return ASSISTANT_MESSAGE_MODEL_TOKEN_KEYS.map((key) => lower(record[key])).filter(Boolean);\n}\n\nfunction hasAnyTokenContaining(tokens: string[], needles: string[]): boolean {\n return tokens.some((token) => needles.some((needle) => token.includes(needle)));\n}\n\nfunction modelOrAssistantMessageHas(message: unknown, model: PiModel | undefined, needles: string[]): boolean {\n return hasAnyTokenContaining([...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)], needles);\n}\n\nfunction isDeepSeekLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"deepseek\"]);\n}\n\nfunction isDeepSeekLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"deepseek\"]);\n}\n\nfunction isOpenAICompatibleApi(api: unknown): boolean {\n const value = lower(api);\n return value === \"openai-completions\" || value === \"openai-responses\";\n}\n\nfunction isAnthropicMessagesApi(api: unknown): boolean {\n return lower(api) === \"anthropic-messages\";\n}\n\nfunction isOpenAICompatibleProxyApi(api: unknown): boolean {\n return lower(api) === \"openai-completions\";\n}\n\nfunction isPiBuiltInLlamaCppModel(model: PiModel | undefined): boolean {\n if (lower(model?.provider) !== \"llama.cpp\" || !isOpenAICompatibleProxyApi(model?.api)) return false;\n\n // Pi's built-in llama.cpp provider supplies this exact explicit compat\n // fingerprint. Provider ids are extension-overridable and models.json can add\n // cache/routing overrides, so provider id alone must never imply exemption.\n const compat = getCompat(model);\n return compat.supportsStore === false\n && compat.supportsDeveloperRole === false\n && compat.supportsReasoningEffort === false\n && compat.supportsUsageInStreaming === false\n && compat.supportsStrictMode === false\n && compat.maxTokensField === \"max_tokens\"\n && compat.sendSessionAffinityHeaders === undefined\n && compat.sessionAffinityFormat === undefined\n && compat.supportsLongCacheRetention === undefined;\n}\n\nfunction shouldInjectOpenAIPromptCacheKeyForModel(model: PiModel | undefined): boolean {\n return isOpenAICompatibleApi(model?.api);\n}\n\nfunction collectAnthropicCacheControlsInWireOrder(payload: unknown): UnknownRecord[] {\n const record = asRecord(payload);\n if (!record) return [];\n\n const controls: UnknownRecord[] = [];\n const collectFromBlock = (value: unknown): void => {\n const cacheControl = asRecord(asRecord(value)?.cache_control);\n if (cacheControl?.type === \"ephemeral\") controls.push(cacheControl);\n };\n\n // Anthropic processes cache breakpoints in tools → system → messages order.\n if (Array.isArray(record.tools)) {\n for (const tool of record.tools) collectFromBlock(tool);\n }\n if (Array.isArray(record.system)) {\n for (const block of record.system) collectFromBlock(block);\n }\n if (Array.isArray(record.messages)) {\n for (const message of record.messages) {\n const content = asRecord(message)?.content;\n if (!Array.isArray(content)) continue;\n for (const block of content) collectFromBlock(block);\n }\n }\n\n return controls;\n}\n\n/**\n * Anthropic requires every 1h cache breakpoint to precede every 5m breakpoint.\n * An ephemeral cache_control without ttl uses Anthropic's default 5m retention.\n * If the final serialized payload contains a short → long transition, downgrade\n * all 1h breakpoints to the default 5m so the request remains valid.\n */\nfunction downgradeAnthropicLongCacheControls(payload: unknown): boolean {\n let changed = false;\n for (const control of collectAnthropicCacheControlsInWireOrder(payload)) {\n if (control.ttl === \"1h\") {\n delete control.ttl;\n changed = true;\n }\n }\n return changed;\n}\n\nfunction hasAnthropicCacheTtlOrderError(message: unknown): boolean {\n const record = getAssistantRecord(message);\n if (record?.stopReason !== \"error\" || typeof record.errorMessage !== \"string\") return false;\n\n const error = lower(record.errorMessage);\n return error.includes(\"cache_control\") &&\n error.includes(\"ttl='1h'\") &&\n error.includes(\"ttl='5m'\") &&\n error.includes(\"must not come after\");\n}\n\nfunction normalizeAnthropicCacheControlTtlOrder(payload: unknown): boolean {\n const controls = collectAnthropicCacheControlsInWireOrder(payload);\n let seenShort = false;\n let hasInvalidLongAfterShort = false;\n\n for (const control of controls) {\n const ttl = control.ttl;\n if (ttl === undefined || ttl === \"5m\") {\n seenShort = true;\n } else if (ttl === \"1h\" && seenShort) {\n hasInvalidLongAfterShort = true;\n break;\n }\n }\n if (!hasInvalidLongAfterShort) return false;\n\n return downgradeAnthropicLongCacheControls(payload);\n}\n\nfunction isResponsesPromptRewriteBypassApi(api: unknown): boolean {\n const value = lower(api);\n return value === \"openai-codex-responses\" || value === \"openai-responses\" || value === \"azure-openai-responses\";\n}\n\nfunction isMistralConversationsApi(api: unknown): boolean {\n return lower(api) === \"mistral-conversations\";\n}\n\nfunction isOpenAIFamilyToken(token: string): boolean {\n return token.includes(\"gpt-\") || token.includes(\"chatgpt\") || OPENAI_REASONING_MODEL_PATTERN.test(token);\n}\n\nfunction isOpenAIFamilyModel(model: PiModel | undefined): boolean {\n return getModelIdNameTokenValues(model).some(isOpenAIFamilyToken);\n}\n\nfunction isOpenAIFamilyAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)].some(isOpenAIFamilyToken);\n}\n\nfunction isClaudeLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"anthropic\", \"claude\"]);\n}\n\nfunction isClaudeLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"anthropic\", \"claude\"]);\n}\n\nfunction isGeminiLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"gemini\", \"vertex\"]);\n}\n\nfunction isGeminiLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"gemini\", \"vertex\"]);\n}\n\n// ── Adaptive generation model detection ────────────────────────────\n\n/**\n * Check whether the model id uses Anthropic's adaptive generation (thinking)\n * that requires `forceAdaptiveThinking: true` in compat.\n *\n * Adaptive-generation models (from pi-ai built-in catalog) include:\n * claude-opus-4-6, claude-opus-4-7, claude-opus-4-8 (also dotted 4.6/4.7/4.8)\n * claude-sonnet-4-6, claude-sonnet-5\n * claude-fable-5\n *\n * We match broadly: opus >= 4-6, sonnet >= 4-6, fable >= 5.\n * Ids may carry date-stamp or size suffixes like \"[1M]\".\n */\nconst ADAPTIVE_OPUS_PATTERN = /(^|[\\/\\s:_-])(opus-4[.-][6-9]|opus-4-[1-9][0-9]|opus-([5-9]|[1-9][0-9]))($|[-_.:\\/\\s\\[])/i;\nconst ADAPTIVE_SONNET_PATTERN = /(^|[\\/\\s:_-])(sonnet-4[.-][6-9]|sonnet-4-[1-9][0-9]|sonnet-([5-9]|[1-9][0-9]))($|[-_.:\\/\\s\\[])/i;\nconst ADAPTIVE_FABLE_PATTERN = /(^|[\\/\\s:_-])fable-([5-9]|[1-9][0-9])($|[-_.:\\/\\s\\[])/i;\n\nfunction isAdaptiveGenerationModel(model: PiModel | undefined): boolean {\n if (!model) return false;\n const tokens = getModelIdNameTokenValues(model);\n return tokens.some((t) => ADAPTIVE_OPUS_PATTERN.test(t) || ADAPTIVE_SONNET_PATTERN.test(t) || ADAPTIVE_FABLE_PATTERN.test(t));\n}\n\nfunction isKimiCodingAdaptiveModel(model: PiModel | undefined): boolean {\n if (!model) return false;\n const provider = lower(model.provider);\n const baseUrl = lower(model.baseUrl);\n const isKimiCodingChannel = provider.includes(\"kimi-coding\") || baseUrl.includes(\"api.kimi.com/coding\");\n if (!isKimiCodingChannel) return false;\n\n const tokens = getModelIdNameTokenValues(model);\n return tokens.some((token) =>\n token === \"k3\"\n || token.includes(\"kimi-k3\")\n || token.includes(\"kimi k3\")\n || token.includes(\"kimi-for-coding\")\n || token.includes(\"kimi for coding\")\n );\n}\n\nfunction isKimiCodingEmptySignatureModel(model: PiModel | undefined): boolean {\n if (!isKimiCodingAdaptiveModel(model)) return false;\n return getModelIdNameTokenValues(model).some((token) =>\n token === \"k3\"\n || token === \"kimi-k3\"\n || token.startsWith(\"kimi-k3-\")\n || token === \"kimi k3\"\n || token === \"kimi-for-coding\"\n || token === \"kimi for coding\"\n );\n}\n\nfunction isAdaptiveThinkingCompatApplicable(model: PiModel): boolean {\n return lower(model.api) === \"anthropic-messages\"\n && (isAdaptiveGenerationModel(model) || isKimiCodingAdaptiveModel(model));\n}\n\nfunction describeMissingAdaptiveThinkingCompat(model: PiModel): string[] {\n const compat = getCompat(model);\n const missing: string[] = [];\n if (compat.forceAdaptiveThinking !== true) {\n missing.push(\"forceAdaptiveThinking\");\n }\n if (isKimiCodingEmptySignatureModel(model) && compat.allowEmptySignature !== true) {\n missing.push(\"allowEmptySignature\");\n }\n return missing;\n}\n\nfunction buildAdaptiveThinkingCompatSuggestion(missing: string[]): Record<string, unknown> {\n const suggestion: Record<string, unknown> = {};\n if (missing.includes(\"forceAdaptiveThinking\")) {\n suggestion.forceAdaptiveThinking = true;\n }\n if (missing.includes(\"allowEmptySignature\")) {\n suggestion.allowEmptySignature = true;\n }\n return suggestion;\n}\n\nfunction appendAdaptiveThinkingCompatAdviceLines(lines: string[], missing: string[], placement: CompatAdvicePlacement = {}): void {\n const suggestion = buildAdaptiveThinkingCompatSuggestion(missing);\n if (Object.keys(suggestion).length > 0) {\n lines.push(\"Suggested fix:\");\n lines.push(JSON.stringify(suggestion, null, 2));\n }\n lines.push(\"- forceAdaptiveThinking: true tells Pi to use adaptive thinking format\");\n lines.push(\" (thinking: {type: 'adaptive'}) instead of legacy budget tokens format.\");\n lines.push(\" Without this flag, Pi sends legacy thinking which adaptive-only upstreams reject.\");\n if (missing.includes(\"allowEmptySignature\")) {\n lines.push(\"- allowEmptySignature: true preserves Kimi Coding K3 thinking blocks whose replay signature is empty.\");\n }\n appendCredentialSafeProviderGuidance(lines, placement, suggestion);\n}\n\nfunction buildAdaptiveThinkingCompatWarningText(key: string, missing: string[]): string {\n const slashIdx = key.indexOf(\"/\");\n const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;\n const modelId = slashIdx > 0 ? key.slice(slashIdx + 1) : undefined;\n const modelsJsonPath = getModelsJsonDisplayPath();\n const lines: string[] = [\n `💡 pi-cache-optimizer: ${key} is an adaptive-generation model but merged compat lacks ${missing.join(\" and \")}.`,\n `Without the required compat, Pi may send legacy thinking or replay thinking blocks incorrectly.`,\n `Edit ${modelsJsonPath} -> providers[\"${providerLabel}\"] -> compat (at the same level as baseUrl/api/apiKey/models).`,\n \"\",\n ];\n appendAdaptiveThinkingCompatAdviceLines(lines, missing, { providerLabel, modelId });\n return lines.join(\"\\n\");\n}\n\n// ── Non-GPT OpenAI-compatible model detection ──────────────────────\n\nfunction isKimiLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"kimi\"]);\n}\nfunction isKimiLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"kimi\"]);\n}\n\nfunction isQwenLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"qwen\"]);\n}\nfunction isQwenLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"qwen\"]);\n}\n\nfunction isGLMLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"glm\"]);\n}\nfunction isGLMLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"glm\"]);\n}\n\nfunction isMiniMaxLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"minimax\"]);\n}\nfunction isMiniMaxLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"minimax\"]);\n}\n\nfunction isMimoLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"xiaomimimo\"]) || tokens.some((t) => MIMO_MODEL_PATTERN.test(t));\n}\nfunction isMimoLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"xiaomimimo\"]) || allTokens.some((t) => MIMO_MODEL_PATTERN.test(t));\n}\n\nfunction isHunyuanLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"hunyuan\"]);\n}\nfunction isHunyuanLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"hunyuan\"]);\n}\n\n// ── Additional OpenAI-compatible model detection ──────────────────\n\nfunction isMistralLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"mistral\", \"mixtral\", \"codestral\"]);\n}\nfunction isMistralLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"mistral\", \"mixtral\", \"codestral\"]);\n}\n\nfunction isGrokLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"grok\"]) || tokens.some((t) => XAI_MODEL_PATTERN.test(t));\n}\nfunction isGrokLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"grok\"]) || allTokens.some((t) => XAI_MODEL_PATTERN.test(t));\n}\n\nfunction isLlamaLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"llama\"]);\n}\nfunction isLlamaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"llama\"]);\n}\n\nfunction isNemotronLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"nemotron\"]);\n}\nfunction isNemotronLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"nemotron\"]);\n}\n\nfunction isCohereLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"cohere\", \"command-r\"]);\n}\nfunction isCohereLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"cohere\", \"command-r\"]);\n}\n\nconst YI_MODEL_PATTERN = /(^|[\\/\\s:_-])yi($|[\\-_.:\\/\\s])/;\n\nfunction isYiLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"yi-\", \"01-ai\", \"zero-one\"]) || tokens.some((t) => YI_MODEL_PATTERN.test(t));\n}\nfunction isYiLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"yi-\", \"01-ai\", \"zero-one\"]) || allTokens.some((t) => YI_MODEL_PATTERN.test(t));\n}\n\n// ── More OpenAI-compatible model detection (batch 2) ───────────────\n\nconst DOUBAO_SEED_PATTERN = /(^|[\\/\\s:_-])seed($|[\\-_.:\\/\\s])/i;\n\nfunction isDoubaoLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"doubao\", \"豆包\", \"volcengine\", \"bytedance\", \"byte-dance\"]) ||\n tokens.some((t) => DOUBAO_SEED_PATTERN.test(t));\n}\nfunction isDoubaoLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"doubao\", \"豆包\", \"volcengine\", \"bytedance\", \"byte-dance\"]) ||\n allTokens.some((t) => DOUBAO_SEED_PATTERN.test(t));\n}\n\nfunction isErnieLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"ernie\", \"wenxin\", \"文心\", \"yiyan\", \"一言\", \"baidu\"]);\n}\nfunction isErnieLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"ernie\", \"wenxin\", \"文心\", \"yiyan\", \"一言\", \"baidu\"]);\n}\n\nfunction isBaichuanLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"baichuan\", \"百川\"]);\n}\nfunction isBaichuanLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"baichuan\", \"百川\"]);\n}\n\nfunction isStepFunLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"stepfun\", \"step-\"]);\n}\nfunction isStepFunLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"stepfun\", \"step-\"]);\n}\n\nfunction isSparkLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"spark\", \"xinghuo\", \"星火\", \"iflytek\", \"讯飞\"]);\n}\nfunction isSparkLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"spark\", \"xinghuo\", \"星火\", \"iflytek\", \"讯飞\"]);\n}\n\nfunction isInternLMLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"internlm\", \"intern-lm\", \"书生\"]);\n}\nfunction isInternLMLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"internlm\", \"intern-lm\", \"书生\"]);\n}\n\nfunction isGemmaLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"gemma\"]);\n}\nfunction isGemmaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"gemma\"]);\n}\n\nconst PHI_MODEL_PATTERN = /(^|[\\/\\s:_-])phi($|[\\-_.:\\/\\s])/i;\n\nfunction isPhiLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"phi-\"]) || tokens.some((t) => PHI_MODEL_PATTERN.test(t));\n}\nfunction isPhiLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"phi-\"]) || allTokens.some((t) => PHI_MODEL_PATTERN.test(t));\n}\n\nfunction isJambaLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"jamba\", \"ai21\"]);\n}\nfunction isJambaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"jamba\", \"ai21\"]);\n}\n\nfunction isSolarLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"solar\", \"upstage\"]);\n}\nfunction isSolarLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"solar\", \"upstage\"]);\n}\n\n// ── New OpenAI-compatible model detection (batch 3, 12 families) ──────\n\n// Perplexity / Sonar\nfunction isPerplexityLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"sonar\", \"perplexity\"]) || tokens.some((t) => PPLX_MODEL_PATTERN.test(t));\n}\nfunction isPerplexityLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"sonar\", \"perplexity\"]) || allTokens.some((t) => PPLX_MODEL_PATTERN.test(t));\n}\n\n// Amazon Nova\nfunction isNovaLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"amazon-nova\"]) || tokens.some((t) => NOVA_MODEL_PATTERN.test(t));\n}\nfunction isNovaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"amazon-nova\"]) || allTokens.some((t) => NOVA_MODEL_PATTERN.test(t));\n}\n\n// Reka\nfunction isRekaLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"reka\"]);\n}\nfunction isRekaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"reka\"]);\n}\n\n// Falcon / TII\nfunction isFalconLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"falcon\", \"tiiuae\"]);\n}\nfunction isFalconLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"falcon\", \"tiiuae\"]);\n}\n\n// Databricks DBRX\nfunction isDbrxLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"dbrx\", \"databricks\"]);\n}\nfunction isDbrxLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"dbrx\", \"databricks\"]);\n}\n\n// MosaicML MPT\nfunction isMptLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"mosaicml\", \"mpt-\"]) || tokens.some((t) => MPT_MODEL_PATTERN.test(t));\n}\nfunction isMptLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"mosaicml\", \"mpt-\"]) || allTokens.some((t) => MPT_MODEL_PATTERN.test(t));\n}\n\n// StableLM / Stability AI\nfunction isStableLMLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"stablelm\", \"stable-lm\", \"stability-ai\"]);\n}\nfunction isStableLMLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"stablelm\", \"stable-lm\", \"stability-ai\"]);\n}\n\n// BAAI / Aquila\nfunction isAquilaLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"aquila\", \"baai\"]);\n}\nfunction isAquilaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"aquila\", \"baai\"]);\n}\n\n// LG EXAONE\nfunction isExaoneLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"exaone\"]);\n}\nfunction isExaoneLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"exaone\"]);\n}\n\n// Naver HyperCLOVA X (conservative: hyperclova, clova-x only)\nfunction isHyperCLOVALikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"hyperclova\", \"clova-x\"]);\n}\nfunction isHyperCLOVALikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"hyperclova\", \"clova-x\"]);\n}\n\n// Aleph Alpha Luminous\nfunction isLuminousLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"luminous\", \"aleph-alpha\"]) || tokens.some((t) => ALEPH_MODEL_PATTERN.test(t));\n}\nfunction isLuminousLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"luminous\", \"aleph-alpha\"]) || allTokens.some((t) => ALEPH_MODEL_PATTERN.test(t));\n}\n\n// Nous / Hermes / OpenHermes\nfunction isHermesLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"nous\", \"hermes\", \"openhermes\"]);\n}\nfunction isHermesLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"nous\", \"hermes\", \"openhermes\"]);\n}\n\n// ── More OpenAI-compatible model detection (batch 4, 18 families) ──\n\n// IBM Granite\nfunction isGraniteLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"granite\", \"ibm-granite\"]);\n}\nfunction isGraniteLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"granite\", \"ibm-granite\"]);\n}\n\n// Snowflake Arctic\nfunction isArcticLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"snowflake-arctic\"]) || tokens.some((t) => ARCTIC_MODEL_PATTERN.test(t));\n}\nfunction isArcticLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"snowflake-arctic\"]) || allTokens.some((t) => ARCTIC_MODEL_PATTERN.test(t));\n}\n\n// Huawei Pangu / 盘古\nfunction isPanguLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"pangu\", \"pan-gu\", \"盘古\", \"huawei-pangu\"]);\n}\nfunction isPanguLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"pangu\", \"pan-gu\", \"盘古\", \"huawei-pangu\"]);\n}\n\n// SenseTime SenseNova / 商汤\nfunction isSenseNovaLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"sensenova\", \"sense-nova\", \"sensechat\", \"商汤\"]);\n}\nfunction isSenseNovaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"sensenova\", \"sense-nova\", \"sensechat\", \"商汤\"]);\n}\n\n// 360 Zhinao / 智脑\nfunction isZhinaoLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"360gpt\", \"360-gpt\", \"zhinao\", \"智脑\"]);\n}\nfunction isZhinaoLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"360gpt\", \"360-gpt\", \"zhinao\", \"智脑\"]);\n}\n\n// OpenBMB MiniCPM\nfunction isMiniCPMLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"minicpm\", \"mini-cpm\", \"openbmb\"]);\n}\nfunction isMiniCPMLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"minicpm\", \"mini-cpm\", \"openbmb\"]);\n}\n\n// XVERSE\nfunction isXVerseLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"xverse\"]);\n}\nfunction isXVerseLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"xverse\"]);\n}\n\n// OrionStar Orion\nfunction isOrionLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"orionstar\", \"orion-star\"]) || tokens.some((t) => ORION_MODEL_PATTERN.test(t));\n}\nfunction isOrionLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"orionstar\", \"orion-star\"]) || allTokens.some((t) => ORION_MODEL_PATTERN.test(t));\n}\n\n// OpenChat\nfunction isOpenChatLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"openchat\"]);\n}\nfunction isOpenChatLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"openchat\"]);\n}\n\n// Vicuna\nfunction isVicunaLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"vicuna\"]);\n}\nfunction isVicunaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"vicuna\"]);\n}\n\n// WizardLM / WizardCoder\nfunction isWizardLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"wizardlm\", \"wizard-lm\", \"wizardcoder\", \"wizard-coder\"]);\n}\nfunction isWizardLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"wizardlm\", \"wizard-lm\", \"wizardcoder\", \"wizard-coder\"]);\n}\n\n// Zephyr\nfunction isZephyrLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"zephyr\"]);\n}\nfunction isZephyrLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"zephyr\"]);\n}\n\n// Dolphin\nfunction isDolphinLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"dolphin\"]);\n}\nfunction isDolphinLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"dolphin\"]);\n}\n\n// OpenOrca\nfunction isOpenOrcaLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"openorca\", \"open-orca\"]);\n}\nfunction isOpenOrcaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"openorca\", \"open-orca\"]);\n}\n\n// Starling\nfunction isStarlingLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"starling\"]);\n}\nfunction isStarlingLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"starling\"]);\n}\n\n// BLOOM / BigScience\nfunction isBloomLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"bloom\", \"bigscience\"]);\n}\nfunction isBloomLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"bloom\", \"bigscience\"]);\n}\n\n// RWKV\nfunction isRwkvLikeModel(model: PiModel | undefined): boolean {\n return hasAnyTokenContaining(getModelIdNameTokenValues(model), [\"rwkv\"]);\n}\nfunction isRwkvLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n return modelOrAssistantMessageHas(message, model, [\"rwkv\"]);\n}\n\n// Cohere Aya\nfunction isAyaLikeModel(model: PiModel | undefined): boolean {\n const tokens = getModelIdNameTokenValues(model);\n return hasAnyTokenContaining(tokens, [\"aya-expanse\"]) || tokens.some((t) => AYA_MODEL_PATTERN.test(t));\n}\nfunction isAyaLikeAssistantMessage(message: unknown, model: PiModel | undefined): boolean {\n const allTokens = [\n ...getModelIdNameTokenValues(model),\n ...getAssistantMessageModelTokenValues(message),\n ];\n return hasAnyTokenContaining(allTokens, [\"aya-expanse\"]) || allTokens.some((t) => AYA_MODEL_PATTERN.test(t));\n}\n\n// ── Model key ──────────────────────────────────────────────────────\n\nfunction modelKey(model: ModelIdentity): string {\n return `${model.provider}/${model.id}`;\n}\n\nfunction isRouterModel(model: PiModel | undefined): boolean {\n return lower(model?.provider) === \"router\";\n}\n\nfunction modelFromAssistantMessage(message: unknown, fallback: PiModel | undefined): PiModel | undefined {\n const record = getAssistantRecord(message);\n if (!record) return fallback;\n\n const id = firstNonEmptyString(record.responseModel, record.model, fallback?.id);\n const provider = firstNonEmptyString(record.provider, fallback?.provider);\n const api = firstNonEmptyString(record.api, fallback?.api) ?? \"\";\n if (!id || !provider) return fallback;\n\n const fallbackName = isNonEmptyString(fallback?.name) ? fallback.name : undefined;\n const preservesFallbackIdentity =\n !isVirtualRoutingModel(fallback) &&\n provider === fallback?.provider &&\n id === fallback?.id &&\n fallbackName !== undefined;\n\n return {\n ...(fallback ?? {}),\n id,\n // Direct providers such as kimi-coding may echo only a short model id\n // (`k3`) while the active model name (`Kimi K3`) carries the adapter token.\n // Preserve that display name only when response and fallback identities are\n // exactly the same; routed/different identities keep message-local naming.\n name: preservesFallbackIdentity ? fallbackName : id,\n provider,\n api,\n baseUrl: fallback?.baseUrl ?? \"\",\n reasoning: fallback?.reasoning ?? false,\n input: fallback?.input ?? [\"text\"],\n cost: fallback?.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: fallback?.contextWindow ?? 0,\n maxTokens: fallback?.maxTokens ?? 0,\n } as PiModel;\n}\n\nfunction keyForModelExt(model: { provider: string; id: string }): string {\n return `${model.provider}/${model.id}`;\n}\n\n/**\n * For direct (non-virtual-routing) providers, the upstream API may normalize or\n * rename the model id echoed in its response (e.g. a request to\n * `zai-org/GLM-5.2-FP8` returns a message whose `model` field is\n * `GLM5.2-FP8`). Writing stats under the echoed name fragments the bucket\n * away from the active-model key the footer reads (`totalsByModel[ctx.model]`),\n * so the footer shows 0% even when the backend is hitting cache.\n *\n * When the response-derived statsModel differs from the active context model\n * only in name (same provider + same cache adapter), consolidate stats back to\n * the active model identity. Virtual routing providers are excluded — their\n * message-local metadata is authoritative for router correctness (spec:\n * `message_end` MUST prefer assistant message metadata).\n *\n * Never merges across providers or across adapters, so genuinely different\n * models are never combined.\n */\nfunction consolidateDirectProviderStatsModel(\n statsModel: PiModel | undefined,\n ctxModel: PiModel | undefined,\n ctx?: Pick<ExtensionContext, \"sessionManager\">,\n): PiModel | undefined {\n if (!statsModel || !ctxModel) return statsModel;\n // Virtual routing providers keep message-local stats identity.\n if (isVirtualRoutingModel(ctxModel, ctx)) return statsModel;\n // Only consolidate within the same provider.\n if (statsModel.provider !== ctxModel.provider) return statsModel;\n // Only consolidate when both resolve to the same cache adapter object, so\n // genuinely different models sharing a provider (or sharing the same adapter\n // family id, e.g. GPT and GLM both report family id \"openai\") are never\n // merged. `selectAdapterForModel` returns the precise adapter object, so\n // object identity is the correct criterion.\n const statsAdapter = selectAdapterForModel(statsModel);\n const ctxAdapter = selectAdapterForModel(ctxModel);\n if (!statsAdapter || !ctxAdapter || statsAdapter !== ctxAdapter) return statsModel;\n // No drift — nothing to consolidate.\n if (statsModel.id === ctxModel.id) return statsModel;\n // Consolidate: pin stats to the active-model identity the footer reads.\n return {\n ...statsModel,\n id: ctxModel.id,\n name: ctxModel.name || ctxModel.id,\n };\n}\n\nfunction usageRecordFromAssistant(message: unknown): UnknownRecord | undefined {\n return asRecord(getAssistantRecord(message)?.usage);\n}\n\nfunction getNestedRecord(record: UnknownRecord | undefined, key: string): UnknownRecord | undefined {\n return asRecord(record?.[key]);\n}\n\nfunction getFirstNonNegativeNumber(...values: unknown[]): number | undefined {\n for (const value of values) {\n const number = getNumber(value);\n if (number !== undefined && number >= 0) return number;\n }\n return undefined;\n}\n\nfunction readCachedTokensFromDetails(details: UnknownRecord | undefined): number | undefined {\n return getFirstNonNegativeNumber(details?.cached_tokens, details?.cachedTokens);\n}\n\nfunction readCacheWriteFromDetails(details: UnknownRecord | undefined): number | undefined {\n return getFirstNonNegativeNumber(details?.cache_write_tokens, details?.cacheWriteTokens);\n}\n\n// Pi normalizes provider-specific raw usage (prompt_cache_hit_tokens, cached_tokens,\n// cache_read_input_tokens, etc.) into a common shape:\n// input = uncached prompt portion (total prompt minus cacheRead minus cacheWrite)\n// cacheRead = tokens read from a previously-cached prefix\n// cacheWrite= tokens newly written into cache in this request\n//\n// We reconstruct the total prompt-token count as input + cacheRead + cacheWrite.\n// Pi guarantees that input, cacheRead, and cacheWrite are always present on\n// assistant messages processed through its provider pipeline (at least as zero).\n//\n// Only DeepSeek sets allowInputOnly=true so that a cache miss (cacheRead=0) still\n// contributes total input tokens to the denominator.\nfunction getPiNormalizedUsage(message: unknown, allowInputOnly = false): UsageSnapshot | undefined {\n const usage = usageRecordFromAssistant(message);\n if (!usage) return undefined;\n\n const input = getNonNegativeNumber(usage, \"input\");\n const cacheRead = getNonNegativeNumber(usage, \"cacheRead\");\n const cacheWrite = getNonNegativeNumber(usage, \"cacheWrite\");\n const hasCacheSignal = cacheRead !== undefined || cacheWrite !== undefined;\n\n if (!hasCacheSignal && (input === undefined || !allowInputOnly)) return undefined;\n\n // Under healthy Pi normalization input is the uncached portion, so\n // totalInput = input + cacheRead + cacheWrite gives the full prompt token count.\n // Guard against degenerate reads where a broken proxy omits prompt_tokens and\n // Pi's input falls to zero: totalInput must never be less than cacheRead + cacheWrite.\n const computed = (input ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0);\n const floor = (cacheRead ?? 0) + (cacheWrite ?? 0);\n return {\n cacheRead: cacheRead ?? 0,\n cacheWrite: cacheWrite ?? 0,\n totalInput: computed >= floor ? computed : floor,\n };\n}\n\n// Raw fallback for DeepSeek responses that still carry their native usage fields.\n// In practice Pi normalizes usage before message_end fires, so this path is only\n// reached when Pi-normalized fields are absent (e.g. custom/foreign providers).\nfunction getDeepSeekRawUsage(message: unknown): UsageSnapshot | undefined {\n const usage = usageRecordFromAssistant(message);\n if (!usage) return undefined;\n\n const cacheRead = getFirstNonNegativeNumber(usage.prompt_cache_hit_tokens);\n if (cacheRead === undefined) return undefined;\n\n const cacheMiss = getFirstNonNegativeNumber(usage.prompt_cache_miss_tokens);\n const promptTokens = getFirstNonNegativeNumber(usage.prompt_tokens);\n // DeepSeek guarantees prompt_tokens = prompt_cache_hit_tokens + prompt_cache_miss_tokens.\n const totalInput = promptTokens ?? cacheRead + (cacheMiss ?? 0);\n\n return { cacheRead, cacheWrite: 0, totalInput };\n}\n\n// Raw fallback for OpenAI-family responses that still carry their native usage fields.\n// In practice Pi normalizes usage before message_end fires, so this path is only\n// reached when Pi-normalized fields are absent (e.g. custom/foreign providers).\nfunction getOpenAIRawUsage(message: unknown): UsageSnapshot | undefined {\n const usage = usageRecordFromAssistant(message);\n if (!usage) return undefined;\n\n const promptDetails = getNestedRecord(usage, \"prompt_tokens_details\") ?? getNestedRecord(usage, \"promptTokensDetails\");\n const inputDetails = getNestedRecord(usage, \"input_tokens_details\") ?? getNestedRecord(usage, \"inputTokensDetails\");\n const cacheRead = readCachedTokensFromDetails(promptDetails) ?? readCachedTokensFromDetails(inputDetails);\n if (cacheRead === undefined) return undefined;\n\n const cacheWrite = readCacheWriteFromDetails(promptDetails) ?? readCacheWriteFromDetails(inputDetails) ?? 0;\n const totalInput = getFirstNonNegativeNumber(\n usage.prompt_tokens,\n usage.promptTokens,\n usage.input_tokens,\n usage.inputTokens,\n ) ?? cacheRead + cacheWrite;\n\n return { cacheRead, cacheWrite, totalInput };\n}\n\n// Raw fallback for Anthropic/Claude responses that still carry their native usage fields.\n// In practice Pi normalizes usage before message_end fires, so this path is only\n// reached when Pi-normalized fields are absent (e.g. custom/foreign providers).\nfunction getAnthropicRawUsage(message: unknown): UsageSnapshot | undefined {\n const usage = usageRecordFromAssistant(message);\n if (!usage) return undefined;\n\n const cacheRead = getFirstNonNegativeNumber(usage.cache_read_input_tokens, usage.cacheReadInputTokens);\n const cacheWrite = getFirstNonNegativeNumber(usage.cache_creation_input_tokens, usage.cacheCreationInputTokens);\n if (cacheRead === undefined && cacheWrite === undefined) return undefined;\n\n // Anthropic input_tokens = tokens after the last cache breakpoint (neither read nor written).\n const input = getFirstNonNegativeNumber(usage.input_tokens, usage.inputTokens) ?? 0;\n return {\n cacheRead: cacheRead ?? 0,\n cacheWrite: cacheWrite ?? 0,\n totalInput: input + (cacheRead ?? 0) + (cacheWrite ?? 0),\n };\n}\n\n// Raw fallback for Gemini/Vertex responses that still carry their native usage fields.\n// In practice Pi normalizes usage before message_end fires, so this path is only\n// reached when Pi-normalized fields are absent (e.g. custom/foreign providers).\nfunction getGeminiRawUsage(message: unknown): UsageSnapshot | undefined {\n const record = getAssistantRecord(message);\n if (!record) return undefined;\n\n const usage = asRecord(record.usage);\n const metadata =\n getNestedRecord(record, \"usageMetadata\") ??\n getNestedRecord(record, \"usage_metadata\") ??\n getNestedRecord(usage, \"usageMetadata\") ??\n getNestedRecord(usage, \"usage_metadata\") ??\n usage;\n if (!metadata) return undefined;\n\n const cacheRead = getFirstNonNegativeNumber(\n metadata.cachedContentTokenCount,\n metadata.cached_content_token_count,\n );\n if (cacheRead === undefined) return undefined;\n\n const totalInput = getFirstNonNegativeNumber(\n metadata.promptTokenCount,\n metadata.prompt_token_count,\n metadata.inputTokenCount,\n metadata.input_token_count,\n usage?.input_tokens,\n usage?.inputTokens,\n usage?.prompt_tokens,\n usage?.promptTokens,\n ) ?? cacheRead;\n\n return { cacheRead, cacheWrite: 0, totalInput };\n}\n\n// Try Pi-normalized usage first (always present for messages that went through Pi's\n// provider pipeline). Fall back to provider-specific raw-field readers when Pi-normalized\n// fields are absent (e.g. messages from custom/foreign providers whose raw usage shape\n// matches the official API).\nfunction normalizeWithFallback(\n message: unknown,\n rawNormalizer: (message: unknown) => UsageSnapshot | undefined,\n options: { allowInputOnlyPiUsage?: boolean } = {},\n): UsageSnapshot | undefined {\n return getPiNormalizedUsage(message, options.allowInputOnlyPiUsage) ?? rawNormalizer(message);\n}\n\nfunction addOpenAIPromptCacheKey(payload: unknown, cacheKey: string | undefined): unknown | undefined {\n const record = asRecord(payload);\n const normalizedCacheKey = clampPromptCacheKey(cacheKey);\n if (!record || !normalizedCacheKey) return undefined;\n\n if (hasEffectivePromptCacheKey(record)) {\n return undefined;\n }\n\n return { ...record, prompt_cache_key: normalizedCacheKey };\n}\n\nfunction hasEffectivePromptCacheKey(record: UnknownRecord): boolean {\n return isNonEmptyString(record.prompt_cache_key) || isNonEmptyString(record.promptCacheKey);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction isOfficialOpenAIBaseUrl(model: PiModel): boolean {\n const value = lower(model.baseUrl).trim();\n if (!value) {\n return lower(model.provider) === \"openai\";\n }\n\n try {\n return new URL(value).hostname === \"api.openai.com\";\n } catch {\n return value === \"api.openai.com\" || value.startsWith(\"api.openai.com/\");\n }\n}\n\nfunction describeMissingOpenAIFamilyProxyCompat(model: PiModel): string[] {\n const compat = getCompat(model);\n const missing: string[] = [];\n\n if (!isOpenAIFamilyModel(model)) return missing;\n if (!isOpenAICompatibleProxyApi(model.api)) return missing;\n if (isOfficialOpenAIBaseUrl(model)) return missing;\n\n if (compat.sendSessionAffinityHeaders !== true) {\n missing.push(\"sendSessionAffinityHeaders\");\n }\n\n return missing;\n}\n\n/**\n * Like describeMissingOpenAIFamilyProxyCompat but without the isOpenAIFamilyModel\n * gate. Warns for ANY model using openai-completions through a non-official base\n * URL — covers GPT, Kimi, Qwen, GLM, MiniMax, Mimo, Hunyuan, and any other\n * OpenAI-compatible proxy.\n */\nfunction describeMissingOpenAICompatibleProxyCompat(model: PiModel): string[] {\n const compat = getCompat(model);\n const missing: string[] = [];\n\n if (!isOpenAICompatibleProxyApi(model.api)) return missing;\n if (isOfficialOpenAIBaseUrl(model)) return missing;\n if (isPiBuiltInLlamaCppModel(model)) return missing;\n\n if (compat.sendSessionAffinityHeaders === undefined) {\n missing.push(\"sendSessionAffinityHeaders\");\n }\n\n // Explicit `sendSessionAffinityHeaders: false` is a valid safe opt-out for\n // proxies/CDNs/WAFs that block Pi's custom affinity headers with HTTP 403.\n // Treat only a missing/undefined value as missing compat; do not mark an\n // intentional false override as ⚠️ compat or let /cache-optimizer fix turn it\n // back to true.\n //\n // NOTE: supportsLongCacheRetention is intentionally NOT checked here.\n // Per spec, it is optional/risky advisory text only and must NOT trigger\n // the ⚠️ compat marker. The before_provider_request hook proactively\n // strips prompt_cache_retention for models without explicit opt-in,\n // so 400 errors are prevented regardless of this compat flag.\n // Doctor/compat may mention it as optional guidance separately.\n\n return missing;\n}\n\nfunction describeOptionalOpenAICompatibleProxyCompat(model: PiModel): string[] {\n const compat = getCompat(model);\n const optional: string[] = [];\n\n if (!isOpenAICompatibleProxyApi(model.api)) return optional;\n if (isOfficialOpenAIBaseUrl(model)) return optional;\n if (isPiBuiltInLlamaCppModel(model)) return optional;\n\n if (compat.supportsLongCacheRetention !== true) {\n optional.push(\"supportsLongCacheRetention\");\n }\n\n return optional;\n}\n\nfunction buildSafeOpenAIProxyCompatSuggestion(missing: string[]): Record<string, boolean> {\n const suggestion: Record<string, boolean> = {};\n if (missing.includes(\"sendSessionAffinityHeaders\")) {\n suggestion.sendSessionAffinityHeaders = true;\n }\n // supportsLongCacheRetention is NOT suggested here — per spec it is\n // optional/risky and must not appear in the copyable safe snippet.\n // The proactive stripping in before_provider_request handles 400 prevention.\n return suggestion;\n}\n\nfunction getPromptCacheRetentionUnsupportedHint(): string {\n return \"If this channel returns `400 Unsupported parameter: prompt_cache_retention`, remove/avoid `supportsLongCacheRetention`; this extension does not write that field directly, but Pi may send it when long retention is requested and compat says the proxy supports it.\";\n}\n\nfunction hasPromptCacheRetentionUnsupportedText(value: unknown): boolean {\n const normalized = lower(value);\n if (!normalized.includes(\"prompt_cache_retention\")) return false;\n\n return [\n \"unsupported parameter\",\n \"unsupported_parameter\",\n \"unknown parameter\",\n \"not supported\",\n \"unsupported field\",\n \"extra inputs\",\n \"not permitted\",\n \"unrecognized\",\n ].some((needle) => normalized.includes(needle));\n}\n\nfunction hasPromptCacheRetentionUnsupportedSignal(headers: Record<string, string> | undefined): boolean {\n if (!headers) return false;\n return hasPromptCacheRetentionUnsupportedText(\n Object.entries(headers)\n .map(([key, value]) => `${key}: ${value}`)\n .join(\"\\n\"),\n );\n}\n\nfunction hasPromptCacheRetentionUnsupportedErrorMessage(message: unknown): boolean {\n const record = getAssistantRecord(message);\n return record?.stopReason === \"error\" &&\n hasPromptCacheRetentionUnsupportedText(record.errorMessage);\n}\n\ntype CompatAdvicePlacement = {\n providerLabel?: string;\n modelId?: string;\n};\n\nfunction buildProviderCompatOverride(providerLabel: string, compat: Record<string, unknown>): Record<string, unknown> {\n return {\n providers: {\n [providerLabel]: {\n compat,\n },\n },\n };\n}\n\nfunction buildModelCompatOverride(providerLabel: string, modelId: string, compat: Record<string, unknown>): Record<string, unknown> {\n return {\n providers: {\n [providerLabel]: {\n modelOverrides: {\n [modelId]: {\n compat,\n },\n },\n },\n },\n };\n}\n\nfunction appendCredentialSafeProviderGuidance(lines: string[], placement: CompatAdvicePlacement, compatSuggestion: Record<string, unknown>): void {\n const providerLabel = placement.providerLabel;\n if (!providerLabel) return;\n\n lines.push(\"\");\n lines.push(\"If this channel has no models.json provider entry yet:\");\n lines.push(\"- Keep existing authentication as-is; do not copy credentials, tokens, or API keys.\");\n lines.push(`- Add only cache/routing compat overrides in ${getModelsJsonDisplayPath()}.`);\n\n if (Object.keys(compatSuggestion).length === 0) {\n lines.push(\"- No safe copyable override is available for the missing flags shown above.\");\n return;\n }\n\n lines.push(\"Provider-level minimal override:\");\n lines.push(JSON.stringify(buildProviderCompatOverride(providerLabel, compatSuggestion), null, 2));\n\n if (placement.modelId) {\n lines.push(\"Single-model override (use this if only this model should change):\");\n lines.push(JSON.stringify(buildModelCompatOverride(providerLabel, placement.modelId, compatSuggestion), null, 2));\n }\n}\n\nfunction appendOpenAIProxyCompatAdviceLines(lines: string[], missing: string[], options: { includeJsonIntro?: boolean } & CompatAdvicePlacement = {}): void {\n const suggestion = buildSafeOpenAIProxyCompatSuggestion(missing);\n const hasSafeSuggestion = Object.keys(suggestion).length > 0;\n\n if (hasSafeSuggestion) {\n if (options.includeJsonIntro !== false) {\n lines.push(\"Safe default suggestion:\");\n }\n lines.push(JSON.stringify(suggestion, null, 2));\n }\n\n if (missing.includes(\"sendSessionAffinityHeaders\")) {\n lines.push(\"- sendSessionAffinityHeaders: recommended for third-party proxies when supported; it helps keep one Pi session on the same upstream/backend.\");\n }\n appendCredentialSafeProviderGuidance(lines, options, suggestion);\n}\n\nfunction appendOptionalOpenAIProxyCompatAdviceLines(lines: string[], optional: string[]): void {\n if (!optional.includes(\"supportsLongCacheRetention\")) return;\n lines.push(\"\");\n lines.push(\"Optional (not required, not auto-fixed):\");\n lines.push(\"- supportsLongCacheRetention: enable only after your endpoint/proxy explicitly supports OpenAI long prompt cache retention.\");\n lines.push(`- ${getPromptCacheRetentionUnsupportedHint()}`);\n}\n\n/**\n * Build the warning text displayed to users when an OpenAI-family third-party\n * proxy is missing one or more cache/session-affinity compat flags.\n *\n * The returned string contains a parseable JSON object (via JSON.stringify)\n * listing only the missing flags with recommended value `true`. Inline\n * explanations for each flag follow the JSON snippet as separate prose lines,\n * so the JSON remains valid and copyable.\n *\n * Expected use: the openai adapter's warningText calls this function; tests\n * exercise it via __internals_for_tests.\n */\nfunction buildOpenAIProxyCompatWarningText(key: string, missing: string[]): string {\n // Extract provider id from the model key (e.g. \"otokapi/gpt-5.5\" -> \"otokapi\").\n // If no slash is found, fall back to the key itself.\n const slashIdx = key.indexOf(\"/\");\n const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;\n const modelId = slashIdx > 0 ? key.slice(slashIdx + 1) : undefined;\n\n const modelsJsonPath = getModelsJsonDisplayPath();\n const lines: string[] = [\n `💡 pi-cache-optimizer: ${key} is a third-party GPT/OpenAI-compatible proxy but merged compat lacks ${missing.join(\" and \")}.`,\n `Edit ${modelsJsonPath} -> providers[\"${providerLabel}\"] -> compat (at the same level as baseUrl/api/apiKey/models).`,\n ``,\n ];\n\n appendOpenAIProxyCompatAdviceLines(lines, missing, { providerLabel, modelId });\n\n return lines.join(\"\\n\");\n}\n\nfunction describeMissingDeepSeekCompat(model: PiModel): string[] {\n const compat = getCompat(model);\n const missing: string[] = [];\n\n if (compat.supportsLongCacheRetention !== true) {\n missing.push(\"supportsLongCacheRetention\");\n }\n if (model.api !== \"openai-responses\" && compat.sendSessionAffinityHeaders !== true) {\n missing.push(\"sendSessionAffinityHeaders\");\n }\n if (compat.requiresReasoningContentOnAssistantMessages !== true) {\n missing.push(\"requiresReasoningContentOnAssistantMessages\");\n }\n if (compat.thinkingFormat !== \"deepseek\") {\n missing.push(\"thinkingFormat\");\n }\n\n return missing;\n}\n\nfunction isDeepSeekCompatCheckApplicable(model: PiModel): boolean {\n return isDeepSeekLikeModel(model) && isOpenAICompatibleApi(model.api) && !isPiBuiltInLlamaCppModel(model);\n}\n\nfunction describeMissingCacheCompatForModel(model: PiModel): string[] {\n if (isAdaptiveThinkingCompatApplicable(model)) {\n return describeMissingAdaptiveThinkingCompat(model);\n }\n if (isDeepSeekCompatCheckApplicable(model)) {\n return describeMissingDeepSeekCompat(model);\n }\n return describeMissingOpenAICompatibleProxyCompat(model);\n}\n\nfunction buildDeepSeekCompatSuggestion(missing: string[]): Record<string, unknown> {\n const suggestion: Record<string, unknown> = {};\n\n if (missing.includes(\"supportsLongCacheRetention\")) {\n suggestion.supportsLongCacheRetention = true;\n }\n if (missing.includes(\"sendSessionAffinityHeaders\")) {\n suggestion.sendSessionAffinityHeaders = true;\n }\n if (missing.includes(\"requiresReasoningContentOnAssistantMessages\")) {\n suggestion.requiresReasoningContentOnAssistantMessages = true;\n }\n if (missing.includes(\"thinkingFormat\")) {\n suggestion.thinkingFormat = \"deepseek\";\n }\n\n return suggestion;\n}\n\nfunction appendDeepSeekCompatAdviceLines(lines: string[], missing: string[], placement: CompatAdvicePlacement = {}): void {\n const suggestion = buildDeepSeekCompatSuggestion(missing);\n if (Object.keys(suggestion).length > 0) {\n lines.push(\"Recommended DeepSeek compat snippet:\");\n lines.push(JSON.stringify(suggestion, null, 2));\n }\n\n if (missing.includes(\"requiresReasoningContentOnAssistantMessages\")) {\n lines.push('- requiresReasoningContentOnAssistantMessages: true keeps replayed assistant turns compatible with DeepSeek reasoning_content requirements.');\n }\n if (missing.includes(\"thinkingFormat\")) {\n lines.push('- thinkingFormat: \"deepseek\" tells Pi to use DeepSeek reasoning/thinking parameter format.');\n }\n if (missing.includes(\"sendSessionAffinityHeaders\")) {\n lines.push(\"- sendSessionAffinityHeaders: recommended for OpenAI-compatible DeepSeek proxies when supported; it helps keep one Pi session on the same upstream/backend.\");\n }\n if (missing.includes(\"supportsLongCacheRetention\")) {\n lines.push(\"- supportsLongCacheRetention: enable for DeepSeek-compatible endpoints that support long cache retention.\");\n }\n\n appendCredentialSafeProviderGuidance(lines, placement, suggestion);\n}\n\nfunction buildDeepSeekCompatWarningText(key: string, missing: string[]): string {\n const slashIdx = key.indexOf(\"/\");\n const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;\n const modelId = slashIdx > 0 ? key.slice(slashIdx + 1) : undefined;\n const modelsJsonPath = getModelsJsonDisplayPath();\n const lines: string[] = [\n `💡 pi-cache-optimizer: ${key} is DeepSeek-like but merged compat lacks ${missing.join(\" and \")}.`,\n `Proxies may reduce or hide cache hits. Edit ${modelsJsonPath} -> providers[\"${providerLabel}\"] -> compat (at the same level as baseUrl/api/apiKey/models).`,\n \"\",\n ];\n\n appendDeepSeekCompatAdviceLines(lines, missing, { providerLabel, modelId });\n\n return lines.join(\"\\n\");\n}\n\nconst CACHE_PROVIDER_ADAPTERS: CacheProviderAdapter[] = [\n {\n id: \"deepseek\",\n label: \"DS cache\",\n matchesModel: isDeepSeekLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isDeepSeekLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getDeepSeekRawUsage, { allowInputOnlyPiUsage: true });\n },\n warningText(model) {\n if (!isDeepSeekCompatCheckApplicable(model)) return undefined;\n\n const missing = describeMissingDeepSeekCompat(model);\n if (missing.length === 0) return undefined;\n\n const key = modelKey(model);\n return buildDeepSeekCompatWarningText(key, missing);\n },\n },\n {\n id: \"claude\",\n label: \"Claude cache\",\n showCacheWrite: true,\n matchesModel: isClaudeLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isClaudeLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getAnthropicRawUsage);\n },\n warningText(model) {\n if (!isClaudeLikeModel(model) || !isOpenAICompatibleApi(model.api) || isPiBuiltInLlamaCppModel(model)) return undefined;\n if (getCompat(model).cacheControlFormat === \"anthropic\") return undefined;\n\n return (\n `💡 Cache optimizer: ${modelKey(model)} looks Claude/Anthropic-like but OpenAI-compatible compat lacks cacheControlFormat: \"anthropic\". ` +\n \"Pi may not place Anthropic cache_control breakpoints unless this endpoint supports and enables that compat flag.\"\n );\n },\n },\n {\n id: \"openai\",\n label: \"OpenAI cache\",\n matchesModel: isOpenAIFamilyModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isOpenAIFamilyAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"gemini\",\n label: \"Gemini cache\",\n matchesModel: isGeminiLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isGeminiLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getGeminiRawUsage);\n },\n },\n // ── Non-GPT OpenAI-compatible adapters ──────────────────────\n {\n id: \"openai\" as CacheProviderId,\n label: \"Kimi cache\",\n matchesModel: isKimiLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isKimiLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Qwen cache\",\n matchesModel: isQwenLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isQwenLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"GLM cache\",\n matchesModel: isGLMLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isGLMLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"MiniMax cache\",\n matchesModel: isMiniMaxLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isMiniMaxLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Mimo cache\",\n matchesModel: isMimoLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isMimoLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Hunyuan cache\",\n matchesModel: isHunyuanLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isHunyuanLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n // ── More OpenAI-compatible adapters ──────────────────────────\n {\n id: \"openai\" as CacheProviderId,\n label: \"Mistral cache\",\n matchesModel: isMistralLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isMistralLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Grok cache\",\n matchesModel: isGrokLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isGrokLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Llama cache\",\n matchesModel: isLlamaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isLlamaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Nemotron cache\",\n matchesModel: isNemotronLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isNemotronLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Cohere cache\",\n matchesModel: isCohereLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isCohereLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Yi cache\",\n matchesModel: isYiLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isYiLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n // ── More OpenAI-compatible adapters (batch 2) ───────────────────\n {\n id: \"openai\" as CacheProviderId,\n label: \"Doubao cache\",\n matchesModel: isDoubaoLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isDoubaoLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"ERNIE cache\",\n matchesModel: isErnieLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isErnieLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Baichuan cache\",\n matchesModel: isBaichuanLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isBaichuanLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"StepFun cache\",\n matchesModel: isStepFunLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isStepFunLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Spark cache\",\n matchesModel: isSparkLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isSparkLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"InternLM cache\",\n matchesModel: isInternLMLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isInternLMLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Gemma cache\",\n matchesModel: isGemmaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isGemmaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Phi cache\",\n matchesModel: isPhiLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isPhiLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Jamba cache\",\n matchesModel: isJambaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isJambaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Solar cache\",\n matchesModel: isSolarLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isSolarLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n // ── New OpenAI-compatible adapters (batch 3, 12 families) ────────\n {\n id: \"openai\" as CacheProviderId,\n label: \"Sonar cache\",\n matchesModel: isPerplexityLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isPerplexityLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Nova cache\",\n matchesModel: isNovaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isNovaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Reka cache\",\n matchesModel: isRekaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isRekaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Falcon cache\",\n matchesModel: isFalconLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isFalconLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"DBRX cache\",\n matchesModel: isDbrxLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isDbrxLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"MPT cache\",\n matchesModel: isMptLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isMptLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"StableLM cache\",\n matchesModel: isStableLMLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isStableLMLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Aquila cache\",\n matchesModel: isAquilaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isAquilaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"EXAONE cache\",\n matchesModel: isExaoneLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isExaoneLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"HyperCLOVA cache\",\n matchesModel: isHyperCLOVALikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isHyperCLOVALikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Luminous cache\",\n matchesModel: isLuminousLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isLuminousLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Hermes cache\",\n matchesModel: isHermesLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isHermesLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n // ── More OpenAI-compatible adapters (batch 4, 18 families) ────────\n {\n id: \"openai\" as CacheProviderId,\n label: \"Granite cache\",\n matchesModel: isGraniteLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isGraniteLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Arctic cache\",\n matchesModel: isArcticLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isArcticLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Pangu cache\",\n matchesModel: isPanguLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isPanguLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"SenseNova cache\",\n matchesModel: isSenseNovaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isSenseNovaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Zhinao cache\",\n matchesModel: isZhinaoLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isZhinaoLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"MiniCPM cache\",\n matchesModel: isMiniCPMLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isMiniCPMLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"XVERSE cache\",\n matchesModel: isXVerseLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isXVerseLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Orion cache\",\n matchesModel: isOrionLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isOrionLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"OpenChat cache\",\n matchesModel: isOpenChatLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isOpenChatLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Vicuna cache\",\n matchesModel: isVicunaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isVicunaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Wizard cache\",\n matchesModel: isWizardLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isWizardLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Zephyr cache\",\n matchesModel: isZephyrLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isZephyrLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Dolphin cache\",\n matchesModel: isDolphinLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isDolphinLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"OpenOrca cache\",\n matchesModel: isOpenOrcaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isOpenOrcaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Starling cache\",\n matchesModel: isStarlingLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isStarlingLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"BLOOM cache\",\n matchesModel: isBloomLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isBloomLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"RWKV cache\",\n matchesModel: isRwkvLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isRwkvLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n {\n id: \"openai\" as CacheProviderId,\n label: \"Aya cache\",\n matchesModel: isAyaLikeModel,\n matchesAssistantMessage(message, model) {\n if (!isAssistantMessage(message)) return false;\n return isAyaLikeAssistantMessage(message, model);\n },\n normalizeUsage(message) {\n return normalizeWithFallback(message, getOpenAIRawUsage);\n },\n warningText(model) {\n const missing = describeMissingOpenAICompatibleProxyCompat(model);\n if (missing.length === 0) return undefined;\n return buildOpenAIProxyCompatWarningText(modelKey(model), missing);\n },\n },\n];\n\nfunction selectAdapterForModel(model: PiModel | undefined): CacheProviderAdapter | undefined {\n return CACHE_PROVIDER_ADAPTERS.find((adapter) => adapter.matchesModel(model));\n}\n\nfunction selectAdapterForAssistantMessage(message: unknown, model: PiModel | undefined): CacheProviderAdapter | undefined {\n // Assistant message metadata is request-local and authoritative for virtual\n // routing providers. Use it first for every model; direct providers normally\n // echo the same provider/model and therefore remain unchanged.\n const responseModel = modelFromAssistantMessage(message, model);\n return CACHE_PROVIDER_ADAPTERS.find((adapter) => adapter.matchesAssistantMessage(message, responseModel));\n}\n\nfunction notifyCacheCompatIfNeeded(\n model: PiModel | undefined,\n ctx: ExtensionContext,\n warnedModels: Set<string>,\n): void {\n if (!model) return;\n\n // Native anthropic-messages adaptive thinking compat check.\n // Adapter warningText only fires for OpenAI-compatible APIs, so native\n // Anthropic and Kimi Coding adaptive models need a separate check.\n if (isAdaptiveThinkingCompatApplicable(model)) {\n const missing = describeMissingAdaptiveThinkingCompat(model);\n if (missing.length > 0) {\n const key = `adaptive-thinking:${modelKey(model)}`;\n if (!warnedModels.has(key)) {\n warnedModels.add(key);\n ctx.ui.notify(buildAdaptiveThinkingCompatWarningText(modelKey(model), missing), \"warning\");\n }\n }\n // Still check adapter warnings for other compat issues.\n }\n\n const adapter = selectAdapterForModel(model);\n const text = adapter?.warningText?.(model);\n if (!adapter || !text) return;\n\n const key = `${adapter.id}:${modelKey(model)}`;\n if (warnedModels.has(key)) return;\n warnedModels.add(key);\n\n ctx.ui.notify(text, \"warning\");\n}\n\nfunction currentLocalDay(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, \"0\");\n const day = String(now.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n\nfunction emptyCacheStats(day = currentLocalDay()): CacheStats {\n return {\n day,\n totalRequests: 0,\n hitRequests: 0,\n cachedInputTokens: 0,\n cacheWriteInputTokens: 0,\n totalInputTokens: 0,\n };\n}\n\nfunction emptyAllCacheStats(day = currentLocalDay()): Partial<Record<CacheProviderId, CacheStats>> {\n return Object.fromEntries(CACHE_PROVIDER_IDS.map((id) => [id, emptyCacheStats(day)])) as Partial<Record<CacheProviderId, CacheStats>>;\n}\n\nfunction addUsageToCacheStats(stats: CacheStats, usage: UsageSnapshot): void {\n stats.totalRequests += 1;\n if (usage.cacheRead > 0) stats.hitRequests += 1;\n stats.cachedInputTokens += usage.cacheRead;\n stats.cacheWriteInputTokens += usage.cacheWrite;\n stats.totalInputTokens += usage.totalInput;\n}\n\nfunction formatTokenCount(value: number): string {\n const millions = Math.max(0, Math.round(value)) / 1_000_000;\n if (millions === 0) return \"0M\";\n if (millions < 0.001) return `${millions.toFixed(4)}M`;\n if (millions < 0.01) return `${millions.toFixed(3)}M`;\n if (millions >= 10) return `${millions.toFixed(1)}M`;\n return `${millions.toFixed(2)}M`;\n}\n\nfunction formatCacheStats(adapter: CacheProviderAdapter, stats: CacheStats): string {\n const percent = stats.totalInputTokens > 0\n ? (stats.cachedInputTokens / stats.totalInputTokens) * 100\n : 0;\n const writeText = adapter.showCacheWrite && stats.cacheWriteInputTokens > 0\n ? `·write ${formatTokenCount(stats.cacheWriteInputTokens)}`\n : \"\";\n\n return `${adapter.label} ${stats.hitRequests}/${stats.totalRequests}·${formatTokenCount(stats.cachedInputTokens)}/${formatTokenCount(stats.totalInputTokens)} ${percent.toFixed(1)}%${writeText}`;\n}\n\nfunction prefixFooterStatus(statusText: string | undefined): string | undefined {\n if (!statusText || statusText.startsWith(\"· \")) return statusText;\n return `· ${statusText}`;\n}\n\n/**\n * Compute a hit-ratio percentage string for a value between 0 and 1.\n * Returns e.g. \"75%\", \"0%\", \"100%\", or \"N/A\" for zero total.\n */\nfunction formatHitRatio(hits: number, total: number): string {\n if (total <= 0) return \"N/A\";\n return `${Math.round((hits / total) * 100)}%`;\n}\n\n/**\n * Format a token-to-M abbreviation for stats output.\n * Example: 1500000 → \"1.50M\"\n */\nfunction formatTokenM(value: number): string {\n const millions = Math.max(0, Math.round(value)) / 1_000_000;\n if (millions === 0) return \"0\";\n if (millions < 0.01) return millions.toFixed(4);\n if (millions >= 10) return millions.toFixed(1);\n return millions.toFixed(2);\n}\n\n/**\n * Check if an assistant message's usage fields appear to be missing or empty.\n * Returns true when Pi-normalized fields (input, cacheRead, cacheWrite) are all\n * absent/zero AND raw usage fields (prompt_tokens, etc.) are also absent/zero\n * for the given adapter.\n */\nfunction hasMissingUsageFields(message: unknown, adapter: CacheProviderAdapter): boolean {\n const usage = usageRecordFromAssistant(message);\n if (!usage) return true;\n\n // Check Pi-normalized fields\n const input = getNonNegativeNumber(usage, \"input\");\n const cacheRead = getNonNegativeNumber(usage, \"cacheRead\");\n const cacheWrite = getNonNegativeNumber(usage, \"cacheWrite\");\n\n // If Pi-normalized fields exist with non-zero values, usage is present\n if (cacheRead !== undefined || cacheWrite !== undefined || (input !== undefined && input > 0)) {\n return false;\n }\n\n // Check raw usage for the adapter's provider family\n const rawUsage = adapter.normalizeUsage(message);\n if (!rawUsage || (rawUsage.cacheRead === 0 && rawUsage.cacheWrite === 0 && rawUsage.totalInput === 0)) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Build a summary string for the recent trend (last N samples).\n * Example: \"Recent 10: 7/10 hits · 65% tok cached · no missing usage\"\n */\nfunction formatRecentTrendSummary(samples: CacheUsageSample[], maxCount: number): string {\n const recent = samples.slice(-maxCount);\n if (recent.length === 0) return `Recent ${maxCount}: no samples yet`;\n\n const hits = recent.filter((s) => s.hit).length;\n const totalCached = recent.reduce((sum, s) => sum + s.cachedInputTokens, 0);\n const totalInput = recent.reduce((sum, s) => sum + s.totalInputTokens, 0);\n const missingCount = recent.filter((s) => s.missingUsageFields).length;\n\n const hitRatio = formatHitRatio(hits, recent.length);\n const tokenRatio = totalInput > 0 ? formatHitRatio(totalCached, totalInput) : \"N/A\";\n\n let result = `Recent ${recent.length}/${maxCount}: ${hits}/${recent.length} hits · ${tokenRatio} tok cached`;\n if (missingCount > 0) {\n result += ` · ${missingCount} missing usage`;\n }\n return result;\n}\n\n/**\n * Build the output for `/cache-optimizer stats`.\n */\nfunction buildStatsOutput(model: PiModel | undefined, adapter: CacheProviderAdapter | undefined, stats: CacheStats | undefined, recentSamples: CacheUsageSample[]): string {\n const lines: string[] = [];\n\n if (!model || !adapter) {\n lines.push(\"ℹ️ No cache-adapter-matched model active. Select a model with a recognized provider family.\");\n return lines.join(\"\\n\");\n }\n\n const key = modelKey(model);\n const currentStats = stats ?? emptyCacheStats();\n\n lines.push(`Model key: ${key}`);\n lines.push(`Adapter: ${adapter.label}`);\n lines.push(\"\");\n lines.push(\"── Today ──\");\n lines.push(`Requests: ${currentStats.hitRequests} hit / ${currentStats.totalRequests} total · ${formatHitRatio(currentStats.hitRequests, currentStats.totalRequests)}`);\n lines.push(`Cached tokens: ${formatTokenM(currentStats.cachedInputTokens)}M / ${formatTokenM(currentStats.totalInputTokens)}M input · ${currentStats.totalInputTokens > 0 ? `${Math.round((currentStats.cachedInputTokens / currentStats.totalInputTokens) * 100)}%` : \"N/A\"}`);\n if (currentStats.cacheWriteInputTokens > 0) {\n lines.push(`Cache write: ${formatTokenM(currentStats.cacheWriteInputTokens)}M tok`);\n }\n\n lines.push(\"\");\n lines.push(\"── Recent trend ──\");\n lines.push(formatRecentTrendSummary(recentSamples, 10));\n lines.push(formatRecentTrendSummary(recentSamples, 30));\n\n // Check if any sample has missingUsageFields flagged\n const missingAny = recentSamples.some((s) => s.missingUsageFields);\n if (missingAny) {\n lines.push(\"\");\n lines.push(\"⚠️ Some recent responses had missing or empty cache usage fields. Footer may under-report hits.\");\n lines.push(\" The proxy may not return prompt_cache_hit_tokens or usage.input/cacheRead in responses.\");\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction getErrorCode(error: unknown): string | undefined {\n return typeof error === \"object\" && error !== null && \"code\" in error\n ? String((error as { code?: unknown }).code)\n : undefined;\n}\n\nfunction parseCacheStats(value: unknown): CacheStats | undefined {\n const stats = asRecord(value);\n if (!stats || typeof stats.day !== \"string\" || !/^\\d{4}-\\d{2}-\\d{2}$/.test(stats.day)) {\n return undefined;\n }\n\n const totalRequests = getNonNegativeNumber(stats, \"totalRequests\");\n const hitRequests = getNonNegativeNumber(stats, \"hitRequests\");\n const cachedInputTokens = getNonNegativeNumber(stats, \"cachedInputTokens\");\n const cacheWriteInputTokens = getNonNegativeNumber(stats, \"cacheWriteInputTokens\") ?? 0;\n const totalInputTokens = getNonNegativeNumber(stats, \"totalInputTokens\");\n\n if (\n totalRequests === undefined ||\n hitRequests === undefined ||\n cachedInputTokens === undefined ||\n totalInputTokens === undefined ||\n hitRequests > totalRequests ||\n cachedInputTokens > totalInputTokens ||\n cacheWriteInputTokens > totalInputTokens\n ) {\n return undefined;\n }\n\n return {\n day: stats.day,\n totalRequests,\n hitRequests,\n cachedInputTokens,\n cacheWriteInputTokens,\n totalInputTokens,\n };\n}\n\nfunction cloneCacheStats(stats: CacheStats): CacheStats {\n return { ...stats };\n}\n\nfunction addCacheStatsTotals(target: CacheStats, source: CacheStats): void {\n target.totalRequests += source.totalRequests;\n target.hitRequests += source.hitRequests;\n target.cachedInputTokens += source.cachedInputTokens;\n target.cacheWriteInputTokens += source.cacheWriteInputTokens;\n target.totalInputTokens += source.totalInputTokens;\n}\n\nfunction mergeCacheStatsForTotal(existing: CacheStats | undefined, incoming: CacheStats): CacheStats {\n if (!existing) return cloneCacheStats(incoming);\n if (incoming.day > existing.day) return cloneCacheStats(incoming);\n if (incoming.day < existing.day) return existing;\n addCacheStatsTotals(existing, incoming);\n return existing;\n}\n\nfunction deriveTotalsByModelFromSessionStats(statsByModel: Record<string, CacheStats>): Record<string, CacheStats> {\n const totals: Record<string, CacheStats> = {};\n for (const [fullKey, stats] of Object.entries(statsByModel)) {\n totals[modelKeyFromSessionKey(fullKey)] = mergeCacheStatsForTotal(\n totals[modelKeyFromSessionKey(fullKey)],\n stats,\n );\n }\n return totals;\n}\n\nfunction parsePersistedTotalsByModel(value: unknown): Record<string, CacheStats> | undefined {\n const record = asRecord(value);\n if (!record) return undefined;\n\n const totals: Record<string, CacheStats> = {};\n for (const [modelKeyStr, rawStats] of Object.entries(record)) {\n const stats = parseCacheStats(rawStats);\n if (stats) totals[modelKeyStr] = stats;\n }\n return totals;\n}\n\nfunction parsePersistedRoutedModelRef(value: unknown): PersistedRoutedModelRef | undefined {\n const record = asRecord(value);\n const provider = record?.provider;\n const id = record?.id;\n const name = record?.name;\n if (!isNonEmptyString(provider) || !isNonEmptyString(id)) return undefined;\n\n return {\n provider: provider.trim(),\n id: id.trim(),\n name: isNonEmptyString(name) ? name.trim() : id.trim(),\n };\n}\n\nfunction routedModelRefToPiModel(ref: PersistedRoutedModelRef): PiModel {\n return {\n id: ref.id,\n name: ref.name ?? ref.id,\n provider: ref.provider,\n api: \"\",\n baseUrl: \"\",\n reasoning: false,\n input: [\"text\"],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 0,\n maxTokens: 0,\n } as PiModel;\n}\n\nfunction selectFooterStatsForModel(\n mode: FooterStatsMode,\n sessionHash: string | undefined,\n statsByModel: Record<string, CacheStats>,\n totalsByModel: Record<string, CacheStats>,\n model: { provider: string; id: string },\n processByModel: Record<string, CacheStats> = {},\n): CacheStats | undefined {\n if (mode === \"total\") return totalsByModel[modelKey(model)];\n if (mode === \"process\") return processByModel[modelKey(model)];\n if (!sessionHash) return undefined;\n return statsByModel[makeSessionModelKey(sessionHash, model.provider, model.id)];\n}\n\nfunction buildExactRouterStatusEntry(\n sessionHash: string | undefined,\n statsByModel: Record<string, CacheStats>,\n lastRoutedModel: PersistedRoutedModelRef | undefined,\n totalsByModel: Record<string, CacheStats> = {},\n mode: FooterStatsMode = \"total\",\n processByModel: Record<string, CacheStats> = {},\n): { model: PiModel; adapter: CacheProviderAdapter; stats: CacheStats } | undefined {\n if (!sessionHash || !lastRoutedModel) return undefined;\n\n const model = routedModelRefToPiModel(lastRoutedModel);\n const adapter = selectAdapterForModel(model);\n if (!adapter) return undefined;\n\n return {\n model,\n adapter,\n stats: selectFooterStatsForModel(mode, sessionHash, statsByModel, totalsByModel, model, processByModel) ?? emptyCacheStats(),\n };\n}\n\nfunction findBestRouterModelStats(\n mode: FooterStatsMode,\n sessionHash: string | undefined,\n statsByModel: Record<string, CacheStats>,\n totalsByModel: Record<string, CacheStats>,\n processByModel: Record<string, CacheStats> = {},\n): { model: PiModel; adapter: CacheProviderAdapter; stats: CacheStats } | undefined {\n const entries = mode === \"total\"\n ? Object.entries(totalsByModel)\n : mode === \"process\"\n ? Object.entries(processByModel)\n : sessionHash\n ? Object.entries(statsByModel)\n .filter(([key]) => key.startsWith(`${sessionHash}:`))\n .map(([key, stats]) => [key.slice(sessionHash.length + 1), stats] as const)\n : [];\n let best: { model: PiModel; adapter: CacheProviderAdapter; stats: CacheStats; total: number } | undefined;\n\n for (const [modelKeyPart, stats] of entries) {\n const slashIdx = modelKeyPart.indexOf(\"/\");\n if (slashIdx < 1 || slashIdx >= modelKeyPart.length - 1) continue;\n const model = routedModelRefToPiModel({\n provider: modelKeyPart.slice(0, slashIdx),\n id: modelKeyPart.slice(slashIdx + 1),\n });\n const adapter = selectAdapterForModel(model);\n if (!adapter) continue;\n if (!best || stats.totalRequests > best.total) {\n best = { model, adapter, stats, total: stats.totalRequests };\n }\n }\n\n return best ? { model: best.model, adapter: best.adapter, stats: best.stats } : undefined;\n}\n\nfunction parsePersistedCacheStats(value: unknown): CacheStatsState | undefined {\n const record = asRecord(value);\n if (!record) return undefined;\n\n // version 4/5/6: session-scoped stats + legacy family fallback.\n // v5 additionally persists the last actual routed model per session so\n // router/auto can restore the exact upstream footer after /reload.\n // v6 adds provider/model totals used for footer continuity across Pi\n // process/terminal restarts, while retaining session buckets for migration\n // and best-effort preservation of older data.\n if (record.version === 4 || record.version === 5 || record.version === 6) {\n const legacyFamily: Partial<Record<CacheProviderId, CacheStats>> = {};\n const rawFamily = asRecord(record.legacyFamily);\n if (rawFamily) {\n for (const id of CACHE_PROVIDER_IDS) {\n const stats = parseCacheStats(rawFamily[id]);\n if (stats) legacyFamily[id] = stats;\n }\n }\n\n // Collect all session entries into statsByModel with session-hash-prefixed keys\n // (e.g. \"abc123:otokapi/gpt-5.5\") so that writePersistedCacheStats can later\n // reconstruct individual sessions from the flat key format and other sessions'\n // data is not silently lost on round-trip.\n const statsByModel: Record<string, CacheStats> = {};\n const rawSessions = asRecord(record.sessions);\n if (rawSessions) {\n for (const [sessionHash, modelMap] of Object.entries(rawSessions)) {\n const parsedMap = asRecord(modelMap);\n if (parsedMap) {\n for (const [modelKey, val] of Object.entries(parsedMap)) {\n const parsed = parseCacheStats(val);\n if (parsed) statsByModel[`${sessionHash}:${modelKey}`] = parsed;\n }\n }\n }\n }\n\n const lastRoutedModelBySession: Record<string, PersistedRoutedModelRef> = {};\n const rawLastRoutedModels = asRecord(record.lastRoutedModelBySession);\n if (rawLastRoutedModels) {\n for (const [sessionHash, rawModel] of Object.entries(rawLastRoutedModels)) {\n const parsed = parsePersistedRoutedModelRef(rawModel);\n if (parsed) lastRoutedModelBySession[sessionHash] = parsed;\n }\n }\n\n const parsedTotals = parsePersistedTotalsByModel(record.totalsByModel);\n const totalsByModel = parsedTotals ?? deriveTotalsByModelFromSessionStats(statsByModel);\n\n return { statsByModel, totalsByModel, legacyFamily, lastRoutedModelBySession };\n }\n\n // version 3: migrate to v4/v5 semantics by wrapping statsByModel into sessions\n if (record.version === 3) {\n const statsByModel: Record<string, CacheStats> = {};\n const rawModelMap = asRecord(record.statsByModel);\n if (rawModelMap) {\n for (const [key, val] of Object.entries(rawModelMap)) {\n const parsed = parseCacheStats(val);\n if (parsed) statsByModel[key] = parsed;\n }\n }\n\n const legacyFamily: Partial<Record<CacheProviderId, CacheStats>> = {};\n const rawFamily = asRecord(record.legacyFamily);\n if (rawFamily) {\n for (const id of CACHE_PROVIDER_IDS) {\n const stats = parseCacheStats(rawFamily[id]);\n if (stats) legacyFamily[id] = stats;\n }\n }\n\n return { statsByModel, totalsByModel: deriveTotalsByModelFromSessionStats(statsByModel), legacyFamily };\n }\n\n // version 2: migrate statsByProvider into legacyFamily\n if (record.version === 2) {\n const statsByProvider = asRecord(record.statsByProvider);\n const legacyFamily: Partial<Record<CacheProviderId, CacheStats>> = {};\n if (statsByProvider) {\n for (const id of CACHE_PROVIDER_IDS) {\n const stats = parseCacheStats(statsByProvider[id]);\n if (stats) legacyFamily[id] = stats;\n }\n }\n return { statsByModel: {}, totalsByModel: {}, legacyFamily };\n }\n\n // version 1: single DeepSeek stats -> migrate to legacyFamily.deepseek\n if (record.version === 1) {\n const migrated = parseCacheStats(record.stats);\n return migrated ? { statsByModel: {}, totalsByModel: {}, legacyFamily: { deepseek: migrated } } : undefined;\n }\n\n return undefined;\n}\n\nasync function readPersistedCacheStats(): Promise<CacheStatsState | undefined> {\n try {\n const raw = await readFile(STATE_FILE_PATH, \"utf8\");\n return parsePersistedCacheStats(JSON.parse(raw));\n } catch (error) {\n if (getErrorCode(error) !== \"ENOENT\") {\n console.warn(`${LOG_PREFIX}: failed to read persisted cache stats`, error);\n return undefined;\n }\n }\n\n // New path missing: try one-shot migration from the old (pre-rename) path.\n try {\n const raw = await readFile(LEGACY_STATE_FILE_PATH, \"utf8\");\n const parsed = parsePersistedCacheStats(JSON.parse(raw));\n if (parsed) {\n try {\n await writePersistedCacheStats(parsed);\n // Best-effort delete; if the unlink fails the new path is still authoritative.\n try {\n await unlink(LEGACY_STATE_FILE_PATH);\n } catch (unlinkError) {\n if (getErrorCode(unlinkError) !== \"ENOENT\") {\n console.warn(`${LOG_PREFIX}: failed to remove legacy stats file`, unlinkError);\n }\n }\n } catch (writeError) {\n console.warn(`${LOG_PREFIX}: failed to migrate legacy cache stats`, writeError);\n }\n return parsed;\n }\n } catch (error) {\n if (getErrorCode(error) !== \"ENOENT\") {\n console.warn(`${LOG_PREFIX}: failed to read legacy cache stats`, error);\n }\n }\n\n return undefined;\n}\n\nfunction filterRestorableStatsForSession(\n persisted: CacheStatsState | undefined,\n currentSessionHash?: string,\n): Record<string, CacheStats> {\n if (!persisted || !currentSessionHash) return {};\n\n const prefix = `${currentSessionHash}:`;\n const filteredModelStats: Record<string, CacheStats> = {};\n for (const [fullKey, stats] of Object.entries(persisted.statsByModel)) {\n if (fullKey.startsWith(prefix)) {\n filteredModelStats[fullKey] = stats;\n } else if (!fullKey.includes(\":\")) {\n // Legacy v3-style key without session hash — migrate to current session.\n filteredModelStats[`${currentSessionHash}:${fullKey}`] = stats;\n } else if (fullKey.startsWith(\"_nosession:\")) {\n // Transitional _nosession bucket — migrate to current session.\n filteredModelStats[`${currentSessionHash}:${fullKey.slice(\"_nosession:\".length)}`] = stats;\n }\n }\n\n return filteredModelStats;\n}\n\n/**\n * The closure-internal writer. Since the closure has access to currentSessionHash,\n * it passes the hash and statsByModel here. This function wraps them in the v4\n * sessions format, combining with any previously-persisted sessions for safety.\n *\n * When called from the closure, `state.statsByModel` contains only the current\n * session's entries (keyed by `${sessionHash}:${provider}/${id}`). We extract\n * the model-key-only entries and store them under the session hash.\n */\n/**\n * Merge in-memory stats state into an existing sessions map for persistence.\n *\n * When `currentSessionHash` is provided (explicit hash mode):\n * - Current-session entries are extracted from `state.statsByModel` (keys\n * prefixed with `currentSessionHash:`) and written under the session hash.\n * - The transitional legacy `_nosession` bucket is DELETED — its entries\n * were already consumed and migrated into memory by `restoreCacheStats`.\n * Keeping `_nosession` on disk would allow resurrection of reset stats\n * on the next reload (the reset-undo bug).\n * - Other real session hashes are preserved intact.\n *\n * When `currentSessionHash` is undefined (no-hash mode):\n * - Keys with a hash prefix (`hash:provider/model`) are grouped under their\n * respective session hashes.\n * - Keys without a hash prefix (legacy v3) are grouped under `_nosession` so\n * `restoreCacheStats` can migrate them on the next load before the session\n * id is known.\n *\n * Pure function (no I/O) — suitable for unit tests without touching the real\n * state file at `~/.pi/agent/pi-cache-optimizer-stats.json`.\n */\nfunction mergeCacheSessions(\n existingSessions: Record<string, Record<string, CacheStats>>,\n state: CacheStatsState,\n currentSessionHash?: string,\n): Record<string, Record<string, CacheStats>> {\n // Deep-copy to avoid mutating the caller's object.\n const sessions: Record<string, Record<string, CacheStats>> = {};\n for (const [hash, models] of Object.entries(existingSessions)) {\n sessions[hash] = { ...models };\n }\n\n if (currentSessionHash !== undefined) {\n // Explicit hash mode: extract this session's data from state.statsByModel.\n // When the session has no entries (e.g. after reset of sole bucket), this\n // still sets an empty map, ensuring the deleted bucket does not return.\n const prefix = `${currentSessionHash}:`;\n const currentModelStats: Record<string, CacheStats> = {};\n for (const [fullKey, stats] of Object.entries(state.statsByModel)) {\n if (fullKey.startsWith(prefix)) {\n currentModelStats[fullKey.slice(prefix.length)] = stats;\n }\n }\n sessions[currentSessionHash] = currentModelStats;\n\n // _nosession is a transitional legacy migration bucket — once we write\n // under an authoritative session hash, those entries have already been\n // consumed and migrated into memory by restoreCacheStats. Delete to\n // prevent resurrection of reset stats on the next reload.\n delete sessions[\"_nosession\"];\n } else {\n // No-hash mode: group entries by their existing hash prefix to avoid\n // collapsing multiple sessions into one bucket. Keys without a hash\n // prefix (legacy v3) go under \"_nosession\" so restoreCacheStats can\n // migrate them to the current session on next load.\n const nosessionMap: Record<string, CacheStats> = {};\n for (const [fullKey, stats] of Object.entries(state.statsByModel)) {\n const idx = fullKey.indexOf(\":\");\n if (idx >= 0) {\n const hash = fullKey.slice(0, idx);\n const modelKey = fullKey.slice(idx + 1);\n if (!sessions[hash]) sessions[hash] = {};\n sessions[hash][modelKey] = stats;\n } else {\n // Key without hash prefix (legacy v3) — group under _nosession.\n nosessionMap[fullKey] = stats;\n }\n }\n if (Object.keys(nosessionMap).length > 0) {\n sessions[\"_nosession\"] = nosessionMap;\n }\n }\n\n return sessions;\n}\n\nfunction createSerializedAsyncRunner(): <T>(operation: () => Promise<T>) => Promise<T> {\n let tail: Promise<unknown> = Promise.resolve();\n return <T>(operation: () => Promise<T>): Promise<T> => {\n const result = tail.then(operation);\n tail = result.catch(() => undefined);\n return result;\n };\n}\n\nfunction mergeCacheTotals(\n existingTotalsByModel: Record<string, CacheStats>,\n stateTotalsByModel: Record<string, CacheStats>,\n options: { deleteModelKeys?: string[]; replaceTotals?: boolean } = {},\n): Record<string, CacheStats> {\n const totals = options.replaceTotals\n ? { ...stateTotalsByModel }\n : { ...existingTotalsByModel, ...stateTotalsByModel };\n for (const key of options.deleteModelKeys ?? []) {\n delete totals[key];\n }\n return totals;\n}\n\nfunction mergeLastRoutedModels(\n existingLastRoutedModelBySession: Record<string, PersistedRoutedModelRef>,\n state: CacheStatsState,\n currentSessionHash?: string,\n): Record<string, PersistedRoutedModelRef> {\n const merged: Record<string, PersistedRoutedModelRef> = { ...existingLastRoutedModelBySession };\n const incoming = state.lastRoutedModelBySession ?? {};\n\n if (currentSessionHash !== undefined) {\n const current = incoming[currentSessionHash];\n if (current) {\n merged[currentSessionHash] = current;\n } else {\n // Explicit deletion: when incoming state has no entry for current session,\n // remove any existing stale entry to reflect intentional reset.\n delete merged[currentSessionHash];\n }\n return merged;\n }\n\n for (const [sessionHash, ref] of Object.entries(incoming)) {\n merged[sessionHash] = ref;\n }\n return merged;\n}\n\nasync function writePersistedCacheStats(\n state: CacheStatsState,\n currentSessionHash?: string,\n options: { deleteModelKeys?: string[]; replaceTotals?: boolean } = {},\n): Promise<void> {\n await mkdir(STATE_DIR, { recursive: true });\n\n // Read existing file to preserve other sessions' data.\n let existingSessions: Record<string, Record<string, CacheStats>> = {};\n let existingTotalsByModel: Record<string, CacheStats> = {};\n let existingLastRoutedModelBySession: Record<string, PersistedRoutedModelRef> = {};\n try {\n const raw = await readFile(STATE_FILE_PATH, \"utf8\");\n const parsed = parsePersistedCacheStats(JSON.parse(raw));\n if (parsed) {\n // Reconstruct sessions from statsByModel keys.\n // Each key has form `${hash}:${provider}/${id}`; group by hash.\n for (const [fullKey, stats] of Object.entries(parsed.statsByModel)) {\n const idx = fullKey.indexOf(\":\");\n if (idx >= 0) {\n const hash = fullKey.slice(0, idx);\n const modelKey = fullKey.slice(idx + 1);\n if (!existingSessions[hash]) existingSessions[hash] = {};\n existingSessions[hash][modelKey] = stats;\n }\n }\n existingTotalsByModel = { ...(parsed.totalsByModel ?? {}) };\n existingLastRoutedModelBySession = { ...(parsed.lastRoutedModelBySession ?? {}) };\n }\n } catch {\n // Ignore read errors (file may not exist yet).\n }\n\n const sessions = mergeCacheSessions(existingSessions, state, currentSessionHash);\n const totalsByModel = mergeCacheTotals(existingTotalsByModel, state.totalsByModel, options);\n const lastRoutedModelBySession = mergeLastRoutedModels(\n existingLastRoutedModelBySession,\n state,\n currentSessionHash,\n );\n\n const payload: PersistedCacheStatsV6 = {\n version: 6,\n sessions,\n totalsByModel,\n legacyFamily: state.legacyFamily,\n ...(Object.keys(lastRoutedModelBySession).length > 0 ? { lastRoutedModelBySession } : {}),\n };\n const tempPath = `${STATE_FILE_PATH}.${process.pid}.${Date.now()}.tmp`;\n\n await writeFile(tempPath, JSON.stringify(payload, null, 2) + \"\\n\", \"utf8\");\n await rename(tempPath, STATE_FILE_PATH);\n}\n\n\n\nfunction isCompatCheckApplicable(model: PiModel): boolean {\n return isOpenAICompatibleProxyApi(model.api) && !isOfficialOpenAIBaseUrl(model) && !isPiBuiltInLlamaCppModel(model);\n}\n\nfunction isPromptCacheRetention400Applicable(model: PiModel): boolean {\n return isOpenAICompatibleApi(model.api) &&\n !isOfficialOpenAIBaseUrl(model) &&\n !isPiBuiltInLlamaCppModel(model) &&\n getCompat(model).supportsLongCacheRetention === true;\n}\n\nfunction isExplicitPromptCacheRetentionUnsupportedApplicable(model: PiModel): boolean {\n // A finalized assistant error with an explicit unsupported-parameter signal\n // proves that prompt_cache_retention reached this provider/model. Do not\n // require compat inherited from the active fallback model: router shells may\n // have no upstream compat metadata when no live routing registry is present.\n return isOpenAICompatibleApi(model.api) &&\n !isOfficialOpenAIBaseUrl(model) &&\n !isPiBuiltInLlamaCppModel(model);\n}\n\n/**\n * Whether the 403 sendSessionAffinityHeaders diagnostic applies to a model.\n *\n * Pi's openai-completions adapter sends custom HTTP headers (session_id,\n * x-client-request-id, x-session-affinity) when `sendSessionAffinityHeaders`\n * is enabled. Some third-party proxies / CDNs / WAFs block these headers and\n * return HTTP 403 \"Your request was blocked\". Pi 0.80.7+ no longer uses this\n * flag for openai-responses; Responses header shape is controlled by\n * `sessionAffinityFormat`. This guard therefore applies only to\n * openai-completions, where doctor/fix can safely advise disabling the flag.\n */\nfunction isSessionAffinity403Applicable(model: PiModel): boolean {\n if (!isOpenAICompatibleProxyApi(model.api)) return false;\n if (isPiBuiltInLlamaCppModel(model)) return false;\n return getCompat(model).sendSessionAffinityHeaders === true;\n}\n\n/**\n * Whether an OpenAI SDK header/User-Agent 403 diagnostic applies.\n *\n * Some third-party OpenAI-compatible proxies/CDNs/WAFs allow a plain curl\n * request but block the OpenAI JS SDK's default request fingerprint, especially\n * its `User-Agent: OpenAI/JS ...` and `X-Stainless-*` headers. This is distinct\n * from session-affinity header blocking: it applies when session-affinity\n * headers are NOT enabled but the model still uses a non-official\n * OpenAI-compatible proxy transport.\n */\nfunction isOpenAISdkHeader403Applicable(model: PiModel): boolean {\n if (!isOpenAICompatibleProxyApi(model.api)) return false;\n if (isOfficialOpenAIBaseUrl(model)) return false;\n if (isPiBuiltInLlamaCppModel(model)) return false;\n return getCompat(model).sendSessionAffinityHeaders !== true;\n}\n\n/**\n * Detect router / channel profiles from a PiModel and return diagnostic notes.\n *\n * This function is advisory only — it does NOT participate in adapter selection,\n * prompt_cache_key injection, or footer stats. It inspects provider, api, baseUrl,\n * and compat to identify common proxy/router patterns where cache performance may\n * be degraded due to multi-backend routing.\n *\n * Known profiles (checked in order):\n * 1. OpenRouter — baseUrl or provider id matching openrouter.ai / openrouter\n * 2. Vercel AI Gateway — baseUrl matching ai-gateway.vercel.sh, or provider\n * matching vercel / vercel-ai-gateway\n * 3. LiteLLM / OneAPI / NewAPI / VoAPI — baseUrl or provider matching litellm,\n * oneapi, one-api, newapi, new-api, voapi, vo-api (self-hosted aggregation)\n * 4. Generic third-party OpenAI-compatible proxy — any openai-completions model\n * with a non-official base URL that does not match a higher-profile above.\n *\n * Official OpenAI (api.openai.com) and custom transports (kiro-api, anthropic-messages,\n * bedrock-converse-stream) do NOT produce notes.\n */\nfunction describeRouterChannelDiagnostics(model: PiModel): string[] {\n const notes: string[] = [];\n const api = lower(model.api);\n const baseUrl = lower(model.baseUrl || \"\");\n const provider = lower(model.provider);\n\n // Router/channel diagnostics only apply to OpenAI-compatible proxy APIs.\n // Native APIs like mistral-conversations, azure-openai-responses,\n // anthropic-messages, or bedrock-converse-stream are intentionally excluded.\n if (api === \"azure-openai-responses\" || isMistralConversationsApi(api) || !isOpenAICompatibleApi(api)) {\n return notes;\n }\n\n // Official OpenAI bypass — no notes needed.\n if (isOfficialOpenAIBaseUrl(model)) {\n return notes;\n }\n\n // Pi 0.81+ built-in llama.cpp uses an OpenAI-shaped transport, but its\n // untouched explicit compat fingerprint does not expose proxy-routing or\n // session-affinity configuration. Same-id overrides are not exempt.\n if (isPiBuiltInLlamaCppModel(model)) {\n return notes;\n }\n\n // ── 1. OpenRouter ────────────────────────────────────────────────\n if (\n baseUrl.includes(\"openrouter.ai\") ||\n baseUrl.includes(\"openrouter\") ||\n provider.includes(\"openrouter\")\n ) {\n const compat = getCompat(model);\n const routing = asRecord((compat as Record<string, unknown>)[\"openRouterRouting\"]);\n const hasOnly = !!routing?.only;\n const hasOrder = !!routing?.order;\n\n notes.push(\n \"🔀 Router/channel: OpenRouter detected. OpenRouter is a multi-provider router; \" +\n \"low cache hit rates are common when each turn lands on a different upstream provider.\",\n );\n\n if (!hasOnly && !hasOrder) {\n notes.push(\n \" Suggestion: Add an openRouterRouting config to fix the upstream provider. \" +\n \"Example for models.json -> providers[\\\"<providerId>\\\"] -> compat:\",\n );\n notes.push(\n ` { \"sendSessionAffinityHeaders\": true, \"supportsLongCacheRetention\": true, ` +\n `\"openRouterRouting\": { \"only\": [\"<provider-slug>\"] } }`,\n );\n notes.push(\n ' Replace <provider-slug> with the actual OpenRouter provider slug (e.g. \"openai\", \"anthropic\").',\n );\n notes.push(\n \" Alternatively, use openRouterRouting.order: [\\\"<provider-slug>\\\", \\\"...\\\"] for fallback order. \" +\n \"Only set supportsLongCacheRetention if your upstream supports long cache retention.\",\n );\n }\n\n return notes;\n }\n\n // ── 2. Vercel AI Gateway ─────────────────────────────────────────\n if (\n baseUrl.includes(\"ai-gateway.vercel.sh\") ||\n provider.includes(\"vercel\") ||\n provider.includes(\"vercel-ai-gateway\")\n ) {\n const compat = getCompat(model);\n const routing = asRecord((compat as Record<string, unknown>)[\"vercelGatewayRouting\"]);\n const hasOnly = !!routing?.only;\n const hasOrder = !!routing?.order;\n\n notes.push(\n \"🔀 Router/channel: Vercel AI Gateway detected. The gateway may route to different \" +\n \"provider endpoints per request, reducing cache locality.\",\n );\n\n if (!hasOnly && !hasOrder) {\n notes.push(\n \" Suggestion: Add a vercelGatewayRouting config to fix the upstream. \" +\n \"Example for models.json -> providers[\\\"<providerId>\\\"] -> compat:\",\n );\n notes.push(\n ` { \"sendSessionAffinityHeaders\": true, \"supportsLongCacheRetention\": true, ` +\n `\"vercelGatewayRouting\": { \"only\": [\"<provider-id>\"] } }`,\n );\n notes.push(\n \" Replace <provider-id> with the actual Vercel provider ID (e.g. \\\"openai\\\").\",\n );\n notes.push(\n \" Only set supportsLongCacheRetention if your upstream supports it.\",\n );\n }\n\n return notes;\n }\n\n // ── 3. LiteLLM / OneAPI / NewAPI / VoAPI (self-hosted aggregation) ──\n const aggregationPatterns = [\"litellm\", \"oneapi\", \"one-api\", \"newapi\", \"new-api\", \"voapi\", \"vo-api\"];\n if (\n aggregationPatterns.some((p) => baseUrl.includes(p)) ||\n aggregationPatterns.some((p) => provider.includes(p))\n ) {\n notes.push(\n \"🔀 Router/channel: Self-hosted aggregation proxy detected (LiteLLM / OneAPI / NewAPI / VoAPI). \" +\n \"These proxies route to multiple upstream accounts or instances, which can split the cache.\",\n );\n notes.push(\n \" Suggestions:\",\n );\n notes.push(\n \" • Ensure the proxy can fix to a single upstream per session (session_id affinity).\",\n );\n notes.push(\n \" • Forward prompt_cache_key and session-affinity headers to the upstream.\",\n );\n notes.push(\n \" • Return cache usage fields (prompt_cache_hit_tokens, etc.) in the response.\",\n );\n notes.push(\n ` Safe compat default: { \"sendSessionAffinityHeaders\": true }`,\n );\n notes.push(\n ` Add supportsLongCacheRetention only if the proxy explicitly supports prompt_cache_retention.`,\n );\n\n return notes;\n }\n\n // ── 4. Generic third-party OpenAI-compatible proxy ─────────────────\n if (api === \"openai-completions\" && baseUrl) {\n const missing = describeMissingCacheCompatForModel(model);\n notes.push(\n \"🔀 Router/channel: Third-party OpenAI-compatible proxy. If cache hit rates are low:\",\n );\n notes.push(\n \" • Verify the proxy routes to the same upstream account/instance per session.\",\n );\n notes.push(\n \" • Ensure the proxy forwards prompt_cache_key and sends session-affinity headers.\",\n );\n notes.push(\n \" • Check that the proxy returns cache usage fields (prompt_cache_hit_tokens etc.).\",\n );\n if (missing.length > 0) {\n notes.push(\n ` • The compat flags above (${missing.join(\", \")}) are recommended for cache stability.`,\n );\n }\n\n return notes;\n }\n\n return notes;\n}\n\nfunction getCompatCheckNotApplicableLines(model: PiModel): string[] {\n const api = lower(model.api);\n\n if (isMistralConversationsApi(api)) {\n return [\n \"ℹ️ Compat check not applicable for this model.\",\n \" Native Mistral `mistral-conversations` uses provider-native transport; OpenAI-compatible proxy compat flags do not apply.\",\n ];\n }\n\n if (api === \"azure-openai-responses\") {\n return [\n \"ℹ️ Compat check not applicable for this model.\",\n \" Native Azure OpenAI Responses uses the Responses transport; OpenAI-compatible proxy compat flags do not apply.\",\n ];\n }\n\n if (api === \"openai-codex-responses\" || (api === \"openai-responses\" && isOfficialOpenAIBaseUrl(model))) {\n return [\n \"ℹ️ Compat check not applicable for this model.\",\n \" Native Responses transports already use Pi core request handling; OpenAI-compatible proxy compat flags do not apply.\",\n ];\n }\n\n return [\"ℹ️ Compat check not applicable for this model.\"];\n}\n\nfunction buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400?: boolean; anthropicTtlOrderError?: boolean; sessionAffinity403?: boolean; openAISdkHeader403?: boolean } = {}): string {\n const lines: string[] = [];\n lines.push(`Provider: ${model.provider}`);\n lines.push(`Model: ${model.id}`);\n if (model.name && model.name !== model.id) lines.push(`Name: ${model.name}`);\n lines.push(`API: ${model.api}`);\n lines.push(`Base URL: ${model.baseUrl || \"(default)\"}`);\n\n const compat = getCompat(model);\n lines.push(`Compat: ${JSON.stringify(compat)}`);\n\n const adaptiveThinkingApplicable = isAdaptiveThinkingCompatApplicable(model);\n const deepSeekCompatApplicable = isDeepSeekCompatCheckApplicable(model);\n const missing = describeMissingCacheCompatForModel(model);\n const optionalOpenAIProxyCompat = (!adaptiveThinkingApplicable && !deepSeekCompatApplicable)\n ? describeOptionalOpenAICompatibleProxyCompat(model)\n : [];\n const fixSug = buildFixSuggestion(model);\n const safeFixableMissing = fixSug ? Object.keys(fixSug.compatKeys) : [];\n const advisoryMissing = missing.filter(m => !safeFixableMissing.includes(m));\n\n if (safeFixableMissing.length > 0) {\n lines.push(`⚠️ Missing compat flags: ${safeFixableMissing.join(\", \")}`);\n }\n if (advisoryMissing.length > 0) {\n lines.push(`ℹ️ Optional: ${advisoryMissing.join(\", \")} (enable only if needed)`);\n }\n\n if (missing.length > 0) {\n const key = modelKey(model);\n const slashIdx = key.indexOf(\"/\");\n const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;\n const modelsJsonPath = getModelsJsonDisplayPath();\n lines.push(`Edit ${modelsJsonPath} -> providers[\"${providerLabel}\"] -> compat (same level as baseUrl/api/apiKey/models).`);\n if (adaptiveThinkingApplicable) {\n appendAdaptiveThinkingCompatAdviceLines(lines, missing, { providerLabel, modelId: model.id });\n } else if (deepSeekCompatApplicable) {\n appendDeepSeekCompatAdviceLines(lines, missing, { providerLabel, modelId: model.id });\n } else {\n appendOpenAIProxyCompatAdviceLines(lines, missing, { providerLabel, modelId: model.id });\n appendOptionalOpenAIProxyCompatAdviceLines(lines, optionalOpenAIProxyCompat);\n }\n } else if (adaptiveThinkingApplicable || deepSeekCompatApplicable || isCompatCheckApplicable(model)) {\n lines.push(\"✅ Compat fully configured.\");\n appendOptionalOpenAIProxyCompatAdviceLines(lines, optionalOpenAIProxyCompat);\n } else {\n lines.push(...getCompatCheckNotApplicableLines(model));\n }\n\n if (isPromptCacheRetention400Applicable(model)) {\n lines.push(\"\");\n if (options.promptCacheRetention400) {\n lines.push(\"⚠️ A 400 response was observed while supportsLongCacheRetention is enabled.\");\n lines.push(` ${getPromptCacheRetentionUnsupportedHint()}`);\n } else {\n lines.push(`ℹ️ Long retention is enabled. ${getPromptCacheRetentionUnsupportedHint()}`);\n }\n }\n\n if (options.anthropicTtlOrderError) {\n lines.push(\"\");\n lines.push(\"⚠️ An Anthropic cache-control TTL ordering error was observed for this model.\");\n lines.push(\" Runtime requests now fall back from 1h to the default 5-minute cache TTL.\");\n lines.push(` Run /cache-optimizer fix to set model-level supportsLongCacheRetention: false in ${getModelsJsonDisplayPath()}.`);\n }\n\n // ── Session affinity 403 diagnostics ──\n // Advises the user when sendSessionAffinityHeaders is enabled, since some\n // third-party proxies / CDNs / WAFs block Pi's custom session-affinity HTTP\n // headers (session_id, x-client-request-id, x-session-affinity) and return\n // 403. If a 403 was already observed for this model, show a stronger hint.\n if (isSessionAffinity403Applicable(model)) {\n lines.push(\"\");\n if (options.sessionAffinity403) {\n lines.push(\"⚠️ A 403 response was observed while sendSessionAffinityHeaders is enabled.\");\n lines.push(\" The proxy/CDN likely blocks Pi's custom session-affinity headers (session_id,\");\n lines.push(\" x-client-request-id, x-session-affinity). Run /cache-optimizer fix to\");\n lines.push(` set sendSessionAffinityHeaders: false in ${getModelsJsonDisplayPath()}.`);\n } else {\n lines.push(\"ℹ️ Session affinity headers are enabled. Some CDNs/WAFs block custom headers\");\n lines.push(\" (session_id, x-client-request-id, x-session-affinity) and return 403. If you\");\n lines.push(\" see 403 errors, run /cache-optimizer fix to set sendSessionAffinityHeaders: false.\");\n }\n } else if (isOpenAISdkHeader403Applicable(model)) {\n lines.push(\"\");\n if (options.openAISdkHeader403) {\n lines.push(\"⚠️ A 403 response was observed while sendSessionAffinityHeaders is not enabled.\");\n lines.push(\" The proxy/CDN may be blocking the OpenAI JS SDK request fingerprint\");\n lines.push(\" (for example User-Agent: OpenAI/JS ... or X-Stainless-* headers). This\");\n lines.push(\" is provider/WAF-specific; /cache-optimizer fix will not auto-write headers.\");\n lines.push(` Manual workaround: add a provider-level headers.User-Agent override in ${getModelsJsonDisplayPath()}`);\n lines.push(\" only after testing the value with the affected provider.\");\n } else {\n lines.push(\"ℹ️ If 403 persists after disabling sendSessionAffinityHeaders, some CDNs/WAFs\");\n lines.push(\" may block the OpenAI JS SDK User-Agent / X-Stainless-* headers. Test the\");\n lines.push(\" provider manually before adding a provider-level headers.User-Agent override.\");\n }\n }\n\n // ── Router/channel diagnostics ──\n const routerNotes = describeRouterChannelDiagnostics(model);\n if (routerNotes.length > 0) {\n lines.push(\"\");\n for (const note of routerNotes) {\n lines.push(note);\n }\n }\n\n // ── Integrity diagnostics ──\n if (lastPromptIntegrityWarningAt > 0) {\n const ago = Date.now() - lastPromptIntegrityWarningAt;\n const mins = Math.floor(ago / 60000);\n if (mins < 5) {\n lines.push(\"\");\n lines.push(\"⚠️ Recent prompt integrity issue detected:\");\n lines.push(` Last detected ${mins > 0 ? `${mins} min` : `${Math.floor(ago / 1000)}s`} ago. The prompt reorder was`);\n lines.push(` skipped on that turn to preserve structural markers.`);\n lines.push(` Common causes: extension system prompt format change, substring collision.`);\n lines.push(` Steps:`);\n lines.push(` 1. Run /reload to reset (may clear transient issues).`);\n lines.push(` 2. Set PI_CACHE_OPTIMIZER_NO_PROMPT_REWRITE=1 & /reload to disable reorder.`);\n lines.push(` 3. If persistent, file an issue with this doctor output.`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Build a \"Cache diagnosis\" section for low-hit causes, appended to doctor output.\n * This is a separate function because it depends on per-session state (recent samples,\n * per-model stats) that is not available at the module level.\n */\nfunction buildLowHitDiagnosis(\n model: PiModel,\n adapter: CacheProviderAdapter | undefined,\n stats: CacheStats | undefined,\n samples: CacheUsageSample[],\n): string[] {\n const lines: string[] = [];\n\n // 1. Missing compat flags (adapter-aware: DeepSeek has extra reasoning compat)\n const fixSugLHD = buildFixSuggestion(model);\n const safeFixableMissingLHD = fixSugLHD ? Object.keys(fixSugLHD.compatKeys) : [];\n\n // 2. Router/channel risk (reuse existing check)\n const routerNotes = describeRouterChannelDiagnostics(model);\n\n // 3. Recent samples missing usage fields\n const missingUsageSamples = samples.filter((s) => s.missingUsageFields).length;\n\n // 4. Recent trend analysis\n const recent10 = samples.slice(-10);\n const recent10Hits = recent10.filter((s) => s.hit).length;\n const recent10Total = recent10.length;\n const recent10Cached = recent10.reduce((sum, s) => sum + s.cachedInputTokens, 0);\n const recent10Input = recent10.reduce((sum, s) => sum + s.totalInputTokens, 0);\n\n // 5. Today's overall trend from persisted stats\n const todayStats = stats ?? emptyCacheStats();\n\n const hasMissingCompat = safeFixableMissingLHD.length > 0;\n const hasRouterRisk = routerNotes.length > 0;\n const hasUsageMissing = missingUsageSamples > 0;\n\n // Today's cached-token ratio is used both inside and outside the recent-sample\n // branch. Keep it block-external so doctor/stats never throw for low-hit\n // models that have persisted counters but no recent in-memory samples.\n const todayHitRatio = todayStats.totalInputTokens > 0\n ? Math.round((todayStats.cachedInputTokens / todayStats.totalInputTokens) * 100)\n : 0;\n\n // Determine if there are actual issues worth flagging\n const hasActualIssues = hasMissingCompat || hasUsageMissing ||\n // Low hit trend (today total > 3 and hit ratio < 30%)\n (todayStats.totalRequests > 3 && todayStats.totalInputTokens > 0 &&\n (todayStats.cachedInputTokens / todayStats.totalInputTokens) < 0.3) ||\n // Low hit rate in recent samples (recent10Total >= 3 and all misses)\n (recent10Total >= 3 && recent10Hits === 0);\n\n // Skip section if no issues\n if (!hasActualIssues && !(hasRouterRisk && (hasMissingCompat || hasUsageMissing))) {\n return lines;\n }\n\n lines.push(\"\");\n lines.push(\"── Cache diagnosis ──\");\n\n // Priority 1: missing compat flags\n if (hasMissingCompat) {\n lines.push(`⚠️ Missing compat flags: ${safeFixableMissingLHD.join(\", \")}`);\n lines.push(\" These flags enable prompt caching and session-affinity routing.\");\n lines.push(\" Run /cache-optimizer compat for edit instructions.\");\n }\n\n // Priority 2: router/channel risk (only flag when there are other issues)\n // Router notes are already shown in the main doctor output, so we only\n // mention them in the diagnosis section when they compound a problem.\n if (hasRouterRisk && (hasMissingCompat || hasUsageMissing || hasActualIssues)) {\n lines.push(\"🔀 Router/channel proxy detected — see routing notes above.\");\n }\n\n // Priority 3: usage fields missing\n if (hasUsageMissing) {\n lines.push(`⚠️ ${missingUsageSamples}/${samples.length} recent responses had missing/empty usage fields.`);\n lines.push(\" Footer may under-report cache hit rate.\");\n lines.push(\" Verify the proxy returns prompt-level usage (prompt_tokens, input_tokens_details).\");\n }\n\n // Priority 4: recent trend low\n if (recent10Total > 0) {\n const hitRatio = recent10Input > 0 ? Math.round((recent10Cached / recent10Input) * 100) : 0;\n if (recent10Hits === 0 && todayStats.totalRequests > 3 && todayHitRatio < 30) {\n lines.push(`📉 Cache hit rate is low: ${todayHitRatio}% today (${recent10Total} recent samples).`);\n lines.push(\" Likely causes: proxy routing to different backends per request,\");\n lines.push(\" or prompt prefix changes across turns.\");\n lines.push(\" Verify session affinity (sendSessionAffinityHeaders) and long cache retention.\");\n } else if (todayHitRatio < 30 && todayStats.totalRequests > 3) {\n lines.push(`📉 Cache hit rate is low: ${todayHitRatio}% today (${todayStats.totalRequests} total requests).`);\n lines.push(\" Check compat flags and proxy upstream routing.\");\n }\n\n // Show brief trend summary if there are enough samples\n if (recent10Total >= 3) {\n const trend = formatRecentTrendSummary(samples, 10);\n lines.push(`📊 ${trend}`);\n }\n }\n\n // For fully configured but low hit models, emphasize sticky routing\n if (!hasMissingCompat && !hasRouterRisk && todayStats.totalRequests > 3 && todayHitRatio < 30) {\n lines.push(\"💡 Compat is configured but cache hit rate remains low.\");\n lines.push(\" Possible causes:\");\n lines.push(\" • Proxy still routes to multiple backends — check session affinity on the proxy side.\");\n lines.push(\" • Prompt prefix varies per turn — check dynamic context in system prompt.\");\n lines.push(\" • Provider does not return cache usage fields — footer can't measure hits.\");\n }\n\n return lines;\n}\n\nfunction buildCompatDiagnosis(model: PiModel): string | undefined {\n const missing = describeMissingCacheCompatForModel(model);\n const fixSugC = buildFixSuggestion(model);\n const safeFixableMissingC = fixSugC ? Object.keys(fixSugC.compatKeys) : [];\n const advisoryMissingC = missing.filter(m => !safeFixableMissingC.includes(m));\n const adaptiveThinkingApplicable = isAdaptiveThinkingCompatApplicable(model);\n const deepSeekCompatApplicable = isDeepSeekCompatCheckApplicable(model);\n const optionalOpenAIProxyCompat = (!adaptiveThinkingApplicable && !deepSeekCompatApplicable)\n ? describeOptionalOpenAICompatibleProxyCompat(model)\n : [];\n const routerNotes = describeRouterChannelDiagnostics(model);\n\n if (missing.length === 0 && routerNotes.length === 0 && optionalOpenAIProxyCompat.length === 0) return undefined;\n\n const key = modelKey(model);\n const lines: string[] = [];\n\n if (missing.length > 0) {\n const slashIdx = key.indexOf(\"/\");\n const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;\n const modelsJsonPath = getModelsJsonDisplayPath();\n lines.push(`Active model: ${key}`);\n if (safeFixableMissingC.length > 0) {\n lines.push(`Safe-fixable: ${safeFixableMissingC.join(\", \")}`);\n }\n if (advisoryMissingC.length > 0) {\n lines.push(`Optional: ${advisoryMissingC.join(\", \")} (enable only if needed)`);\n }\n lines.push(\"\");\n lines.push(`Edit ${modelsJsonPath} -> providers[\"${providerLabel}\"] -> compat`);\n lines.push(`(at the same level as baseUrl/api/apiKey/models).`);\n if (adaptiveThinkingApplicable) {\n appendAdaptiveThinkingCompatAdviceLines(lines, missing, { providerLabel, modelId: model.id });\n } else if (deepSeekCompatApplicable) {\n appendDeepSeekCompatAdviceLines(lines, missing, { providerLabel, modelId: model.id });\n } else {\n appendOpenAIProxyCompatAdviceLines(lines, missing, { providerLabel, modelId: model.id });\n appendOptionalOpenAIProxyCompatAdviceLines(lines, optionalOpenAIProxyCompat);\n }\n }\n\n // When compat is fully configured but router/optional notes exist, prefix the status.\n if ((routerNotes.length > 0 || optionalOpenAIProxyCompat.length > 0) && missing.length === 0) {\n if (adaptiveThinkingApplicable || deepSeekCompatApplicable || isCompatCheckApplicable(model)) {\n lines.push(\"✅ Compat fully configured.\");\n if (isPromptCacheRetention400Applicable(model)) {\n lines.push(getPromptCacheRetentionUnsupportedHint());\n }\n // Advisory for 403 session-affinity header blocking (only when enabled),\n // or OpenAI SDK header/User-Agent blocking after session affinity is disabled.\n if (isSessionAffinity403Applicable(model)) {\n lines.push(\n \"ℹ️ Session affinity headers are enabled. If you see 403 \\\"blocked\\\" errors,\",\n \" the proxy/CDN may be blocking Pi's custom headers. Set sendSessionAffinityHeaders: false.\",\n );\n } else if (isOpenAISdkHeader403Applicable(model)) {\n lines.push(\n \"ℹ️ If 403 persists with sendSessionAffinityHeaders disabled, the proxy/CDN may\",\n \" be blocking the OpenAI JS SDK User-Agent / X-Stainless-* headers. Test a\",\n \" provider-level headers.User-Agent override manually; /fix does not auto-write it.\",\n );\n }\n appendOptionalOpenAIProxyCompatAdviceLines(lines, optionalOpenAIProxyCompat);\n } else {\n lines.push(...getCompatCheckNotApplicableLines(model));\n }\n lines.push(\"\");\n }\n\n if (routerNotes.length > 0) {\n if (missing.length > 0) lines.push(\"\");\n for (const note of routerNotes) {\n lines.push(note);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n// ============================================================\n// JSONC comment-preserving surgical edit helpers for /cache-optimizer fix\n// ============================================================\n\n/** The real models.json path used for I/O. */\nconst MODELS_JSON_PATH = join(STATE_DIR, \"models.json\");\n\n// ── String-aware JSONC scanning primitives ─────────────────────────\n//\n// These operate on comment-stripped text produced by stripJsoncComments()\n// (which preserves byte offsets), so every offset they return is also valid\n// in the original text. All scanning skips string literals, so braces or\n// brackets inside string values (e.g. apiKeyCommand shell snippets) cannot\n// corrupt depth tracking.\n\nfunction isJsonWhitespace(ch: string): boolean {\n return ch === \" \" || ch === \"\\n\" || ch === \"\\r\" || ch === \"\\t\";\n}\n\nfunction skipJsonWhitespace(text: string, pos: number): number {\n while (pos < text.length && isJsonWhitespace(text[pos])) pos++;\n return pos;\n}\n\n/**\n * Read a JSON string literal starting at `pos` (which must be `\"`).\n * Returns the decoded value and the offset just past the closing quote,\n * or undefined when the literal is unterminated/malformed.\n */\nfunction readJsonStringLiteral(text: string, pos: number): { value: string; end: number } | undefined {\n if (text[pos] !== '\"') return undefined;\n let i = pos + 1;\n let value = \"\";\n while (i < text.length) {\n const ch = text[i];\n if (ch === \"\\\\\") {\n const next = text[i + 1];\n if (next === undefined) return undefined;\n if (next === \"u\") {\n const hex = text.slice(i + 2, i + 6);\n if (!/^[0-9a-fA-F]{4}$/.test(hex)) return undefined;\n value += String.fromCharCode(parseInt(hex, 16));\n i += 6;\n } else {\n if (next === \"n\") value += \"\\n\";\n else if (next === \"t\") value += \"\\t\";\n else if (next === \"r\") value += \"\\r\";\n else if (next === \"b\") value += \"\\b\";\n else if (next === \"f\") value += \"\\f\";\n else value += next; // \", \\\\, / and lenient passthrough\n i += 2;\n }\n continue;\n }\n if (ch === '\"') return { value, end: i + 1 };\n value += ch;\n i++;\n }\n return undefined;\n}\n\n/**\n * Find the offset of the `}` / `]` matching the opener at `openPos`,\n * skipping string literals. Returns undefined on imbalance.\n */\nfunction findMatchingBracket(text: string, openPos: number): number | undefined {\n const open = text[openPos];\n if (open !== \"{\" && open !== \"[\") return undefined;\n let depth = 0;\n let i = openPos;\n while (i < text.length) {\n const ch = text[i];\n if (ch === '\"') {\n const str = readJsonStringLiteral(text, i);\n if (!str) return undefined;\n i = str.end;\n continue;\n }\n if (ch === \"{\" || ch === \"[\") depth++;\n else if (ch === \"}\" || ch === \"]\") {\n depth--;\n if (depth === 0) return i;\n }\n i++;\n }\n return undefined;\n}\n\n/** Skip one JSON value starting at/after `pos`; returns the offset just past it. */\nfunction skipJsonValue(text: string, pos: number): number | undefined {\n pos = skipJsonWhitespace(text, pos);\n const ch = text[pos];\n if (ch === '\"') {\n const str = readJsonStringLiteral(text, pos);\n return str?.end;\n }\n if (ch === \"{\" || ch === \"[\") {\n const end = findMatchingBracket(text, pos);\n return end === undefined ? undefined : end + 1;\n }\n let i = pos;\n while (i < text.length && !\",}]\".includes(text[i]) && !isJsonWhitespace(text[i])) i++;\n return i > pos ? i : undefined;\n}\n\n/**\n * Find a top-level key in the object whose `{` is at `openBracePos`.\n * Only direct children are considered (nested values are skipped whole).\n * Returns the key's opening-quote offset and its value's start offset,\n * or undefined when the key is absent or the object is malformed.\n */\nfunction findJsonObjectKey(\n text: string,\n openBracePos: number,\n targetKey: string,\n): { keyStart: number; valueStart: number } | undefined {\n if (text[openBracePos] !== \"{\") return undefined;\n let i = openBracePos + 1;\n while (i < text.length) {\n i = skipJsonWhitespace(text, i);\n if (i >= text.length || text[i] === \"}\") return undefined;\n if (text[i] === \",\") {\n i++;\n continue;\n }\n if (text[i] !== '\"') return undefined; // unexpected token — refuse to guess\n const keyStart = i;\n const key = readJsonStringLiteral(text, i);\n if (!key) return undefined;\n i = skipJsonWhitespace(text, key.end);\n if (text[i] !== \":\") return undefined;\n i = skipJsonWhitespace(text, i + 1);\n if (key.value === targetKey) return { keyStart, valueStart: i };\n const after = skipJsonValue(text, i);\n if (after === undefined) return undefined;\n i = after;\n }\n return undefined;\n}\n\n/** Leading whitespace of the line containing offset `pos` (up to `pos`). */\nfunction lineIndentOf(text: string, pos: number): string {\n let lineStart = text.lastIndexOf(\"\\n\", pos - 1);\n lineStart = lineStart < 0 ? 0 : lineStart + 1;\n const m = text.slice(lineStart, pos).match(/^[ \\t]*/);\n return m ? m[0] : \"\";\n}\n\n/**\n * Indentation used by the first line inside the object spanning\n * `openBrace`..`closeBrace` in the ORIGINAL text. Falls back to the\n * opener's line indent plus two spaces for single-line objects.\n */\nfunction deriveInnerIndent(text: string, openBrace: number, closeBrace: number): string {\n const nl = text.indexOf(\"\\n\", openBrace + 1);\n if (nl >= 0 && nl < closeBrace) {\n let i = nl + 1;\n let ws = \"\";\n while (i < text.length && (text[i] === \" \" || text[i] === \"\\t\")) {\n ws += text[i];\n i++;\n }\n if (ws.length > 0) return ws;\n }\n return lineIndentOf(text, openBrace) + \" \";\n}\n\ninterface FixSuggestion {\n providerLabel: string;\n modelId: string;\n compatKeys: Record<string, unknown>;\n /** Runtime-observed failures must never broaden a model-specific fix. */\n forceModelLevel?: boolean;\n}\n\n/**\n * Build the fix suggestion for the current active model.\n * Returns undefined if there is nothing to fix.\n */\nfunction buildFixSuggestion(model: PiModel): FixSuggestion | undefined {\n const missing = describeMissingCacheCompatForModel(model);\n if (missing.length === 0) return undefined;\n\n let compatKeys: Record<string, unknown> = {};\n\n if (isAdaptiveThinkingCompatApplicable(model)) {\n compatKeys = buildAdaptiveThinkingCompatSuggestion(missing);\n } else if (isDeepSeekCompatCheckApplicable(model)) {\n compatKeys = buildDeepSeekCompatSuggestion(missing);\n } else {\n compatKeys = buildSafeOpenAIProxyCompatSuggestion(missing);\n }\n\n if (Object.keys(compatKeys).length === 0) return undefined;\n\n const key = modelKey(model);\n const slashIdx = key.indexOf(\"/\");\n const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;\n\n return {\n providerLabel,\n modelId: model.id,\n compatKeys,\n };\n}\n\n/**\n * Strip JSONC comments from text, replacing them with spaces.\n * Handles string literals, escaped quotes, // line comments, /* block comments *\\/.\n * Returns the cleaned text with same line/column positions.\n */\nfunction stripJsoncComments(text: string): string {\n const out: string[] = [];\n let i = 0;\n while (i < text.length) {\n const ch = text[i];\n\n if (ch === '\"') {\n // String literal — copy byte-for-byte until the closing quote.\n // Escaped quotes/slashes must not be mistaken for comment delimiters.\n out.push(ch);\n i++;\n while (i < text.length) {\n const sc = text[i];\n out.push(sc);\n i++;\n if (sc === '\\\\' && i < text.length) {\n out.push(text[i]);\n i++;\n } else if (sc === '\"') {\n break;\n }\n }\n continue;\n }\n\n if (ch === '/' && i + 1 < text.length && text[i + 1] === '/') {\n // Line comment — replace BOTH slashes and every comment byte with\n // spaces, but leave the newline to be copied by the normal path.\n out.push(' ', ' ');\n i += 2;\n while (i < text.length && text[i] !== '\\n') {\n out.push(' ');\n i++;\n }\n continue;\n }\n\n if (ch === '/' && i + 1 < text.length && text[i + 1] === '*') {\n // Block comment — replace every byte with a space except newlines.\n // This deliberately preserves text.length and all structural offsets.\n out.push(' ', ' ');\n i += 2;\n while (i < text.length) {\n if (text[i] === '*' && i + 1 < text.length && text[i + 1] === '/') {\n out.push(' ', ' ');\n i += 2;\n break;\n }\n out.push(text[i] === '\\n' ? '\\n' : ' ');\n i++;\n }\n continue;\n }\n\n out.push(ch);\n i++;\n }\n return out.join('');\n}\n\n/**\n * Remove JSONC trailing commas from already comment-stripped text.\n * The returned text stays length-preserving (commas become spaces), which\n * gives JSON.parse a tolerant JSONC surface without affecting diagnostics.\n */\nfunction stripJsoncTrailingCommas(text: string): string {\n const chars = text.split(\"\");\n let i = 0;\n while (i < chars.length) {\n if (chars[i] === '\"') {\n const str = readJsonStringLiteral(text, i);\n if (!str) break;\n i = str.end;\n continue;\n }\n\n if (chars[i] === ',') {\n let j = i + 1;\n while (j < chars.length && isJsonWhitespace(chars[j])) j++;\n if (chars[j] === '}' || chars[j] === ']') chars[i] = ' ';\n }\n i++;\n }\n return chars.join('');\n}\n\nfunction parseJsonc(text: string): unknown {\n return JSON.parse(stripJsoncTrailingCommas(stripJsoncComments(text)));\n}\n\ntype ExplicitCompatSource = \"modelOverride\" | \"model\" | \"provider\";\n\ninterface ExplicitCompatValue {\n source: ExplicitCompatSource;\n value: unknown;\n}\n\n/**\n * Resolve one explicitly configured compat value using Pi's models.json\n * precedence. Built-in model overrides win over custom model definitions,\n * which in turn win over provider defaults.\n */\nfunction resolveExplicitCompatValue(\n config: unknown,\n providerLabel: string,\n modelId: string,\n compatKey: string,\n): ExplicitCompatValue | undefined {\n const providers = asRecord(asRecord(config)?.providers);\n const provider = asRecord(providers?.[providerLabel]);\n if (!provider) return undefined;\n\n const override = asRecord(asRecord(provider.modelOverrides)?.[modelId]);\n const overrideCompat = asRecord(override?.compat);\n if (overrideCompat && Object.prototype.hasOwnProperty.call(overrideCompat, compatKey)) {\n return { source: \"modelOverride\", value: overrideCompat[compatKey] };\n }\n\n if (Array.isArray(provider.models)) {\n const model = provider.models.find((entry: unknown) => asRecord(entry)?.id === modelId);\n const modelCompat = asRecord(asRecord(model)?.compat);\n if (modelCompat && Object.prototype.hasOwnProperty.call(modelCompat, compatKey)) {\n return { source: \"model\", value: modelCompat[compatKey] };\n }\n }\n\n const providerCompat = asRecord(provider.compat);\n if (providerCompat && Object.prototype.hasOwnProperty.call(providerCompat, compatKey)) {\n return { source: \"provider\", value: providerCompat[compatKey] };\n }\n\n return undefined;\n}\n\nfunction hasExplicitLongRetentionOptInFromConfig(\n config: unknown,\n providerLabel: string,\n modelId: string,\n): boolean {\n return resolveExplicitCompatValue(\n config,\n providerLabel,\n modelId,\n \"supportsLongCacheRetention\",\n )?.value === true;\n}\n\n/**\n * JSONC scanner: locate the provider block and model entry in models.json text.\n * Returns the byte offsets for surgical insertion, or undefined if ambiguous.\n */\ninterface ModelNodeLocation {\n /** Offset of the model object's opening `{` */\n modelObjectBrace: number;\n /** Offset of the model object's closing `}` */\n modelObjectEnd: number;\n /** Offset of the \"compat\" key start (the `\"`), or -1 if compat doesn't exist */\n compatKeyStart: number;\n /** Offset of the compat object's opening `{`, or -1 if compat doesn't exist */\n compatObjectBrace: number;\n /** Offset of the compat object's closing `}`, or -1 */\n compatObjectEnd: number;\n /** Indentation string to use for inserted lines (derived from surrounding context) */\n indent: string;\n /** Offset of the provider object's opening `{` */\n providerObjectBrace: number;\n /** Offset of the provider object's closing `}` */\n providerObjectEnd: number;\n /** Offset of the provider-level compat object's opening `{`, or -1 if absent */\n providerCompatBrace: number;\n /** Offset of the provider-level compat object's closing `}`, or -1 if absent */\n providerCompatEnd: number;\n /** Offset of the target modelOverrides entry's opening `{`, or -1 if absent */\n modelOverrideObjectBrace: number;\n /** Offset of the target modelOverrides entry's closing `}`, or -1 if absent */\n modelOverrideObjectEnd: number;\n /** Offset of the target override compat object's opening `{`, or -1 if absent */\n modelOverrideCompatBrace: number;\n /** Offset of the target override compat object's closing `}`, or -1 if absent */\n modelOverrideCompatEnd: number;\n /** All model ids found in this provider's models array (for placement safety analysis) */\n allModelIds: string[];\n}\n\ninterface ModelOverrideNodeLocation {\n providerObjectBrace: number;\n providerObjectEnd: number;\n modelOverridesObjectBrace: number;\n modelOverridesObjectEnd: number;\n modelOverrideObjectBrace: number;\n modelOverrideObjectEnd: number;\n modelOverrideCompatBrace: number;\n modelOverrideCompatEnd: number;\n}\n\n/** Locate a provider and optional modelOverrides entry without requiring models[]. */\nfunction locateModelOverrideInJsonc(\n text: string,\n providerLabel: string,\n modelId: string,\n): ModelOverrideNodeLocation | undefined {\n const clean = stripJsoncComments(text);\n const rootBrace = skipJsonWhitespace(clean, 0);\n if (clean[rootBrace] !== \"{\") return undefined;\n const providersKey = findJsonObjectKey(clean, rootBrace, \"providers\");\n if (!providersKey) return undefined;\n const providersBrace = skipJsonWhitespace(clean, providersKey.valueStart);\n if (clean[providersBrace] !== \"{\") return undefined;\n const providersEnd = findMatchingBracket(clean, providersBrace);\n if (providersEnd === undefined) return undefined;\n const providerKey = findJsonObjectKey(clean, providersBrace, providerLabel);\n if (!providerKey || providerKey.keyStart > providersEnd) return undefined;\n const providerObjectBrace = skipJsonWhitespace(clean, providerKey.valueStart);\n if (clean[providerObjectBrace] !== \"{\") return undefined;\n const providerObjectEnd = findMatchingBracket(clean, providerObjectBrace);\n if (providerObjectEnd === undefined || providerObjectEnd > providersEnd) return undefined;\n\n let modelOverridesObjectBrace = -1;\n let modelOverridesObjectEnd = -1;\n let modelOverrideObjectBrace = -1;\n let modelOverrideObjectEnd = -1;\n let modelOverrideCompatBrace = -1;\n let modelOverrideCompatEnd = -1;\n const overridesKey = findJsonObjectKey(clean, providerObjectBrace, \"modelOverrides\");\n if (overridesKey && overridesKey.keyStart < providerObjectEnd) {\n const brace = skipJsonWhitespace(clean, overridesKey.valueStart);\n if (clean[brace] !== \"{\") return undefined;\n const end = findMatchingBracket(clean, brace);\n if (end === undefined || end > providerObjectEnd) return undefined;\n modelOverridesObjectBrace = brace;\n modelOverridesObjectEnd = end;\n\n const overrideKey = findJsonObjectKey(clean, brace, modelId);\n if (overrideKey && overrideKey.keyStart < end) {\n const entryBrace = skipJsonWhitespace(clean, overrideKey.valueStart);\n if (clean[entryBrace] !== \"{\") return undefined;\n const entryEnd = findMatchingBracket(clean, entryBrace);\n if (entryEnd === undefined || entryEnd > end) return undefined;\n modelOverrideObjectBrace = entryBrace;\n modelOverrideObjectEnd = entryEnd;\n\n const compatKey = findJsonObjectKey(clean, entryBrace, \"compat\");\n if (compatKey && compatKey.keyStart < entryEnd) {\n const compatBrace = skipJsonWhitespace(clean, compatKey.valueStart);\n if (clean[compatBrace] !== \"{\") return undefined;\n const compatEnd = findMatchingBracket(clean, compatBrace);\n if (compatEnd === undefined || compatEnd > entryEnd) return undefined;\n modelOverrideCompatBrace = compatBrace;\n modelOverrideCompatEnd = compatEnd;\n }\n }\n }\n\n return {\n providerObjectBrace,\n providerObjectEnd,\n modelOverridesObjectBrace,\n modelOverridesObjectEnd,\n modelOverrideObjectBrace,\n modelOverrideObjectEnd,\n modelOverrideCompatBrace,\n modelOverrideCompatEnd,\n };\n}\n\n/**\n * Locate the provider + model entry in raw JSONC text.\n * Returns the positions needed for surgical insertion, or undefined on failure.\n *\n * This is a scan-only pass — no AST build, no regex reliance.\n */\nfunction locateModelInJsonc(\n text: string,\n providerLabel: string,\n modelId: string,\n): ModelNodeLocation | undefined {\n // Clean text of comments first for reliable structural scanning\n const clean = stripJsoncComments(text);\n\n // Strategy: find `\"providers\"` as a direct root key, then find the\n // provider key under it, then the provider's direct `\"models\"` key.\n // All object/value traversal uses the string-aware primitives above so\n // braces, brackets, comment markers, or escaped quotes inside strings do\n // not corrupt offsets.\n const rootBrace = skipJsonWhitespace(clean, 0);\n if (clean[rootBrace] !== \"{\") return undefined;\n\n const providersKey = findJsonObjectKey(clean, rootBrace, \"providers\");\n if (!providersKey) return undefined;\n const providersBrace = skipJsonWhitespace(clean, providersKey.valueStart);\n if (clean[providersBrace] !== \"{\") return undefined;\n const providersEnd = findMatchingBracket(clean, providersBrace);\n if (providersEnd === undefined) return undefined;\n\n const providerKey = findJsonObjectKey(clean, providersBrace, providerLabel);\n if (!providerKey || providerKey.keyStart > providersEnd) return undefined;\n const providerBrace = skipJsonWhitespace(clean, providerKey.valueStart);\n if (clean[providerBrace] !== \"{\") return undefined;\n const providerEndBrace = findMatchingBracket(clean, providerBrace);\n if (providerEndBrace === undefined || providerEndBrace > providersEnd) return undefined;\n\n // Provider-level compat is a direct provider child only. Nested model\n // compat objects are intentionally skipped whole by findJsonObjectKey.\n let providerCompatBrace = -1;\n let providerCompatEnd = -1;\n const providerCompatKey = findJsonObjectKey(clean, providerBrace, \"compat\");\n if (providerCompatKey && providerCompatKey.keyStart < providerEndBrace) {\n const brace = skipJsonWhitespace(clean, providerCompatKey.valueStart);\n if (clean[brace] === \"{\") {\n const end = findMatchingBracket(clean, brace);\n if (end !== undefined && end <= providerEndBrace) {\n providerCompatBrace = brace;\n providerCompatEnd = end;\n }\n }\n }\n\n const overrideLocation = locateModelOverrideInJsonc(text, providerLabel, modelId);\n const modelOverrideObjectBrace = overrideLocation?.modelOverrideObjectBrace ?? -1;\n const modelOverrideObjectEnd = overrideLocation?.modelOverrideObjectEnd ?? -1;\n const modelOverrideCompatBrace = overrideLocation?.modelOverrideCompatBrace ?? -1;\n const modelOverrideCompatEnd = overrideLocation?.modelOverrideCompatEnd ?? -1;\n\n const modelsKey = findJsonObjectKey(clean, providerBrace, \"models\");\n if (!modelsKey || modelsKey.keyStart > providerEndBrace) return undefined;\n\n let modelsScan = skipJsonWhitespace(clean, modelsKey.valueStart);\n if (clean[modelsScan] !== \"[\") return undefined;\n const modelsEnd = findMatchingBracket(clean, modelsScan);\n if (modelsEnd === undefined || modelsEnd > providerEndBrace) return undefined;\n modelsScan++; // Skip `[`\n\n // Scan ALL array elements: collect every model id, and record the target's position\n const allModelIds: string[] = [];\n let modelBrace = -1;\n let modelEndBrace = -1;\n let compatKeyStartClean = -1;\n let compatBrace = -1;\n let compatEndBrace = -1;\n\n while (modelsScan < modelsEnd) {\n modelsScan = skipJsonWhitespace(clean, modelsScan);\n if (clean[modelsScan] === ',') {\n modelsScan++;\n continue;\n }\n if (modelsScan >= modelsEnd || clean[modelsScan] === ']') break;\n if (clean[modelsScan] !== '{') return undefined;\n\n const elementBrace = modelsScan;\n const elementEnd = findMatchingBracket(clean, elementBrace);\n if (elementEnd === undefined || elementEnd > modelsEnd) return undefined;\n\n const idKey = findJsonObjectKey(clean, elementBrace, \"id\");\n let elementId: string | undefined;\n if (idKey && idKey.keyStart < elementEnd) {\n const idValueStart = skipJsonWhitespace(clean, idKey.valueStart);\n const idLiteral = readJsonStringLiteral(clean, idValueStart);\n if (idLiteral && idLiteral.end <= elementEnd) {\n elementId = idLiteral.value;\n }\n }\n\n if (elementId !== undefined) {\n allModelIds.push(elementId);\n }\n\n if (elementId === modelId && modelBrace < 0) {\n modelBrace = elementBrace;\n modelEndBrace = elementEnd;\n\n const compatKey = findJsonObjectKey(clean, modelBrace, \"compat\");\n if (compatKey && compatKey.keyStart < modelEndBrace) {\n compatKeyStartClean = compatKey.keyStart;\n const brace = skipJsonWhitespace(clean, compatKey.valueStart);\n if (clean[brace] === \"{\") {\n const end = findMatchingBracket(clean, brace);\n if (end !== undefined && end <= modelEndBrace) {\n compatBrace = brace;\n compatEndBrace = end;\n }\n }\n }\n }\n\n modelsScan = elementEnd + 1;\n }\n\n if (modelBrace < 0 || modelEndBrace < 0) return undefined;\n\n // Derive indentation from the model object's opening `{` line in original text\n // Look backwards to find the line start\n let lineStart = text.lastIndexOf('\\n', modelBrace);\n if (lineStart < 0) lineStart = 0;\n const lineBefore = text.slice(lineStart, modelBrace);\n const indentMatch = lineBefore.match(/^(\\s*)/);\n const baseIndent = indentMatch ? indentMatch[1] : ' ';\n const indent = baseIndent + ' '; // +2 for one level deeper\n\n return {\n modelObjectBrace: modelBrace,\n modelObjectEnd: modelEndBrace,\n compatKeyStart: compatKeyStartClean >= 0 ? compatKeyStartClean : -1,\n compatObjectBrace: compatBrace,\n compatObjectEnd: compatEndBrace,\n indent,\n providerObjectBrace: providerBrace,\n providerObjectEnd: providerEndBrace,\n providerCompatBrace,\n providerCompatEnd,\n modelOverrideObjectBrace,\n modelOverrideObjectEnd,\n modelOverrideCompatBrace,\n modelOverrideCompatEnd,\n allModelIds,\n };\n}\n\n/**\n * Scan produced by `analyzeModelsJsonForMissingEntry` when\n * `locateModelInJsonc` cannot find the target provider/model.\n */\ntype MissingEntryDiagnosis =\n | { scenario: \"provider_missing\"; providersBrace: number; providersEnd: number }\n | { scenario: \"model_missing\"; modelsEnd: number; providerBrace: number; providerEndBrace: number }\n | { scenario: \"provider_without_models\"; providerBrace: number; providerEndBrace: number };\n\n/**\n * Light second-pass scan that determines *why* `locateModelInJsonc` failed.\n * Returns structured diagnostic so the fix handler can compose targeted\n * guidance and an optional surgical insertion for API-logged-in models\n * (e.g. opencode go) that never appear in `models.json`.\n */\nfunction analyzeModelsJsonForMissingEntry(\n text: string,\n providerLabel: string,\n modelId: string,\n): MissingEntryDiagnosis | undefined {\n const clean = stripJsoncComments(text);\n const rootBrace = skipJsonWhitespace(clean, 0);\n if (clean[rootBrace] !== \"{\") return undefined;\n\n const providersKey = findJsonObjectKey(clean, rootBrace, \"providers\");\n if (!providersKey) {\n // Root has no \"providers\" key at all — we don't auto-create one.\n return undefined;\n }\n const providersBrace = skipJsonWhitespace(clean, providersKey.valueStart);\n if (clean[providersBrace] !== \"{\") return undefined;\n const providersEnd = findMatchingBracket(clean, providersBrace);\n if (providersEnd === undefined) return undefined;\n\n const providerKey = findJsonObjectKey(clean, providersBrace, providerLabel);\n if (!providerKey || providerKey.keyStart > providersEnd) {\n return { scenario: \"provider_missing\", providersBrace, providersEnd };\n }\n\n // Provider exists. Check for a models array so we know where to append.\n const providerBrace = skipJsonWhitespace(clean, providerKey.valueStart);\n if (clean[providerBrace] !== \"{\") return undefined;\n const providerEndBrace = findMatchingBracket(clean, providerBrace);\n if (providerEndBrace === undefined || providerEndBrace > providersEnd) return undefined;\n\n const modelsKey = findJsonObjectKey(clean, providerBrace, \"models\");\n if (modelsKey && modelsKey.keyStart < providerEndBrace) {\n let mScan = skipJsonWhitespace(clean, modelsKey.valueStart);\n if (clean[mScan] === \"[\") {\n const modelsEnd = findMatchingBracket(clean, mScan);\n if (modelsEnd !== undefined && modelsEnd <= providerEndBrace) {\n return { scenario: \"model_missing\", modelsEnd, providerBrace, providerEndBrace };\n }\n }\n }\n\n // Provider exists, but there's no discoverable models array — treat as\n // a provider that needs one.\n return { scenario: \"provider_without_models\", providerBrace, providerEndBrace };\n}\n\n/**\n * Build a copyable manual-edit snippet for the missing entry. Used when the\n * terminal is non-interactive or the user chooses to edit by hand.\n * Returns a complete provider→model→compat JSON block that the user can\n * paste into `models.json` under `providers`.\n */\nfunction formatMissingEntryManualSnippet(\n providerLabel: string,\n modelId: string,\n compatKeys: Record<string, unknown>,\n): string {\n const lines: string[] = [];\n const sorted = Object.entries(compatKeys).sort(([a], [b]) => a.localeCompare(b));\n const compatItems = sorted.map(([k, v]) => ` ${JSON.stringify(k)}: ${JSON.stringify(v)}`);\n lines.push(`\"${providerLabel}\": {`);\n lines.push(` \"modelOverrides\": {`);\n lines.push(` ${JSON.stringify(modelId)}: {`);\n lines.push(` \"compat\": {`);\n lines.push(compatItems.join(\",\\n\"));\n lines.push(` }`);\n lines.push(` }`);\n lines.push(` }`);\n lines.push(` }`);\n return lines.join(\"\\n\");\n}\n\n/**\n * Insert or repair a modelOverrides entry without creating a custom models[]\n * definition. This is the safest representation for built-in/API-login models\n * and has the same highest precedence Pi applies at runtime.\n */\nfunction composeModelOverrideInsertion(\n originalText: string,\n providerLabel: string,\n modelId: string,\n compatKeys: Record<string, unknown>,\n): { modifiedText: string; placementLabel: string } | undefined {\n const location = locateModelOverrideInJsonc(originalText, providerLabel, modelId);\n\n if (location?.modelOverrideObjectBrace !== undefined && location.modelOverrideObjectBrace >= 0) {\n const modelLocation: ModelNodeLocation = {\n modelObjectBrace: -1,\n modelObjectEnd: -1,\n compatKeyStart: -1,\n compatObjectBrace: -1,\n compatObjectEnd: -1,\n indent: \"\",\n providerObjectBrace: location.providerObjectBrace,\n providerObjectEnd: location.providerObjectEnd,\n providerCompatBrace: -1,\n providerCompatEnd: -1,\n modelOverrideObjectBrace: location.modelOverrideObjectBrace,\n modelOverrideObjectEnd: location.modelOverrideObjectEnd,\n modelOverrideCompatBrace: location.modelOverrideCompatBrace,\n modelOverrideCompatEnd: location.modelOverrideCompatEnd,\n allModelIds: [],\n };\n return {\n modifiedText: composeFixInsertion(originalText, modelLocation, compatKeys, \"modelOverride\"),\n placementLabel: `providers[\"${providerLabel}\"] -> modelOverrides[\"${modelId}\"] -> compat`,\n };\n }\n\n const sortedEntries = Object.entries(compatKeys).sort(([a], [b]) => a.localeCompare(b));\n const formatOverrideEntry = (keyIndent: string, unit: string): string => {\n const propertyIndent = keyIndent + unit;\n const compatIndent = propertyIndent + unit;\n const compatLines = sortedEntries\n .map(([key, value]) => `${compatIndent}${JSON.stringify(key)}: ${JSON.stringify(value)}`)\n .join(\",\\n\");\n return `${keyIndent}${JSON.stringify(modelId)}: {\\n` +\n `${propertyIndent}\"compat\": {\\n${compatLines}\\n${propertyIndent}}\\n${keyIndent}}`;\n };\n const previousTokenNeedsComma = (clean: string, closeBrace: number): boolean => {\n let pos = closeBrace - 1;\n while (pos >= 0 && isJsonWhitespace(clean[pos])) pos--;\n return clean[pos] !== \"{\" && clean[pos] !== \",\";\n };\n\n if (location) {\n const clean = stripJsoncComments(originalText);\n if (location.modelOverridesObjectBrace >= 0) {\n const containerIndent = lineIndentOf(originalText, location.modelOverridesObjectBrace);\n const keyIndent = deriveInnerIndent(\n originalText,\n location.modelOverridesObjectBrace,\n location.modelOverridesObjectEnd,\n );\n const unit = keyIndent.length > containerIndent.length\n ? keyIndent.slice(containerIndent.length)\n : \" \";\n const comma = previousTokenNeedsComma(clean, location.modelOverridesObjectEnd) ? \",\" : \"\";\n const insertion = `${comma}\\n${formatOverrideEntry(keyIndent, unit)}\\n${containerIndent}`;\n return {\n modifiedText: originalText.slice(0, location.modelOverridesObjectEnd) + insertion + originalText.slice(location.modelOverridesObjectEnd),\n placementLabel: `providers[\"${providerLabel}\"] -> modelOverrides -> (new entry \"${modelId}\")`,\n };\n }\n\n const providerIndent = lineIndentOf(originalText, location.providerObjectBrace);\n const propertyIndent = deriveInnerIndent(originalText, location.providerObjectBrace, location.providerObjectEnd);\n const unit = propertyIndent.length > providerIndent.length\n ? propertyIndent.slice(providerIndent.length)\n : \" \";\n const entryIndent = propertyIndent + unit;\n const block = `\\n${propertyIndent}\"modelOverrides\": {\\n` +\n `${formatOverrideEntry(entryIndent, unit)}\\n${propertyIndent}},`;\n return {\n modifiedText: originalText.slice(0, location.providerObjectBrace + 1) + block + originalText.slice(location.providerObjectBrace + 1),\n placementLabel: `providers[\"${providerLabel}\"] -> (new modelOverrides entry \"${modelId}\")`,\n };\n }\n\n const diagnosis = analyzeModelsJsonForMissingEntry(originalText, providerLabel, modelId);\n if (!diagnosis || diagnosis.scenario !== \"provider_missing\") return undefined;\n const clean = stripJsoncComments(originalText);\n const providersIndent = lineIndentOf(originalText, diagnosis.providersEnd);\n const providerIndent = deriveInnerIndent(originalText, diagnosis.providersBrace, diagnosis.providersEnd);\n const unit = providerIndent.length > providersIndent.length\n ? providerIndent.slice(providersIndent.length)\n : \" \";\n const overridesIndent = providerIndent + unit;\n const entryIndent = overridesIndent + unit;\n const comma = previousTokenNeedsComma(clean, diagnosis.providersEnd) ? \",\" : \"\";\n const block = `${comma}\\n${providerIndent}${JSON.stringify(providerLabel)}: {\\n` +\n `${overridesIndent}\"modelOverrides\": {\\n${formatOverrideEntry(entryIndent, unit)}\\n` +\n `${overridesIndent}}\\n${providerIndent}}\\n${providersIndent}`;\n return {\n modifiedText: originalText.slice(0, diagnosis.providersEnd) + block + originalText.slice(diagnosis.providersEnd),\n placementLabel: `providers -> (new modelOverrides-only entry \"${providerLabel}/${modelId}\")`,\n };\n}\n\n/**\n * Surgically insert the missing provider/model entry into the original\n * JSONC text. Returns the modified text and placement descriptor.\n *\n * Handles three scenarios:\n * - `model_missing`: append a new model object to the provider's `models` array.\n * - `provider_missing`: append a new provider block to the root `providers` object.\n * - `provider_without_models`: inject a `\"models\": [...]` key into the existing provider.\n */\nfunction composeMissingEntryInsertion(\n originalText: string,\n diagnosis: MissingEntryDiagnosis,\n providerLabel: string,\n modelId: string,\n compatKeys: Record<string, unknown>,\n): { modifiedText: string; placementLabel: string } {\n // Comments preserve length when stripped (`stripJsoncComments` replaces\n // comment bytes 1-for-1 with spaces), so offsets derived from the\n // comment-stripped text map cleanly back to the original. However,\n // `lastIndexOf(\"{\", pos)` / `lastIndexOf(\"[\", pos)` must NOT be run\n // against the raw original: a comment like `// add [more] here with a {\n // brace` would surface `[` / `{` bytes that have no structural meaning,\n // contaminating the `hasExisting`/`hasExistingElements` decision below\n // and producing a stray leading comma. Run the structural searches\n // against the comment-stripped version; keep the indentation lookups\n // (which only care about newlines + leading whitespace) on the\n // original since comments never contain forward-scan-relevant bytes.\n const cleanText = stripJsoncComments(originalText);\n\n // Resolve a sensible indentation step from an arbitrary byte offset in\n // the original file.\n const indentUnitAt = (offset: number): string => {\n const ls = originalText.lastIndexOf(\"\\n\", offset);\n const line = originalText.slice(ls < 0 ? 0 : ls + 1, offset);\n const m = line.match(/^(\\s+)/);\n return m ? m[1] : \" \";\n };\n\n // Figure out the base indent from the insertion point's own line.\n // Then derive inner indents (+1 and +2 levels).\n const sorted = Object.entries(compatKeys).sort(([a], [b]) => a.localeCompare(b));\n const formatCompactCompat = (indent: string): string => {\n // Single-line compact when there's only one key, multi-line otherwise.\n if (sorted.length === 1) {\n const [k, v] = sorted[0];\n return `{ ${JSON.stringify(k)}: ${JSON.stringify(v)} }`;\n }\n return (\n \"{\\n\" +\n sorted.map(([k, v]) => `${indent}${JSON.stringify(k)}: ${JSON.stringify(v)}`).join(\",\\n\") +\n \"\\n\" +\n indent.slice(0, -2) +\n \"}\"\n );\n };\n\n if (diagnosis.scenario === \"model_missing\") {\n // Append to the provider's models array, right before `]`.\n const unit = indentUnitAt(diagnosis.modelsEnd);\n const inner0 = unit + unit; // indent of model object's own keys\n const inner1 = inner0 + unit; // indent of compat keys inside the model\n const inner2 = inner1 + unit; // indent of compat values\n\n // Determine whether the array is empty (need to skip the leading comma).\n // Search for the models `[` on the comment-stripped text so a `[` inside\n // a comment cannot be mistaken for the array opener.\n const arrayInterior = cleanText.slice(\n cleanText.lastIndexOf(\"[\", diagnosis.modelsEnd) + 1,\n diagnosis.modelsEnd,\n ).trim();\n const hasExistingElements = arrayInterior.length > 0;\n\n const compatBlock = formatCompactCompat(inner2);\n const modelBlock = [\n hasExistingElements ? \",\" : \"\",\n inner0 + \"{\",\n inner1 + `\"id\": ${JSON.stringify(modelId)},`,\n inner1 + `\"compat\": ` + compatBlock,\n inner0 + \"}\",\n unit,\n ].filter(Boolean).join(\"\\n\");\n\n const insertionPoint = diagnosis.modelsEnd;\n const prefix = originalText.slice(0, insertionPoint);\n const suffix = originalText.slice(insertionPoint); // starts with `]`\n return {\n modifiedText: prefix + modelBlock + suffix,\n placementLabel: `providers[\"${providerLabel}\"] -> models -> (new entry for \"${modelId}\")`,\n };\n }\n\n if (diagnosis.scenario === \"provider_missing\") {\n // Append a new provider entry to the root `providers` object, right\n // before its closing `}`.\n const unit = indentUnitAt(diagnosis.providersEnd);\n const inner0 = unit + unit;\n const inner1 = inner0 + unit;\n const inner2 = inner1 + unit;\n const inner3 = inner2 + unit;\n\n const compatBlock = formatCompactCompat(inner3);\n // Search for the providers `{` on the comment-stripped text so a `{`\n // inside a comment cannot be mistaken for the providers object opener.\n const providersInterior = cleanText.slice(\n cleanText.lastIndexOf(\"{\", diagnosis.providersEnd) + 1,\n diagnosis.providersEnd,\n ).trim();\n const hasExisting = providersInterior.length > 0;\n\n const providerBlock = [\n hasExisting ? \",\" : \"\",\n inner0 + `\"${providerLabel}\": {`,\n inner1 + `\"models\": [`,\n inner2 + \"{\",\n inner3 + `\"id\": ${JSON.stringify(modelId)},`,\n inner3 + `\"compat\": ` + compatBlock,\n inner2 + \"}\",\n inner1 + \"]\",\n inner0 + \"}\",\n unit,\n ].filter(Boolean).join(\"\\n\");\n\n const insertionPoint = diagnosis.providersEnd;\n const prefix = originalText.slice(0, insertionPoint);\n const suffix = originalText.slice(insertionPoint);\n return {\n modifiedText: prefix + providerBlock + suffix,\n placementLabel: `providers -> (new entry \"${providerLabel}\")`,\n };\n }\n\n // `provider_without_models`: inject a models array key into the\n // existing provider block, right after the provider's opening `{`.\n const unit = indentUnitAt(diagnosis.providerBrace);\n const inner0 = unit + unit;\n const inner1 = inner0 + unit;\n const inner2 = inner1 + unit;\n\n const compatBlock = formatCompactCompat(inner2);\n const afterBrace = diagnosis.providerBrace + 1;\n const modelsBlock = [\n \"\",\n inner0 + `\"models\": [`,\n inner1 + \"{\",\n inner2 + `\"id\": ${JSON.stringify(modelId)},`,\n inner2 + `\"compat\": ` + compatBlock,\n inner1 + \"}\",\n inner0 + \"],\",\n unit,\n ].join(\"\\n\");\n\n return {\n modifiedText: originalText.slice(0, afterBrace) + modelsBlock + originalText.slice(afterBrace),\n placementLabel: `providers[\"${providerLabel}\"] -> (new \"models\" array with \"${modelId}\")`,\n };\n}\n\n/**\n * Lightweight self-check for a newly inserted model or modelOverrides entry.\n * Parses the modified text as JSONC and confirms:\n * 1. The target exists under models[] or modelOverrides.\n * 2. Every compat key has the expected effective three-layer value.\n * Returns null on success, an error string on failure.\n */\nfunction selfCheckMissingEntryInsertion(\n originalText: string,\n modifiedText: string,\n providerLabel: string,\n modelId: string,\n compatKeys: Record<string, unknown>,\n): string | null {\n try {\n const origParsed = parseJsonc(originalText);\n const modParsed = parseJsonc(modifiedText);\n const providers = asRecord(asRecord(modParsed)?.providers);\n if (!providers) return \"Modified file: providers object missing or invalid\";\n const provider = asRecord(providers[providerLabel]);\n if (!provider) return `Modified file: provider \"${providerLabel}\" not found`;\n const models = provider.models;\n const targetModel = Array.isArray(models)\n ? models.find((m: unknown) => asRecord(m)?.id === modelId)\n : undefined;\n const targetOverride = asRecord(asRecord(provider.modelOverrides)?.[modelId]);\n if (!targetModel && !targetOverride) {\n return `Modified file: model or modelOverrides entry \"${modelId}\" not found in provider after insertion`;\n }\n\n for (const [k, v] of Object.entries(compatKeys)) {\n const effective = resolveExplicitCompatValue(modParsed, providerLabel, modelId, k);\n if (!effective) return `Modified file: effective compat.${k} not found`;\n if (effective.value !== v) {\n return `Modified file: effective compat.${k} wrong value: expected ${JSON.stringify(v)}, got ${JSON.stringify(effective.value)} from ${effective.source}`;\n }\n }\n\n // Normalize only the intended override edit back to its original shape,\n // then require the complete parsed structure to match. This catches data\n // loss while allowing legitimate shorter repairs such as false -> true.\n const normalized = JSON.parse(JSON.stringify(modParsed)) as Record<string, unknown>;\n const origProviders = asRecord(asRecord(origParsed)?.providers);\n const origProvider = asRecord(origProviders?.[providerLabel]);\n const normalizedProviders = asRecord(normalized.providers);\n if (!normalizedProviders) return \"Modified file: normalized providers object missing\";\n\n if (!origProvider) {\n delete normalizedProviders[providerLabel];\n } else if (targetOverride) {\n const normalizedProvider = asRecord(normalizedProviders[providerLabel]);\n const normalizedOverrides = asRecord(normalizedProvider?.modelOverrides);\n const origOverrides = asRecord(origProvider.modelOverrides);\n const origOverride = asRecord(origOverrides?.[modelId]);\n\n if (!normalizedProvider || !normalizedOverrides) {\n return `Modified file: modelOverrides[\"${modelId}\"] missing during structure validation`;\n }\n if (!origOverride) {\n delete normalizedOverrides[modelId];\n if (!origOverrides) delete normalizedProvider.modelOverrides;\n } else {\n const normalizedOverride = asRecord(normalizedOverrides[modelId]);\n if (!normalizedOverride) return `Modified file: modelOverrides[\"${modelId}\"] invalid`;\n const origCompat = asRecord(origOverride.compat);\n const normalizedCompat = asRecord(normalizedOverride.compat);\n if (!origCompat) {\n delete normalizedOverride.compat;\n } else {\n if (!normalizedCompat) return `Modified file: modelOverrides[\"${modelId}\"].compat invalid`;\n for (const key of Object.keys(compatKeys)) {\n if (Object.prototype.hasOwnProperty.call(origCompat, key)) {\n normalizedCompat[key] = origCompat[key];\n } else {\n delete normalizedCompat[key];\n }\n }\n }\n }\n } else {\n // Backward-compatible validation for the legacy models[] insertion\n // helper retained in __internals_for_tests.\n const normalizedProvider = asRecord(normalizedProviders[providerLabel]);\n const normalizedModels = normalizedProvider?.models;\n const origModels = origProvider.models;\n if (!normalizedProvider || !Array.isArray(normalizedModels)) {\n return `Modified file: provider \"${providerLabel}\".models missing during structure validation`;\n }\n normalizedProvider.models = normalizedModels.filter(\n (entry: unknown) => asRecord(entry)?.id !== modelId,\n );\n if (!Array.isArray(origModels)) delete normalizedProvider.models;\n }\n\n if (!deepEqualIgnoringKeys(normalized, origParsed, [])) {\n return \"Modified file: original structure was altered (data loss detected)\";\n }\n\n const modClean = stripJsoncComments(modifiedText);\n const rootStart = skipJsonWhitespace(modClean, 0);\n const rootEnd = findMatchingBracket(modClean, rootStart);\n if (rootEnd === undefined) return \"Modified file: root bracket mismatch\";\n if (skipJsonWhitespace(modClean, rootEnd + 1) !== modClean.length)\n return \"Modified file: trailing content after root object\";\n\n return null;\n } catch (e) {\n return `Self-check error: ${e instanceof Error ? e.message : String(e)}`;\n }\n}\n\n/**\n * Deep-equal comparison of two values, used for post-write self-check.\n * Compares all keys recursively, allowing `extraKeys` to be present in `a` but not in `b`.\n */\nfunction deepEqualIgnoringKeys(a: unknown, b: unknown, extraKeys: string[]): boolean {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!deepEqualIgnoringKeys(a[i], b[i], extraKeys)) return false;\n }\n return true;\n }\n if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {\n const aKeys = Object.keys(a as Record<string, unknown>).filter(k => !extraKeys.includes(k));\n const bKeys = Object.keys(b as Record<string, unknown>);\n if (aKeys.length !== bKeys.length) return false;\n for (const k of aKeys) {\n if (!(k in (b as Record<string, unknown>))) return false;\n if (!deepEqualIgnoringKeys(\n (a as Record<string, unknown>)[k],\n (b as Record<string, unknown>)[k],\n extraKeys,\n )) return false;\n }\n return true;\n }\n return false;\n}\n\n/**\n * Compose the fix: produce the modified text with compat keys inserted.\n *\n * Strategy:\n * - If compat object exists: replace its interior (between `{` and `}`)\n * with new keys + existing content, preserving surrounding bytes.\n * - If compat doesn't exist: insert `\"compat\": { keys }` after model `{`.\n *\n * Uses the raw original text; only the inserted/compat region changes.\n */\n/**\n * Compat keys that describe CHANNEL capabilities (routing, endpoint features).\n * These are always safe at the provider level because they do not change\n * per-model request semantics.\n */\nconst PROVIDER_LEVEL_SAFE_COMPAT_KEYS = new Set<string>([\n \"sendSessionAffinityHeaders\",\n \"supportsLongCacheRetention\",\n]);\n\nfunction syntheticModelForId(providerLabel: string, id: string): PiModel {\n return { provider: providerLabel, id, name: id } as PiModel;\n}\n\n/**\n * Decide whether the fix should write provider-level or model-level compat.\n *\n * Strategy (auto-detect, prefer provider level when safe):\n * - Channel-capability keys (session affinity / long retention) are normally\n * provider-safe; runtime-observed model failures explicitly override this\n * default through chooseFixPlacement(..., forceModelLevel=true).\n * - Model-behavior keys (forceAdaptiveThinking, allowEmptySignature,\n * thinkingFormat, ...) are provider-safe ONLY when every sibling model in\n * the provider also matches the same detection (all adaptive-generation /\n * Kimi Coding adaptive / DeepSeek-like).\n * - Single-model providers: provider level is equivalent — prefer it.\n * - Any unsafe key → fall back to model level (single write, smallest blast radius).\n */\nfunction decideFixPlacement(\n compatKeys: Record<string, unknown>,\n providerLabel: string,\n allModelIds: string[],\n): { placement: \"provider\" | \"model\"; reason: string } {\n const siblings = allModelIds.filter(Boolean);\n\n if (siblings.length <= 1) {\n return {\n placement: \"provider\",\n reason: \"this provider has only one model — provider-level compat is equivalent and easier to maintain\",\n };\n }\n\n const unsafeKeys: string[] = [];\n for (const key of Object.keys(compatKeys)) {\n if (PROVIDER_LEVEL_SAFE_COMPAT_KEYS.has(key)) continue;\n\n if (key === \"forceAdaptiveThinking\") {\n const allAdaptive = siblings.every((id) => {\n const sibling = syntheticModelForId(providerLabel, id);\n return isAdaptiveGenerationModel(sibling) || isKimiCodingAdaptiveModel(sibling);\n });\n if (!allAdaptive) unsafeKeys.push(key);\n continue;\n }\n if (key === \"allowEmptySignature\") {\n const allKimiCodingAdaptive = siblings.every((id) => isKimiCodingAdaptiveModel(syntheticModelForId(providerLabel, id)));\n if (!allKimiCodingAdaptive) unsafeKeys.push(key);\n continue;\n }\n if (key === \"thinkingFormat\" || key === \"requiresReasoningContentOnAssistantMessages\") {\n const allDeepSeek = siblings.every((id) => isDeepSeekLikeModel(syntheticModelForId(providerLabel, id)));\n if (!allDeepSeek) unsafeKeys.push(key);\n continue;\n }\n // Unknown model-behavior key — be conservative, keep it model-scoped.\n unsafeKeys.push(key);\n }\n\n if (unsafeKeys.length === 0) {\n return {\n placement: \"provider\",\n reason: `all ${siblings.length} models in this provider are compatible with these flags`,\n };\n }\n return {\n placement: \"model\",\n reason: `${unsafeKeys.join(\", \")} could break sibling models in this provider (${siblings.length} models total) — scoping to this model only`,\n };\n}\n\nfunction findExistingCompatKeysInJsonc(\n original: string,\n compatBrace: number,\n compatEnd: number,\n keys: string[],\n): string[] {\n if (compatBrace < 0 || compatEnd <= compatBrace) return [];\n const clean = stripJsoncComments(original);\n return keys.filter((key) => {\n const found = findJsonObjectKey(clean, compatBrace, key);\n return !!found && found.keyStart < compatEnd;\n });\n}\n\nfunction chooseFixPlacement(\n original: string,\n location: ModelNodeLocation,\n compatKeys: Record<string, unknown>,\n providerLabel: string,\n forceModelLevel = false,\n): { placement: \"provider\" | \"model\" | \"modelOverride\"; reason: string } {\n if (location.modelOverrideObjectBrace >= 0) {\n return {\n placement: \"modelOverride\",\n reason: \"an existing modelOverrides entry has Pi's highest precedence — repairing it directly\",\n };\n }\n\n if (forceModelLevel) {\n return {\n placement: \"model\",\n reason: \"runtime-observed provider/model failure — scoping the repair to the affected model\",\n };\n }\n\n const decision = decideFixPlacement(compatKeys, providerLabel, location.allModelIds);\n const existingModelKeys = findExistingCompatKeysInJsonc(\n original,\n location.compatObjectBrace,\n location.compatObjectEnd,\n Object.keys(compatKeys),\n );\n\n // Provider-level writes cannot override a model-level compat key because Pi's\n // merge order is provider.compat then model.compat. If the active model already\n // has one of the keys we need to repair (e.g. thinkingFormat: \"legacy\"), write\n // at model level even when the key would otherwise be provider-safe.\n if (decision.placement === \"provider\" && existingModelKeys.length > 0) {\n return {\n placement: \"model\",\n reason: `model-level compat already contains ${existingModelKeys.join(\", \")} — repairing the active model override directly`,\n };\n }\n\n return decision;\n}\n\nfunction composeFixInsertion(\n original: string,\n location: ModelNodeLocation,\n compatKeys: Record<string, unknown>,\n placement: \"provider\" | \"model\" | \"modelOverride\" = \"model\",\n): string {\n // Resolve the target compat object and its container based on placement.\n const targetCompatBrace = placement === \"provider\"\n ? location.providerCompatBrace\n : placement === \"modelOverride\"\n ? location.modelOverrideCompatBrace\n : location.compatObjectBrace;\n const targetCompatEnd = placement === \"provider\"\n ? location.providerCompatEnd\n : placement === \"modelOverride\"\n ? location.modelOverrideCompatEnd\n : location.compatObjectEnd;\n const containerBrace = placement === \"provider\"\n ? location.providerObjectBrace\n : placement === \"modelOverride\"\n ? location.modelOverrideObjectBrace\n : location.modelObjectBrace;\n\n // Helper: format key/value pairs as lines with the given indent,\n // alphabetically sorted for stable previews and deterministic edits.\n const sortedEntries = Object.entries(compatKeys).sort(([a], [b]) => a.localeCompare(b));\n const formatEntries = (indent: string, entries: Array<[string, unknown]>): string =>\n entries\n .map(([k, v]) => `${indent}${JSON.stringify(k)}: ${JSON.stringify(v)}`)\n .join(',\\n');\n\n // Helper: line-start indentation of the line containing `offset` in `original`.\n const lineIndentAt = (offset: number): string => {\n let ls = original.lastIndexOf('\\n', offset);\n if (ls < 0) ls = -1;\n const line = original.slice(ls + 1, offset);\n const m = line.match(/^(\\s*)/);\n return m ? m[1] : '';\n };\n\n if (targetCompatBrace >= 0 && targetCompatEnd > targetCompatBrace) {\n // ── Existing compat object: insert absent keys and surgically replace\n // direct existing keys whose value is wrong (e.g. thinkingFormat: \"legacy\").\n // Unrelated interior bytes/comments/key order are preserved.\n const interiorStart = targetCompatBrace + 1;\n const interior = original.slice(interiorStart, targetCompatEnd);\n const hasContent = interior.trim().length > 0;\n const clean = stripJsoncComments(original);\n\n // Indent for inserted key lines: copy the first existing key line's indent,\n // else derive one level deeper than the compat brace's own line.\n const braceLineIndent = lineIndentAt(targetCompatBrace);\n const innerMatch = interior.match(/\\r?\\n([ \\t]+)\\S/);\n const innerIndent = innerMatch ? innerMatch[1] : braceLineIndent + ' ';\n\n const edits: Array<{ start: number; end: number; text: string }> = [];\n const missingEntries: Array<[string, unknown]> = [];\n\n for (const [key, value] of sortedEntries) {\n const existing = findJsonObjectKey(clean, targetCompatBrace, key);\n if (existing && existing.keyStart < targetCompatEnd) {\n const valueStart = skipJsonWhitespace(clean, existing.valueStart);\n const valueEnd = skipJsonValue(clean, valueStart);\n if (valueEnd !== undefined && valueEnd <= targetCompatEnd) {\n const nextValue = JSON.stringify(value);\n if (original.slice(valueStart, valueEnd) !== nextValue) {\n edits.push({ start: valueStart, end: valueEnd, text: nextValue });\n }\n continue;\n }\n }\n missingEntries.push([key, value]);\n }\n\n if (missingEntries.length > 0) {\n const keysFormatted = formatEntries(innerIndent, missingEntries);\n if (hasContent) {\n edits.push({ start: interiorStart, end: interiorStart, text: `\\n${keysFormatted},` });\n } else {\n edits.push({ start: interiorStart, end: targetCompatEnd, text: `\\n${keysFormatted}\\n${braceLineIndent}` });\n }\n }\n\n // Apply later edits first so earlier offsets remain valid.\n return edits\n .sort((a, b) => b.start - a.start)\n .reduce((text, edit) => text.slice(0, edit.start) + edit.text + text.slice(edit.end), original);\n }\n\n // ── No compat object yet: create one right after the container `{`. ──\n // Everything after the brace (including the next line's indentation) is\n // preserved byte-for-byte; we only prepend a complete `\"compat\": {...},` block.\n const afterBrace = containerBrace + 1;\n const suffix = original.slice(afterBrace);\n\n // Key indent: copy the first sibling key line's indent from the suffix,\n // else one level deeper than the container brace's line.\n const containerLineIndent = lineIndentAt(containerBrace);\n const siblingMatch = suffix.match(/^\\r?\\n([ \\t]+)\\S/);\n const keyIndent = siblingMatch ? siblingMatch[1] : containerLineIndent + ' ';\n\n // One more level for keys inside compat: reuse the file's own indent unit.\n const unit = keyIndent.startsWith(containerLineIndent) && keyIndent.length > containerLineIndent.length\n ? keyIndent.slice(containerLineIndent.length)\n : ' ';\n const innerIndent = keyIndent + unit;\n\n const compatBlock = `\\n${keyIndent}\"compat\": {\\n${formatEntries(innerIndent, sortedEntries)}\\n${keyIndent}},`;\n return original.slice(0, afterBrace) + compatBlock + suffix;\n}\n\n/**\n * Self-check after compose: parse original and modified as JSONC,\n * assert target compat flags exist in the right path, and remaining structure\n * is deep-equal (ignoring the inserted keys).\n * Returns null on success, error message on failure.\n */\nfunction selfCheckFix(\n original: string,\n modified: string,\n providerLabel: string,\n modelId: string,\n compatKeys: Record<string, unknown>,\n placement: \"provider\" | \"model\" | \"modelOverride\" = \"model\",\n): string | null {\n try {\n // Step 1: Parse both versions as JSONC (comments + trailing commas allowed).\n const origParsed = parseJsonc(original);\n const modParsed = parseJsonc(modified);\n\n // Step 2: Validate modified file has correct structure\n const providers = asRecord(asRecord(modParsed)?.providers);\n if (!providers) {\n return \"Modified file: providers object missing or invalid\";\n }\n const provider = asRecord(providers[providerLabel]);\n if (!provider) {\n return `Modified file: provider \"${providerLabel}\" not found`;\n }\n\n // Step 3: Validate models array structure\n const models = provider.models;\n if (!Array.isArray(models)) {\n return `Modified file: provider \"${providerLabel}\".models is not an array`;\n }\n if (models.length === 0) {\n return `Modified file: provider \"${providerLabel}\".models is empty`;\n }\n\n // Step 4: Find and validate target model\n const targetModel = models.find((m: Record<string, unknown>) => m.id === modelId);\n if (!targetModel || typeof targetModel !== 'object') {\n return `Modified file: model \"${modelId}\" not found in provider`;\n }\n\n // Locate the corresponding original provider/model objects. The structure\n // preservation check below may allow repaired compat values to differ, but\n // only on these exact target/provider compat objects — never on siblings.\n const origProviders = asRecord(asRecord(origParsed)?.providers);\n const origProvider = asRecord(origProviders?.[providerLabel]);\n const origModels = Array.isArray(origProvider?.models) ? origProvider.models : undefined;\n const origTargetModel = origModels?.find((m: unknown) => asRecord(m)?.id === modelId);\n const origTargetModelRecord = asRecord(origTargetModel);\n if (!origProvider || !origTargetModelRecord) {\n return `Original file: provider/model \"${providerLabel}/${modelId}\" not found`;\n }\n\n // Step 5: Compute the EFFECTIVE merged compat using Pi's precedence:\n // provider, custom model, then modelOverrides. The fix may have written any\n // level, so validation must check what Pi will actually use.\n const provCompatRaw = (provider as Record<string, unknown>).compat;\n const provCompat = (provCompatRaw && typeof provCompatRaw === 'object' && !Array.isArray(provCompatRaw))\n ? provCompatRaw as Record<string, unknown>\n : {};\n const modelCompatRaw = (targetModel as Record<string, unknown>).compat;\n if (modelCompatRaw !== undefined && (typeof modelCompatRaw !== 'object' || modelCompatRaw === null || Array.isArray(modelCompatRaw))) {\n return `Modified file: model \"${modelId}\" compat is not an object`;\n }\n const mdlCompat = (modelCompatRaw ?? {}) as Record<string, unknown>;\n const override = asRecord(asRecord(provider.modelOverrides)?.[modelId]);\n const overrideCompatRaw = override?.compat;\n if (overrideCompatRaw !== undefined && !asRecord(overrideCompatRaw)) {\n return `Modified file: modelOverrides[\"${modelId}\"].compat is not an object`;\n }\n const overrideCompat = asRecord(overrideCompatRaw) ?? {};\n const mergedCompat: Record<string, unknown> = { ...provCompat, ...mdlCompat, ...overrideCompat };\n\n // Step 6: Validate all inserted keys are effective in the merged compat\n for (const [k, v] of Object.entries(compatKeys)) {\n if (!(k in mergedCompat)) {\n return `Modified file: compat.${k} not found at provider or model level (insertion failed)`;\n }\n if (mergedCompat[k] !== v) {\n return `Modified file: effective compat.${k} has wrong value: expected ${JSON.stringify(v)}, got ${JSON.stringify(mergedCompat[k])}`;\n }\n }\n\n // Step 7: Validate original structure is preserved (no accidental deletions/changes)\n\n function isSubset(origVal: unknown, modVal: unknown, path = ''): boolean {\n if (origVal === modVal) return true;\n if (typeof origVal !== typeof modVal) return false;\n if (typeof origVal !== 'object' || origVal === null || modVal === null) return false;\n if (Array.isArray(origVal) !== Array.isArray(modVal)) return false;\n if (Array.isArray(origVal) && Array.isArray(modVal)) {\n if (origVal.length !== modVal.length) return false;\n return origVal.every((_, i) => isSubset(origVal[i], modVal[i], `${path}[${i}]`));\n }\n // Both objects: check that every key in orig is in mod with same value\n const origObj = origVal as Record<string, unknown>;\n const modObj = modVal as Record<string, unknown>;\n for (const key of Object.keys(origObj)) {\n if (!(key in modObj)) return false;\n if (key === 'compat') {\n // For compat, allow extra keys in modified (the inserted ones).\n // Use recursive isSubset so nested objects (e.g. { deep: true })\n // are compared by content, not reference.\n if (typeof origObj[key] !== 'object' || typeof modObj[key] !== 'object') {\n if (origObj[key] !== modObj[key]) return false;\n } else {\n const origCompat = origObj[key] as Record<string, unknown>;\n const modCompat = modObj[key] as Record<string, unknown>;\n // Only the compat object at the level ACTUALLY edited may have\n // its values repaired by this fix. The un-edited level must\n // remain byte/structure-equivalent, so its same-name keys stay\n // under full validation. Using a disjunction OR (provider ||\n // target) here would silently skip validation at the un-edited\n // level, masking corruption (e.g. a buggy editor accidentally\n // breaking provider.compat.sendSessionAffinityHeaders while\n // the fix was a model-level repair). Track placement — only\n // the placement-resolved object's own compat may be exempt.\n const mayRepairThisCompat =\n (placement === \"provider\" && origObj === origProvider) ||\n (placement === \"model\" && origObj === origTargetModelRecord) ||\n (placement === \"modelOverride\" && origObj === asRecord(asRecord(origProvider.modelOverrides)?.[modelId]));\n for (const ck of Object.keys(origCompat)) {\n if (!(ck in modCompat)) return false;\n // The fix may repair an existing wrong compat value (for example\n // thinkingFormat: \"legacy\" -> \"deepseek\"), but only on the\n // target provider/model compat objects. Sibling compat blocks must\n // remain structure-equivalent.\n if (mayRepairThisCompat && Object.prototype.hasOwnProperty.call(compatKeys, ck)) continue;\n if (!isSubset(origCompat[ck], modCompat[ck], `${path}.${ck}`)) return false;\n }\n }\n } else if (!isSubset(origObj[key], modObj[key], `${path}.${key}`)) {\n return false;\n }\n }\n return true;\n }\n\n if (!isSubset(origParsed, modParsed)) {\n return \"Modified file: original structure was altered (data loss detected)\";\n }\n\n // Note: we intentionally do NOT enforce `modified.length >= original.length`.\n // The surgical editor may replace an existing compat value with a shorter one\n // (e.g. `false` -> `true`), which legitimately shrinks the file by a byte.\n // Real data loss / truncation is already caught by Step 7's isSubset\n // (every original key still present) and Step 8's root-bracket integrity\n // check below — a surviving length heuristic would false-positive on every\n // such value repair. (Tracked: the mofas glm-5.2 self-check failure path.)\n\n // Step 8: Validate root bracket integrity with the same string/comment-aware\n // scanner used for edits. Do not count raw braces: comments or strings may\n // legitimately contain unmatched `{` / `}` bytes.\n const modifiedClean = stripJsoncComments(modified);\n const rootStart = skipJsonWhitespace(modifiedClean, 0);\n const rootEnd = findMatchingBracket(modifiedClean, rootStart);\n if (rootEnd === undefined) {\n return \"Modified file: root bracket mismatch\";\n }\n if (skipJsonWhitespace(modifiedClean, rootEnd + 1) !== modifiedClean.length) {\n return \"Modified file: trailing non-whitespace content after root object\";\n }\n\n return null;\n } catch (e) {\n return `Self-check error: ${e instanceof Error ? e.message : String(e)}`;\n }\n}\n\n/**\n * Serialize a compat suggestion to the JSON text that will be inserted.\n * Returns the exact key-value pairs as a formatted JSON string without outer braces.\n */\nfunction formatCompatKeysForInsertion(compatKeys: Record<string, unknown>): string {\n return Object.entries(compatKeys)\n .map(([k, v]) => {\n return ` ${JSON.stringify(k)}: ${JSON.stringify(v)}`;\n })\n .join(',\\n');\n}\n\n/**\n * Generate a unique UTC timestamp component for backup filenames.\n * Milliseconds, process id, and an in-process sequence prevent collisions.\n */\nlet backupSequence = 0;\n\nfunction backupTimestamp(now: Date = new Date()): string {\n const y = now.getUTCFullYear();\n const m = String(now.getUTCMonth() + 1).padStart(2, \"0\");\n const d = String(now.getUTCDate()).padStart(2, \"0\");\n const h = String(now.getUTCHours()).padStart(2, \"0\");\n const min = String(now.getUTCMinutes()).padStart(2, \"0\");\n const s = String(now.getUTCSeconds()).padStart(2, \"0\");\n const ms = String(now.getUTCMilliseconds()).padStart(3, \"0\");\n return `${y}${m}${d}T${h}${min}${s}${ms}Z-${process.pid}-${backupSequence++}`;\n}\n\nfunction uniqueTempPath(targetPath: string, purpose: string): string {\n return `${targetPath}.${process.pid}.${Date.now()}.${backupSequence++}.${purpose}.tmp`;\n}\n\nasync function atomicReplaceTextFilePreservingMode(\n targetPath: string,\n content: string,\n mode: number,\n purpose: string,\n): Promise<void> {\n const tempPath = uniqueTempPath(targetPath, purpose);\n try {\n await writeFile(tempPath, content, { encoding: \"utf8\", mode });\n await chmod(tempPath, mode);\n await rename(tempPath, targetPath);\n } catch (error) {\n try {\n await unlink(tempPath);\n } catch (cleanupError) {\n if (getErrorCode(cleanupError) !== \"ENOENT\") {\n console.warn(`${LOG_PREFIX}: failed to remove temporary models.json file`, cleanupError);\n }\n }\n throw error;\n }\n}\n\nasync function atomicRestoreFileFromBackup(\n backupPath: string,\n targetPath: string,\n mode: number,\n): Promise<void> {\n const tempPath = uniqueTempPath(targetPath, \"restore\");\n try {\n await copyFile(backupPath, tempPath, fsConstants.COPYFILE_EXCL);\n await chmod(tempPath, mode);\n await rename(tempPath, targetPath);\n } catch (error) {\n try {\n await unlink(tempPath);\n } catch (cleanupError) {\n if (getErrorCode(cleanupError) !== \"ENOENT\") {\n console.warn(`${LOG_PREFIX}: failed to remove temporary restore file`, cleanupError);\n }\n }\n throw error;\n }\n}\n\ntype ModelsJsonFixTransactionResult =\n | { ok: true }\n | { ok: false; postCheckError: string };\n\nasync function applyModelsJsonFixTransaction(\n modifiedText: string,\n backupPath: string,\n validateWrittenText: (writtenText: string) => string | null,\n): Promise<ModelsJsonFixTransactionResult> {\n const originalMode = (await stat(MODELS_JSON_PATH)).mode & 0o7777;\n await copyFile(MODELS_JSON_PATH, backupPath, fsConstants.COPYFILE_EXCL);\n await chmod(backupPath, originalMode);\n\n let targetReplaced = false;\n try {\n await atomicReplaceTextFilePreservingMode(\n MODELS_JSON_PATH,\n modifiedText,\n originalMode,\n \"fix\",\n );\n targetReplaced = true;\n\n const writtenText = await readFile(MODELS_JSON_PATH, \"utf8\");\n const postCheckError = validateWrittenText(writtenText);\n const writtenMode = (await stat(MODELS_JSON_PATH)).mode & 0o7777;\n const effectiveError = postCheckError ?? (\n writtenMode === originalMode\n ? null\n : `models.json access mode changed from ${originalMode.toString(8)} to ${writtenMode.toString(8)}`\n );\n if (effectiveError !== null) {\n await atomicRestoreFileFromBackup(backupPath, MODELS_JSON_PATH, originalMode);\n return { ok: false, postCheckError: effectiveError };\n }\n\n return { ok: true };\n } catch (error) {\n if (targetReplaced) {\n try {\n await atomicRestoreFileFromBackup(backupPath, MODELS_JSON_PATH, originalMode);\n } catch (restoreError) {\n throw new AggregateError(\n [error, restoreError],\n \"models.json fix failed and the atomic backup restore also failed\",\n );\n }\n }\n throw error;\n }\n}\n\n// Internal helpers exported only so the task verification script\n// (.trellis/tasks/.../verify.ts) can exercise them. They are not part of the\n// extension's public API; pi only invokes the default export below.\nexport const __internals_for_tests = {\n buildStableCandidates,\n optimizeSystemPrompt,\n stripSessionOverviewChurn,\n extractStructuralMarkers,\n formatSkillsForPrompt,\n formatSkillsForPromptCompressed,\n compressSkillsInSystemPrompt,\n MIN_STABLE_CANDIDATE_LENGTH,\n SKILL_COMPRESSION_MIN_COUNT,\n NO_PROMPT_REWRITE_ENV,\n isEnabledEnv,\n // OpenAI-family cache-key helpers\n addOpenAIPromptCacheKey,\n clampPromptCacheKey,\n hasEffectivePromptCacheKey,\n isNonEmptyString,\n shouldInjectOpenAIPromptCacheKey,\n shouldInjectOpenAIPromptCacheKeyForModel,\n isOpenAICompatibleApi,\n isAnthropicMessagesApi,\n isOpenAICompatibleProxyApi,\n collectAnthropicCacheControlsInWireOrder,\n downgradeAnthropicLongCacheControls,\n hasAnthropicCacheTtlOrderError,\n normalizeAnthropicCacheControlTtlOrder,\n isPiBuiltInLlamaCppModel,\n isResponsesPromptRewriteBypassApi,\n isMistralConversationsApi,\n isOpenAIFamilyModel,\n isOpenAIFamilyAssistantMessage,\n isOpenAIFamilyToken,\n describeMissingOpenAIFamilyProxyCompat,\n describeMissingOpenAICompatibleProxyCompat,\n describeOptionalOpenAICompatibleProxyCompat,\n describeMissingDeepSeekCompat,\n isDeepSeekCompatCheckApplicable,\n describeMissingCacheCompatForModel,\n buildDeepSeekCompatSuggestion,\n buildDeepSeekCompatWarningText,\n buildSafeOpenAIProxyCompatSuggestion,\n getPromptCacheRetentionUnsupportedHint,\n isOfficialOpenAIBaseUrl,\n isCompatCheckApplicable,\n isPromptCacheRetention400Applicable,\n isSessionAffinity403Applicable,\n isOpenAISdkHeader403Applicable,\n hasPromptCacheRetentionUnsupportedSignal,\n hasPromptCacheRetentionUnsupportedErrorMessage,\n // Non-GPT OpenAI-compatible model detection\n isKimiLikeModel,\n isKimiLikeAssistantMessage,\n isQwenLikeModel,\n isQwenLikeAssistantMessage,\n isGLMLikeModel,\n isGLMLikeAssistantMessage,\n isMiniMaxLikeModel,\n isMiniMaxLikeAssistantMessage,\n isMimoLikeModel,\n isMimoLikeAssistantMessage,\n isHunyuanLikeModel,\n isHunyuanLikeAssistantMessage,\n // Additional OpenAI-compatible model detection\n isMistralLikeModel,\n isMistralLikeAssistantMessage,\n isGrokLikeModel,\n isGrokLikeAssistantMessage,\n isLlamaLikeModel,\n isLlamaLikeAssistantMessage,\n isNemotronLikeModel,\n isNemotronLikeAssistantMessage,\n isCohereLikeModel,\n isCohereLikeAssistantMessage,\n isYiLikeModel,\n isYiLikeAssistantMessage,\n // More OpenAI-compatible model detection (batch 2)\n isDoubaoLikeModel,\n isDoubaoLikeAssistantMessage,\n isErnieLikeModel,\n isErnieLikeAssistantMessage,\n isBaichuanLikeModel,\n isBaichuanLikeAssistantMessage,\n isStepFunLikeModel,\n isStepFunLikeAssistantMessage,\n isSparkLikeModel,\n isSparkLikeAssistantMessage,\n isInternLMLikeModel,\n isInternLMLikeAssistantMessage,\n isGemmaLikeModel,\n isGemmaLikeAssistantMessage,\n isPhiLikeModel,\n isPhiLikeAssistantMessage,\n isJambaLikeModel,\n isJambaLikeAssistantMessage,\n isSolarLikeModel,\n isSolarLikeAssistantMessage,\n // New OpenAI-compatible model detection (batch 3, 12 families)\n isPerplexityLikeModel,\n isPerplexityLikeAssistantMessage,\n isNovaLikeModel,\n isNovaLikeAssistantMessage,\n isRekaLikeModel,\n isRekaLikeAssistantMessage,\n isFalconLikeModel,\n isFalconLikeAssistantMessage,\n isDbrxLikeModel,\n isDbrxLikeAssistantMessage,\n isMptLikeModel,\n isMptLikeAssistantMessage,\n isStableLMLikeModel,\n isStableLMLikeAssistantMessage,\n isAquilaLikeModel,\n isAquilaLikeAssistantMessage,\n isExaoneLikeModel,\n isExaoneLikeAssistantMessage,\n isHyperCLOVALikeModel,\n isHyperCLOVALikeAssistantMessage,\n isLuminousLikeModel,\n isLuminousLikeAssistantMessage,\n isHermesLikeModel,\n isHermesLikeAssistantMessage,\n // More OpenAI-compatible model detection (batch 4, 18 families)\n isGraniteLikeModel,\n isGraniteLikeAssistantMessage,\n isArcticLikeModel,\n isArcticLikeAssistantMessage,\n isPanguLikeModel,\n isPanguLikeAssistantMessage,\n isSenseNovaLikeModel,\n isSenseNovaLikeAssistantMessage,\n isZhinaoLikeModel,\n isZhinaoLikeAssistantMessage,\n isMiniCPMLikeModel,\n isMiniCPMLikeAssistantMessage,\n isXVerseLikeModel,\n isXVerseLikeAssistantMessage,\n isOrionLikeModel,\n isOrionLikeAssistantMessage,\n isOpenChatLikeModel,\n isOpenChatLikeAssistantMessage,\n isVicunaLikeModel,\n isVicunaLikeAssistantMessage,\n isWizardLikeModel,\n isWizardLikeAssistantMessage,\n isZephyrLikeModel,\n isZephyrLikeAssistantMessage,\n isDolphinLikeModel,\n isDolphinLikeAssistantMessage,\n isOpenOrcaLikeModel,\n isOpenOrcaLikeAssistantMessage,\n isStarlingLikeModel,\n isStarlingLikeAssistantMessage,\n isBloomLikeModel,\n isBloomLikeAssistantMessage,\n isRwkvLikeModel,\n isRwkvLikeAssistantMessage,\n isAyaLikeModel,\n isAyaLikeAssistantMessage,\n selectAdapterForModel,\n selectAdapterForAssistantMessage,\n buildOpenAIProxyCompatWarningText,\n getModelIdNameTokenValues,\n getAssistantMessageModelTokenValues,\n getCompat,\n modelKey,\n modelFromAssistantMessage,\n consolidateDirectProviderStatsModel,\n // Platform-friendly path helpers\n getAgentDirDisplayPath,\n getModelsJsonDisplayPath,\n buildProviderCompatOverride,\n buildModelCompatOverride,\n captureCacheRetentionEnv,\n requestLongCacheRetention,\n restoreCacheRetentionEnv,\n setRuntimeOptimizerEnabled,\n isRuntimeOptimizerEnabled,\n getOptimizerRuntimeModeLines,\n formatOptimizerRuntimeMode,\n PI_CACHE_RETENTION_ENV,\n LONG_CACHE_RETENTION_VALUE,\n // Integrity diagnostics\n getLastPromptIntegrityWarningAt,\n // Diagnostic command helpers\n buildDoctorDiagnosis,\n buildCompatDiagnosis,\n describeRouterChannelDiagnostics,\n // Cache stats helpers (module-level, usable from verify script)\n addUsageToCacheStats,\n formatCacheStats,\n prefixFooterStatus,\n getCacheOptimizerArgumentCompletions,\n emptyCacheStats,\n emptyAllCacheStats,\n parseCacheStats,\n parsePersistedCacheStats,\n deriveTotalsByModelFromSessionStats,\n parsePersistedTotalsByModel,\n // Recent sample / stats output / diagnosis helpers\n MAX_RECENT_SAMPLES,\n buildStatsOutput,\n buildLowHitDiagnosis,\n formatRecentTrendSummary,\n formatHitRatio,\n formatTokenM,\n hasMissingUsageFields,\n keyForModelExt,\n // Session-scoped helpers\n hashSessionId,\n makeSessionModelKey,\n modelKeyFromSessionKey,\n filterRestorableStatsForSession,\n parsePersistedRoutedModelRef,\n routedModelRefToPiModel,\n buildExactRouterStatusEntry,\n findBestRouterModelStats,\n selectFooterStatsForModel,\n parseFooterStatsMode,\n parsePersistedCacheOptimizerConfig,\n readPersistedFooterMode,\n writePersistedFooterMode,\n resolveFooterStatsMode,\n footerStatsMode,\n CONFIG_FILE_PATH,\n FOOTER_MODE_ENV,\n // Routing-provider protocol helpers\n PI_ROUTING_REGISTRY_SYMBOL,\n PI_CACHE_HINTS_SYMBOL,\n ensureRoutingRegistry,\n getRoutingRegistry,\n parseRouteSnapshot,\n resolveActiveRouteSnapshot,\n routeSnapshotToPiModel,\n resolveRouteModel,\n isVirtualRoutingModel,\n installCacheHintsService,\n getCacheHintsService,\n markOptimizerOwnedCacheHintsService,\n isOptimizerOwnedCacheHintsService,\n getOrCaptureCacheRetentionBaseline,\n PI_CACHE_RETENTION_BASELINE_SYMBOL,\n // Persistence helpers (for reload/reset tests)\n mergeCacheSessions,\n mergeCacheTotals,\n mergeLastRoutedModels,\n createSerializedAsyncRunner,\n writePersistedCacheStats,\n readPersistedCacheStats,\n STATE_FILE_PATH,\n LEGACY_STATE_FILE_PATH,\n STATE_DIR,\n // JSONC surgical edit helpers\n MODELS_JSON_PATH,\n stripJsoncComments,\n stripJsoncTrailingCommas,\n parseJsonc,\n resolveExplicitCompatValue,\n hasExplicitLongRetentionOptInFromConfig,\n locateModelInJsonc,\n locateModelOverrideInJsonc,\n composeFixInsertion,\n selfCheckFix,\n analyzeModelsJsonForMissingEntry,\n composeMissingEntryInsertion,\n composeModelOverrideInsertion,\n selfCheckMissingEntryInsertion,\n decideFixPlacement,\n chooseFixPlacement,\n findExistingCompatKeysInJsonc,\n deepEqualIgnoringKeys,\n formatCompatKeysForInsertion,\n backupTimestamp,\n atomicReplaceTextFilePreservingMode,\n atomicRestoreFileFromBackup,\n applyModelsJsonFixTransaction,\n // Fix suggestion builder\n buildFixSuggestion,\n // Adaptive thinking compat helpers\n isAdaptiveGenerationModel,\n isKimiCodingAdaptiveModel,\n isKimiCodingEmptySignatureModel,\n isAdaptiveThinkingCompatApplicable,\n describeMissingAdaptiveThinkingCompat,\n buildAdaptiveThinkingCompatSuggestion,\n buildAdaptiveThinkingCompatWarningText,\n appendAdaptiveThinkingCompatAdviceLines,\n};\n\nexport default function (pi: ExtensionAPI) {\n const warnedModels = new Set<string>();\n const promptCacheRetention400Models = new Set<string>();\n const warnedPromptCacheRetention400Models = new Set<string>();\n const anthropicTtlFallbackState = getAnthropicTtlFallbackState();\n const anthropicTtlOrderErrorModels = anthropicTtlFallbackState.modelKeys;\n const warnedAnthropicTtlOrderErrorModels = anthropicTtlFallbackState.warnedModelKeys;\n const sendSessionAffinityHeaders403Models = new Set<string>();\n const warnedSendSessionAffinityHeaders403Models = new Set<string>();\n const openAISdkHeader403Models = new Set<string>();\n const warnedOpenAISdkHeader403Models = new Set<string>();\n let cacheStatsByModel: Record<string, CacheStats> = {};\n let cacheStatsProcessByModel: Record<string, CacheStats> = {};\n let cacheStatsTotalsByModel: Record<string, CacheStats> = {};\n let cacheStatsLegacyFamily: Partial<Record<CacheProviderId, CacheStats>> = emptyAllCacheStats();\n let lastStatusText: string | undefined;\n let persistenceWarningShown = false;\n let persistTimer: ReturnType<typeof setTimeout> | null = null;\n const enqueuePersist = createSerializedAsyncRunner();\n let replacePersistedTotalsOnNextWrite = false;\n let forceReplaceTotalsAfterFailure = false;\n const pendingDeletedTotalModelKeys = new Set<string>();\n let integrityNotificationShown = false;\n let currentSessionId = \"\";\n let currentSessionHash = \"\";\n let currentSessionHashSet = false;\n let lastActualRoutedModel: PersistedRoutedModelRef | undefined;\n let latestCacheHint: PiCacheHintSnapshot | undefined;\n const PERSIST_DEBOUNCE_MS = 2000;\n\n function buildObservedRuntimeFixSuggestion(model: PiModel): FixSuggestion | undefined {\n const key = modelKey(model);\n const slashIdx = key.indexOf(\"/\");\n const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;\n\n if (\n anthropicTtlOrderErrorModels.has(key) ||\n (isPromptCacheRetention400Applicable(model) && promptCacheRetention400Models.has(key))\n ) {\n return {\n providerLabel,\n modelId: model.id,\n compatKeys: { supportsLongCacheRetention: false },\n forceModelLevel: true,\n };\n }\n if (isSessionAffinity403Applicable(model) && sendSessionAffinityHeaders403Models.has(key)) {\n return { providerLabel, modelId: model.id, compatKeys: { sendSessionAffinityHeaders: false } };\n }\n return undefined;\n }\n\n function buildCommandFixSuggestion(model: PiModel): FixSuggestion | undefined {\n const regular = buildFixSuggestion(model);\n const observed = buildObservedRuntimeFixSuggestion(model);\n if (!regular) return observed;\n if (!observed) return regular;\n return {\n providerLabel: regular.providerLabel,\n modelId: regular.modelId,\n compatKeys: { ...regular.compatKeys, ...observed.compatKeys },\n forceModelLevel: regular.forceModelLevel || observed.forceModelLevel,\n };\n }\n /** In-memory recent usage samples per model key (not persisted, cleared on reload). */\n const recentSamplesByModelKey = new Map<string, CacheUsageSample[]>();\n\n function syncSessionHash(ctx: Pick<ExtensionContext, \"sessionManager\">): void {\n const sid = ctx.sessionManager.getSessionId();\n if (sid && (sid !== currentSessionId || !currentSessionHashSet)) {\n currentSessionId = sid;\n currentSessionHash = hashSessionId(sid);\n currentSessionHashSet = true;\n lastActualRoutedModel = undefined;\n }\n }\n\n const uninstallCacheHintsService = installCacheHintsService(markOptimizerOwnedCacheHintsService({\n version: 1,\n getHints(input: PiCacheHintsInput): PiCacheHintsOutput | undefined {\n if (!runtimeOptimizerEnabled || isEnabledEnv(process.env[NO_PROMPT_REWRITE_ENV])) return undefined;\n const hint = latestCacheHint;\n if (!hint) return undefined;\n if (input.sessionIdHash && hint.sessionIdHash && input.sessionIdHash !== hint.sessionIdHash) return undefined;\n if (input.virtualProvider && hint.virtualProvider && input.virtualProvider !== hint.virtualProvider) return undefined;\n if (input.virtualModelId && hint.virtualModelId && input.virtualModelId !== hint.virtualModelId) return undefined;\n if (input.upstreamProvider && hint.upstreamProvider && input.upstreamProvider !== hint.upstreamProvider) return undefined;\n if (input.upstreamModelId && hint.upstreamModelId && input.upstreamModelId !== hint.upstreamModelId) return undefined;\n if (input.api && hint.api && input.api !== hint.api) return undefined;\n\n return {\n systemPrompt: hint.systemPrompt,\n promptCacheKey: hint.promptCacheKey,\n cacheRetention: hint.cacheRetention,\n };\n },\n }), { discardPrevious: isOptimizerOwnedCacheHintsService });\n\n /**\n * Build a session-scoped stats key from the current session hash + model key.\n * Returns `${sessionHash}:${provider}/${id}`.\n */\n function sessionModelKey(model: { provider: string; id: string }): string {\n const hash = currentSessionHash || \"_nosession\";\n return `${hash}:${model.provider}/${model.id}`;\n }\n\n /**\n * Extract the user-facing model key from a session-scoped key.\n * \"abc123:otokapi/gpt-5.5\" → \"otokapi/gpt-5.5\"\n */\n function modelKeyFromSessionScoped(sKey: string): string {\n const idx = sKey.indexOf(\":\");\n return idx >= 0 ? sKey.slice(idx + 1) : sKey;\n }\n\n function recordRecentSample(modelKeyStr: string, usage: UsageSnapshot, missingUsageFields: boolean): void {\n let samples = recentSamplesByModelKey.get(modelKeyStr);\n if (!samples) {\n samples = [];\n recentSamplesByModelKey.set(modelKeyStr, samples);\n }\n samples.push({\n timestamp: Date.now(),\n hit: usage.cacheRead > 0,\n cachedInputTokens: usage.cacheRead,\n cacheWriteInputTokens: usage.cacheWrite,\n totalInputTokens: usage.totalInput,\n missingUsageFields,\n });\n if (samples.length > MAX_RECENT_SAMPLES) {\n samples.splice(0, samples.length - MAX_RECENT_SAMPLES);\n }\n }\n\n function getRecentSamples(modelKeyStr: string): CacheUsageSample[] {\n return recentSamplesByModelKey.get(modelKeyStr) ?? [];\n }\n\n function clearRecentSamples(): void {\n recentSamplesByModelKey.clear();\n }\n\n function getCacheStatsState(): CacheStatsState {\n return {\n statsByModel: cacheStatsByModel,\n totalsByModel: cacheStatsTotalsByModel,\n legacyFamily: cacheStatsLegacyFamily,\n ...(currentSessionHashSet && lastActualRoutedModel\n ? { lastRoutedModelBySession: { [currentSessionHash]: lastActualRoutedModel } }\n : {}),\n };\n }\n\n /** Look up visible cumulative stats for a model, falling back to legacy family. */\n function getStatsForModel(model: PiModel | undefined, adapter: CacheProviderAdapter): CacheStats {\n if (model) {\n const key = modelKey(model);\n const existing = cacheStatsTotalsByModel[key];\n if (existing) return existing;\n }\n\n // Fallback: legacy family bucket — used when model key is unknown.\n const family = cacheStatsLegacyFamily[adapter.id];\n if (family) return family;\n\n const created = emptyCacheStats();\n cacheStatsLegacyFamily[adapter.id] = created;\n return created;\n }\n\n /** Get or create a session-scoped stats entry for the given key. */\n function getOrCreateStatsByModelKey(key: string): CacheStats {\n const existing = cacheStatsByModel[key];\n if (existing) return existing;\n\n const created = emptyCacheStats();\n cacheStatsByModel[key] = created;\n return created;\n }\n\n /** Get or create the current-process provider/model stats entry. */\n function getOrCreateProcessStatsForModel(model: PiModel): CacheStats {\n const key = modelKey(model);\n const existing = cacheStatsProcessByModel[key];\n if (existing) return existing;\n\n const created = emptyCacheStats();\n cacheStatsProcessByModel[key] = created;\n return created;\n }\n\n /** Get or create the cumulative provider/model stats entry shown in the footer. */\n function getOrCreateTotalStatsForModel(model: PiModel): CacheStats {\n const key = modelKey(model);\n const existing = cacheStatsTotalsByModel[key];\n if (existing) return existing;\n\n const created = emptyCacheStats();\n cacheStatsTotalsByModel[key] = created;\n return created;\n }\n\n function resetStatsForModel(model: PiModel): void {\n const displayKey = modelKey(model);\n for (const key of Object.keys(cacheStatsByModel)) {\n if (modelKeyFromSessionScoped(key) === displayKey) delete cacheStatsByModel[key];\n }\n delete cacheStatsTotalsByModel[displayKey];\n delete cacheStatsProcessByModel[displayKey];\n pendingDeletedTotalModelKeys.add(displayKey);\n for (const key of Array.from(recentSamplesByModelKey.keys())) {\n if (modelKeyFromSessionScoped(key) === displayKey) recentSamplesByModelKey.delete(key);\n }\n lastStatusText = undefined;\n }\n\n function resetCurrentSessionStats(): void {\n const prefix = `${currentSessionHash || \"_nosession\"}:`;\n for (const key of Object.keys(cacheStatsByModel)) {\n if (key.startsWith(prefix)) delete cacheStatsByModel[key];\n }\n cacheStatsProcessByModel = {};\n cacheStatsTotalsByModel = {};\n replacePersistedTotalsOnNextWrite = true;\n for (const key of Array.from(recentSamplesByModelKey.keys())) {\n if (key.startsWith(prefix)) recentSamplesByModelKey.delete(key);\n }\n lastActualRoutedModel = undefined;\n lastStatusText = undefined;\n }\n\n function snapshotCacheStatsState(): CacheStatsState {\n const state = getCacheStatsState();\n const statsByModel = Object.fromEntries(\n Object.entries(state.statsByModel).map(([key, stats]) => [key, { ...stats }]),\n );\n const totalsByModel = Object.fromEntries(\n Object.entries(state.totalsByModel).map(([key, stats]) => [key, { ...stats }]),\n );\n const legacyFamily: Partial<Record<CacheProviderId, CacheStats>> = {};\n for (const id of CACHE_PROVIDER_IDS) {\n const stats = state.legacyFamily[id];\n if (stats) legacyFamily[id] = { ...stats };\n }\n const lastRoutedModelBySession = state.lastRoutedModelBySession\n ? Object.fromEntries(\n Object.entries(state.lastRoutedModelBySession).map(([key, model]) => [key, { ...model }]),\n )\n : undefined;\n return { statsByModel, totalsByModel, legacyFamily, lastRoutedModelBySession };\n }\n\n function persistCacheStats(ctx?: ExtensionContext): Promise<void> {\n const state = snapshotCacheStatsState();\n const sessionHash = currentSessionHashSet ? currentSessionHash : undefined;\n const deleteModelKeys = Array.from(pendingDeletedTotalModelKeys);\n const replaceTotals = replacePersistedTotalsOnNextWrite;\n // This queued write owns the current intents. Later mutations can enqueue\n // independent intents without an earlier completion clearing them.\n pendingDeletedTotalModelKeys.clear();\n replacePersistedTotalsOnNextWrite = false;\n\n return enqueuePersist(async () => {\n const recoverFromEarlierFailure = forceReplaceTotalsAfterFailure;\n try {\n await writePersistedCacheStats(state, sessionHash, {\n deleteModelKeys,\n replaceTotals: replaceTotals || recoverFromEarlierFailure,\n });\n if (recoverFromEarlierFailure) forceReplaceTotalsAfterFailure = false;\n } catch (error) {\n // The next queued write must replace totals authoritatively so a failed\n // reset/delete cannot merge stale persisted counters back into state.\n forceReplaceTotalsAfterFailure = true;\n console.warn(`${LOG_PREFIX}: failed to persist cache stats`, error);\n if (!persistenceWarningShown) {\n persistenceWarningShown = true;\n ctx?.ui.notify(\n `${LOG_PREFIX}: failed to persist footer stats; using in-memory stats for this process.`,\n \"warning\",\n );\n }\n }\n });\n }\n\n /** Schedule a debounced persist. Coalesces rapid message_end writes\n * into a single disk write after PERSIST_DEBOUNCE_MS of silence.\n * In-memory stats remain instantly up-to-date for the footer; only\n * the on-disk persistence is delayed. */\n function schedulePersistCacheStats(ctx?: ExtensionContext): void {\n if (persistTimer !== null) clearTimeout(persistTimer);\n persistTimer = setTimeout(() => {\n persistTimer = null;\n persistCacheStats(ctx).catch((err) => {\n console.warn(`${LOG_PREFIX}: debounced persist failed`, err);\n });\n }, PERSIST_DEBOUNCE_MS);\n }\n\n /** Flush any pending debounced persist immediately (cancels timer + writes).\n * Used on reload and day-rollover where immediate durability matters. */\n async function flushPersistCacheStats(ctx?: ExtensionContext): Promise<void> {\n if (persistTimer !== null) {\n clearTimeout(persistTimer);\n persistTimer = null;\n }\n await persistCacheStats(ctx);\n }\n\n async function rollOverStatsIfNeeded(ctx?: ExtensionContext): Promise<void> {\n const day = currentLocalDay();\n let changed = false;\n\n // Roll over per-model entries.\n for (const key of Object.keys(cacheStatsByModel)) {\n const stats = cacheStatsByModel[key];\n if (stats && stats.day !== day) {\n cacheStatsByModel[key] = emptyCacheStats(day);\n changed = true;\n }\n }\n\n // Roll over cumulative provider/model totals.\n for (const key of Object.keys(cacheStatsProcessByModel)) {\n const stats = cacheStatsProcessByModel[key];\n if (stats && stats.day !== day) {\n cacheStatsProcessByModel[key] = emptyCacheStats(day);\n changed = true;\n }\n }\n\n for (const key of Object.keys(cacheStatsTotalsByModel)) {\n const stats = cacheStatsTotalsByModel[key];\n if (stats && stats.day !== day) {\n cacheStatsTotalsByModel[key] = emptyCacheStats(day);\n changed = true;\n }\n }\n\n // Roll over legacy family entries.\n for (const id of CACHE_PROVIDER_IDS) {\n const stats = cacheStatsLegacyFamily[id];\n if (stats && stats.day !== day) {\n cacheStatsLegacyFamily[id] = emptyCacheStats(day);\n changed = true;\n }\n }\n\n if (changed) {\n lastStatusText = undefined;\n await persistCacheStats(ctx);\n }\n }\n\n async function restoreCacheStats(reason: string, ctx: ExtensionContext): Promise<void> {\n syncSessionHash(ctx);\n\n if (reason === \"reload\") {\n // /reload: preserve session-scoped stats (same session hash).\n // Pi extension reload creates a fresh closure, so cacheStatsByModel\n // starts empty. Read persisted data and filter for current session.\n lastStatusText = undefined;\n lastPromptIntegrityWarningAt = 0;\n integrityNotificationShown = false;\n clearRecentSamples();\n\n const persisted = await readPersistedCacheStats();\n cacheStatsByModel = filterRestorableStatsForSession(\n persisted,\n currentSessionHashSet ? currentSessionHash : undefined,\n );\n cacheStatsProcessByModel = {};\n cacheStatsTotalsByModel = persisted?.totalsByModel ?? {};\n cacheStatsLegacyFamily = persisted?.legacyFamily ?? emptyAllCacheStats();\n lastActualRoutedModel = currentSessionHashSet\n ? persisted?.lastRoutedModelBySession?.[currentSessionHash]\n : undefined;\n\n await rollOverStatsIfNeeded(ctx);\n return;\n }\n\n // First load / process start: read persisted stats and filter for\n // this session's entries. If the session hash is unavailable, start\n // fresh instead of loading all persisted session buckets.\n const persisted = await readPersistedCacheStats();\n cacheStatsByModel = filterRestorableStatsForSession(\n persisted,\n currentSessionHashSet ? currentSessionHash : undefined,\n );\n cacheStatsProcessByModel = {};\n cacheStatsTotalsByModel = persisted?.totalsByModel ?? {};\n cacheStatsLegacyFamily = persisted?.legacyFamily ?? emptyAllCacheStats();\n lastActualRoutedModel = currentSessionHashSet\n ? persisted?.lastRoutedModelBySession?.[currentSessionHash]\n : undefined;\n lastStatusText = undefined;\n await rollOverStatsIfNeeded(ctx);\n }\n\n async function publishStatus(ctx: ExtensionContext, model: PiModel | undefined = ctx.model): Promise<void> {\n syncSessionHash(ctx);\n await rollOverStatsIfNeeded(ctx);\n\n const routedModel = resolveRouteModel(model, ctx);\n const displayModel = routedModel ?? model;\n const adapter = selectAdapterForModel(displayModel);\n const activeIsVirtualRoute = !!routedModel || isVirtualRoutingModel(model, ctx);\n let statusText: string | undefined;\n const mode = footerStatsMode();\n const sessionHash = currentSessionHashSet ? currentSessionHash : undefined;\n\n if (!adapter && !routedModel && activeIsVirtualRoute) {\n // On model_select (existing footer), keep the existing cache footer\n // visible instead of clearing it. On session_start (no footer yet\n // after reload/fresh start), restore the exact last actual routed model\n // for this session when available; fall back to older best-effort\n // heuristics only when no exact metadata exists.\n if (lastStatusText !== undefined) return;\n const realEntry = buildExactRouterStatusEntry(\n sessionHash,\n cacheStatsByModel,\n lastActualRoutedModel,\n cacheStatsTotalsByModel,\n mode,\n cacheStatsProcessByModel,\n ) ?? findBestRouterModelStats(\n mode,\n sessionHash,\n cacheStatsByModel,\n cacheStatsTotalsByModel,\n cacheStatsProcessByModel,\n );\n if (realEntry) {\n const statsText = formatCacheStats(realEntry.adapter, realEntry.stats);\n statusText = runtimeOptimizerEnabled\n ? statsText\n : `Cache Optimizer disabled · ${statsText}`;\n }\n }\n\n if (adapter) {\n // Footer mode defaults to daily provider/model totals. Users may select\n // current-session counters through persistent command config or the env var.\n const stats = displayModel\n ? selectFooterStatsForModel(mode, sessionHash, cacheStatsByModel, cacheStatsTotalsByModel, displayModel, cacheStatsProcessByModel)\n : undefined;\n const statsText = formatCacheStats(adapter, stats ?? emptyCacheStats());\n statusText = runtimeOptimizerEnabled ? statsText : `Cache Optimizer disabled · ${statsText}`;\n }\n\n // If optimizeSystemPrompt detected structural truncation on this or\n // a recent turn, flag it once in the footer so the user knows to\n // /reload before continuing. The flag resets after emission so a\n // single-turn glitch does not permanently taint the footer.\n if (promptTruncationDetected && statusText !== undefined) {\n statusText = statusText + \" ⚠️ integrity\";\n promptTruncationDetected = false;\n lastPromptIntegrityWarningAt = Date.now();\n\n // One-time notification with recovery steps (per session).\n if (!integrityNotificationShown) {\n integrityNotificationShown = true;\n ctx.ui.notify(\n `⚠️ ${LOG_PREFIX}: A prompt structural marker was lost during reorder on this turn. ` +\n `The original prompt was used instead to preserve integrity.\\n\\n` +\n `Recovery steps:\\n` +\n `1. Run /reload to reset (may clear transient issues).\\n` +\n `2. Set PI_CACHE_OPTIMIZER_NO_PROMPT_REWRITE=1 and /reload to disable reorder.\\n` +\n `3. If persistent, run /cache-optimizer doctor and file an issue (no API keys/prompts).`,\n \"warning\",\n );\n }\n }\n\n // ⚠️ compat footer marker: if the active model has adapter-specific\n // missing compat (DeepSeek reasoning/cache compat, or a non-official\n // openai-completions model missing cache/session-affinity flags), append\n // the marker to indicate that compat configuration is incomplete.\n // Re-evaluated on every status update so the marker persists through stats\n // changes and day rollovers. Redundant setStatus calls are blocked by the\n // `lastStatusText` early return above.\n if (runtimeOptimizerEnabled && statusText !== undefined && displayModel) {\n // Only show ⚠️ compat when there are safe-fixable missing compat keys.\n // Optional/advisory-only flags (e.g. supportsLongCacheRetention on generic\n // OpenAI-compatible proxies) do NOT trigger the marker — the doctor/compat\n // commands still mention them as optional guidance.\n if (buildFixSuggestion(displayModel) !== undefined) {\n statusText = statusText + \" ⚠️ compat\";\n }\n }\n\n statusText = prefixFooterStatus(statusText);\n if (statusText === lastStatusText) return;\n\n lastStatusText = statusText;\n ctx.ui.setStatus(STATUS_KEY, statusText);\n }\n\n ensureRoutingRegistry();\n\n /**\n * Check whether a model has an EXPLICIT supportsLongCacheRetention: true\n * opt-in in models.json. Precedence mirrors Pi's effective model config:\n * modelOverrides[model.id].compat, then models[].compat, then provider.compat.\n *\n * Returns true ONLY when the user explicitly opted in. Returns false for:\n * - Explicit false (opt-out)\n * - In models.json but field absent (Pi defaults to true — unsafe)\n * - Not in models.json at all (API-logged-in providers)\n * - File missing/unreadable\n *\n * The caller strips prompt_cache_retention when this returns false.\n */\n function hasExplicitLongRetentionOptIn(model: PiModel): boolean {\n try {\n const text = readFileSync(MODELS_JSON_PATH, \"utf8\");\n const parsed = parseJsonc(text);\n return hasExplicitLongRetentionOptInFromConfig(parsed, model.provider, model.id);\n } catch {\n return false;\n }\n }\n\n pi.on(\"session_start\", async (event, ctx) => {\n if (runtimeOptimizerEnabled) requestLongCacheRetention();\n await restoreCacheStats(event.reason, ctx);\n await publishStatus(ctx);\n });\n\n pi.on(\"session_shutdown\", async (_event, ctx) => {\n try {\n await flushPersistCacheStats(ctx);\n } finally {\n latestCacheHint = undefined;\n delete getProtocolGlobal().__piCacheOptimizerCacheKey__;\n uninstallCacheHintsService();\n restoreCacheRetentionEnv(STARTUP_CACHE_RETENTION_ENV);\n }\n });\n\n pi.on(\"model_select\", async (event, ctx) => {\n if (runtimeOptimizerEnabled) notifyCacheCompatIfNeeded(resolveRouteModel(event.model, ctx) ?? event.model, ctx, warnedModels);\n await publishStatus(ctx, event.model);\n });\n\n pi.on(\"before_agent_start\", async (event, _ctx) => {\n latestCacheHint = undefined;\n // Clear the legacy global before any bypass/disable early return. A valid\n // rewrite path republishes the current session key below; otherwise callers\n // must not observe a stale key from the previously selected model/route.\n delete getProtocolGlobal().__piCacheOptimizerCacheKey__;\n const routeSnapshot = resolveActiveRouteSnapshot(_ctx.model, _ctx);\n const routedModel = routeSnapshot\n ? findModelInRegistry(_ctx.modelRegistry, routeSnapshot.provider, routeSnapshot.modelId) ?? routeSnapshotToPiModel(routeSnapshot, _ctx.model)\n : undefined;\n\n // ────────────────────────────────────────────────────────────────\n // OpenAI Responses-family bypass (codex-responses + responses + azure responses)\n //\n // OpenAI's Responses API endpoints — both the Codex backend\n // (openai-codex-responses, chatgpt.com) and the public\n // Responses API (openai-responses, api.openai.com / Copilot) —\n // have two properties that make client-side prompt reordering\n // unnecessary and potentially harmful:\n //\n // 1. Server-managed caching: both APIs send `prompt_cache_key`\n // (= Pi session id) in every request body, so the server\n // already maintains a stable cache without prefix ordering.\n // Client-side reordering adds no cache benefit.\n //\n // 2. Stricter content-safety filtering: the Codex backend in\n // particular has a product-level safety filter that flags\n // reordered prompts (tool snippets / guidelines lifted above\n // the assistant role) as potential prompt-injection, returning\n // `content_filter` and blocking tool calls (notably\n // `subagent`). The public Responses API shares the same\n // filter framework and could behave similarly.\n //\n // We therefore skip ALL prompt modifications (churn strip, skill\n // compression, reorder) for these APIs. Third-party providers\n // that use openai-completions are unaffected.\n // ────────────────────────────────────────────────────────────────\n const model = routedModel ?? _ctx.model;\n if (model && isResponsesPromptRewriteBypassApi(model.api)) {\n return {};\n }\n\n if (!runtimeOptimizerEnabled) return {};\n\n // Global opt-out: PI_CACHE_OPTIMIZER_NO_PROMPT_REWRITE=1 bypasses all\n // prompt mutations below (session-overview churn strip, skill compression,\n // and stable-prefix reordering). Footer stats and the OpenAI\n // prompt_cache_key fallback remain active.\n if (isEnabledEnv(process.env[NO_PROMPT_REWRITE_ENV])) {\n return {};\n }\n\n // Step 1: strip per-turn churn from <session-overview>.\n // Removing RECENT COMMITS, Working directory status, and\n // Journal line count makes more of the session-overview stable\n // across turns, which DeepSeek's prefix cache can then retain.\n const strippedPrompt = stripSessionOverviewChurn(event.systemPrompt);\n\n // Step 2: compress skills XML → one-line index.\n // The compressed form is identical-string-equivalent to the\n // verbose one as far as cache-stability is concerned because both\n // are deterministic from the same `event.systemPromptOptions.skills`.\n // No-op if opted out, below SKILL_COMPRESSION_MIN_COUNT, or if pi\n // emitted a format we don't recognize.\n const compressedPrompt = compressSkillsInSystemPrompt(\n strippedPrompt,\n event.systemPromptOptions,\n );\n\n // Step 3: lift stable content above dynamic content for cache\n // stability. Operates on the (stripped + compressed) prompt so the\n // cache key derived from `stablePrefix` reflects what actually\n // ships to the provider.\n const optimized = optimizeSystemPrompt(compressedPrompt, event.systemPromptOptions);\n\n const promptCacheKey = getSessionPromptCacheKey(_ctx);\n const cacheRetention = process.env[PI_CACHE_RETENTION_ENV] === LONG_CACHE_RETENTION_VALUE ? LONG_CACHE_RETENTION_VALUE : undefined;\n const publishHint = (systemPrompt: string): void => {\n latestCacheHint = {\n sessionIdHash: currentSessionHashSet ? currentSessionHash : sessionHashFromContext(_ctx),\n virtualProvider: routeSnapshot?.virtualProvider ?? _ctx.model?.provider,\n virtualModelId: routeSnapshot?.virtualModelId ?? _ctx.model?.id,\n upstreamProvider: routeSnapshot?.provider ?? model?.provider,\n upstreamModelId: routeSnapshot?.modelId ?? model?.id,\n api: model?.api,\n systemPrompt,\n promptCacheKey,\n cacheRetention,\n timestamp: Date.now(),\n };\n const globals = getProtocolGlobal();\n if (promptCacheKey) {\n globals.__piCacheOptimizerCacheKey__ = promptCacheKey;\n } else {\n delete globals.__piCacheOptimizerCacheKey__;\n }\n };\n\n if (optimized.changed && optimized.systemPrompt.trim().length > 0) {\n publishHint(optimized.systemPrompt);\n return { systemPrompt: optimized.systemPrompt };\n }\n\n // Reorder didn't apply but compression might have. Return the\n // compressed (or stripped) prompt directly so we still benefit from\n // the volume cut even when reorder is a no-op (e.g., short sessions\n // where no stable candidate is long enough).\n if (compressedPrompt !== strippedPrompt && compressedPrompt.trim().length > 0) {\n publishHint(compressedPrompt);\n return { systemPrompt: compressedPrompt };\n }\n if (strippedPrompt !== event.systemPrompt && strippedPrompt.trim().length > 0) {\n publishHint(strippedPrompt);\n return { systemPrompt: strippedPrompt };\n }\n\n publishHint(event.systemPrompt);\n return {};\n });\n\n pi.on(\"before_provider_request\", (event, ctx) => {\n const requestModel = resolveRouteModel(ctx.model, ctx) ?? ctx.model;\n\n // Anthropic rejects mixed cache breakpoints when a 1h block appears after\n // a 5m/default block in wire order (tools → system → messages). Repair any\n // conflict visible in Pi's final payload immediately. Some proxies inject\n // hidden short breakpoints after this hook; only models that have actually\n // returned the explicit TTL-order error receive the process-local 5m fallback.\n if (requestModel && isAnthropicMessagesApi(requestModel.api)) {\n const visibleConflictFixed = normalizeAnthropicCacheControlTtlOrder(event.payload);\n if (!visibleConflictFixed && anthropicTtlOrderErrorModels.has(modelKey(requestModel))) {\n downgradeAnthropicLongCacheControls(event.payload);\n }\n }\n\n // ── Safety: strip prompt_cache_retention from payload for models that\n // are not authorised to send it. Pi defaults supportsLongCacheRetention\n // to true for all openai-completions models, but most third-party APIs\n // reject the parameter with 400 “Extra inputs are not permitted”.\n //\n // Gate order (first match wins):\n // 1. Official OpenAI → keep (trusted to support it)\n // 2. 400 history → strip (empirical evidence overrides user config)\n // 3. Explicit opt-in in models.json → keep (user explicitly wants it)\n // 4. Everything else → strip (safe default for third-party APIs)\n //\n // Gate 2 before Gate 3 is critical: if a user explicitly opted in but\n // the API returned 400, we must strip — otherwise the 400 repeats forever.\n if (runtimeOptimizerEnabled) {\n const payload = event.payload as UnknownRecord;\n if (payload && typeof payload.prompt_cache_retention === 'string') {\n if (requestModel) {\n if (isOfficialOpenAIBaseUrl(requestModel)) {\n // Gate 1: Official OpenAI → keep\n } else if (promptCacheRetention400Models.has(modelKey(requestModel))) {\n // Gate 2: 400 history → strip (overrides user opt-in)\n delete payload.prompt_cache_retention;\n } else if (hasExplicitLongRetentionOptIn(requestModel)) {\n // Gate 3: Explicit user opt-in → keep\n } else {\n // Gate 4: Safe default → strip\n delete payload.prompt_cache_retention;\n }\n }\n }\n }\n\n if (!shouldInjectOpenAIPromptCacheKey()) return undefined;\n if (!shouldInjectOpenAIPromptCacheKeyForModel(requestModel)) return undefined;\n\n return addOpenAIPromptCacheKey(event.payload, getSessionPromptCacheKey(ctx));\n });\n\n pi.on(\"after_provider_response\", (event, ctx) => {\n const model = resolveRouteModel(ctx.model, ctx) ?? ctx.model;\n if (!runtimeOptimizerEnabled || !model) return;\n\n // ── 400: prompt_cache_retention unsupported ──\n if (event.status === 400) {\n if (!isPromptCacheRetention400Applicable(model)) return;\n if (!hasPromptCacheRetentionUnsupportedSignal(event.headers)) return;\n\n const key = modelKey(model);\n promptCacheRetention400Models.add(key);\n if (warnedPromptCacheRetention400Models.has(key)) return;\n warnedPromptCacheRetention400Models.add(key);\n ctx.ui.notify(\n `⚠️ ${LOG_PREFIX}: ${key} returned HTTP 400 while supportsLongCacheRetention is enabled. ` +\n getPromptCacheRetentionUnsupportedHint() +\n ` Run /cache-optimizer doctor for the exact edit location.`,\n \"warning\",\n );\n return;\n }\n\n // ── 403: proxy/CDN/WAF blocking ──\n if (event.status === 403) {\n const key403 = modelKey(model);\n\n // Case 1: Pi's custom session-affinity headers are enabled. Offer the\n // existing safe compat fix (`sendSessionAffinityHeaders: false`).\n if (isSessionAffinity403Applicable(model)) {\n sendSessionAffinityHeaders403Models.add(key403);\n if (warnedSendSessionAffinityHeaders403Models.has(key403)) return;\n warnedSendSessionAffinityHeaders403Models.add(key403);\n ctx.ui.notify(\n `⚠️ ${LOG_PREFIX}: ${key403} returned HTTP 403 while sendSessionAffinityHeaders is enabled. ` +\n `The proxy/CDN may be blocking Pi's custom session-affinity headers (session_id, x-client-request-id, x-session-affinity). ` +\n `Run /cache-optimizer doctor for details and /cache-optimizer fix to set sendSessionAffinityHeaders: false.`,\n \"warning\",\n );\n return;\n }\n\n // Case 2: session-affinity headers are already absent/disabled, but the\n // provider still returns 403. Some CDNs/WAFs block the OpenAI JS SDK\n // default request fingerprint (User-Agent: OpenAI/JS ... or\n // X-Stainless-* headers). This is provider-specific; do NOT auto-fix by\n // writing a User-Agent because the right value depends on the endpoint.\n if (isOpenAISdkHeader403Applicable(model)) {\n openAISdkHeader403Models.add(key403);\n if (warnedOpenAISdkHeader403Models.has(key403)) return;\n warnedOpenAISdkHeader403Models.add(key403);\n ctx.ui.notify(\n `⚠️ ${LOG_PREFIX}: ${key403} returned HTTP 403 even though sendSessionAffinityHeaders is not enabled. ` +\n `The proxy/CDN may be blocking the OpenAI JS SDK User-Agent / X-Stainless-* headers. ` +\n `Run /cache-optimizer doctor for manual diagnostic guidance; /cache-optimizer fix will not auto-write User-Agent headers.`,\n \"warning\",\n );\n return;\n }\n }\n });\n\n pi.on(\"message_end\", async (event, ctx) => {\n syncSessionHash(ctx);\n const msgRecord = asRecord(event.message);\n\n // Some providers expose an HTTP 400 error body only through the finalized\n // assistant error message; after_provider_response may contain the status\n // and no diagnostic response headers. Record the same model-scoped\n // prompt_cache_retention fallback from that authoritative message identity.\n if (runtimeOptimizerEnabled && hasPromptCacheRetentionUnsupportedErrorMessage(event.message)) {\n const fallbackModel = resolveRouteModel(ctx.model, ctx) ?? ctx.model;\n const messageModel = modelFromAssistantMessage(event.message, fallbackModel) ?? fallbackModel;\n const errorModel = messageModel\n ? findModelInRegistry(ctx.modelRegistry, messageModel.provider, messageModel.id) ?? messageModel\n : undefined;\n if (errorModel && isExplicitPromptCacheRetentionUnsupportedApplicable(errorModel)) {\n const key = modelKey(errorModel);\n promptCacheRetention400Models.add(key);\n if (!warnedPromptCacheRetention400Models.has(key)) {\n warnedPromptCacheRetention400Models.add(key);\n ctx.ui.notify(\n `⚠️ ${LOG_PREFIX}: ${key} rejected prompt_cache_retention. ` +\n getPromptCacheRetentionUnsupportedHint() +\n ` Run /cache-optimizer doctor for the exact edit location.`,\n \"warning\",\n );\n }\n }\n }\n\n // Record only Anthropic's explicit mixed-TTL ordering error. This is a\n // non-retryable 400 in Pi 0.82.1, so the fallback applies to the next\n // subsequent request (and to a retry only if another layer initiates one).\n if (hasAnthropicCacheTtlOrderError(event.message)) {\n const fallbackModel = resolveRouteModel(ctx.model, ctx) ?? ctx.model;\n const errorModel = modelFromAssistantMessage(event.message, fallbackModel) ?? fallbackModel;\n if (errorModel && isAnthropicMessagesApi(errorModel.api)) {\n const key = modelKey(errorModel);\n anthropicTtlOrderErrorModels.add(key);\n if (!warnedAnthropicTtlOrderErrorModels.has(key)) {\n warnedAnthropicTtlOrderErrorModels.add(key);\n ctx.ui.notify(\n `⚠️ ${LOG_PREFIX}: ${key} returned an Anthropic cache-control TTL ordering error. ` +\n `The next request will fall back to the default 5-minute cache TTL. ` +\n `Run /cache-optimizer fix to set model-level supportsLongCacheRetention: false persistently.`,\n \"warning\",\n );\n }\n }\n }\n\n const adapter = selectAdapterForAssistantMessage(event.message, ctx.model);\n if (!adapter) return;\n\n // Skip stats for error/aborted messages (network retries, user aborts).\n // Pi's auto-retry emits message_end for the failed attempt before\n // removing it and retrying. The error message carries zero-usage fields\n // ({ input: 0, cacheRead: 0, cacheWrite: 0 }) that normalizeUsage\n // returns as a valid (non-undefined) snapshot, which would inflate\n // totalRequests and skew cache hit-rate accuracy. The final successful\n // response is emitted as a separate message_end with real usage data.\n if (msgRecord?.stopReason === \"error\" || msgRecord?.stopReason === \"aborted\") {\n return;\n }\n\n const usage = adapter.normalizeUsage(event.message);\n\n // Completed message metadata is request-local and authoritative for virtual\n // routing providers. Use it whenever it supplies provider/model identity;\n // fall back to the active context model for direct providers.\n let statsModel = modelFromAssistantMessage(event.message, ctx.model) ?? ctx.model;\n // For direct (non-virtual-routing) providers, the upstream API may echo a\n // normalized/renamed model id in its response (e.g. request\n // `zai-org/GLM-5.2-FP8` but the message carries `GLM5.2-FP8`). Writing\n // stats under the echoed name fragments the bucket away from the\n // active-model key the footer reads, showing 0% even when the backend is\n // hitting cache. When the response model drifts from the active model only\n // in name (same provider, same cache adapter), consolidate stats back to\n // the active model id. Virtual routing providers keep message-local\n // identity (router correctness).\n statsModel = consolidateDirectProviderStatsModel(statsModel, ctx.model, ctx);\n let routedModelChanged = false;\n if (isVirtualRoutingModel(ctx.model, ctx) && statsModel && !isVirtualRoutingModel(statsModel, ctx)) {\n const nextRoutedModel: PersistedRoutedModelRef = {\n provider: statsModel.provider,\n id: statsModel.id,\n name: statsModel.name || statsModel.id,\n };\n if (\n !lastActualRoutedModel ||\n lastActualRoutedModel.provider !== nextRoutedModel.provider ||\n lastActualRoutedModel.id !== nextRoutedModel.id ||\n (lastActualRoutedModel.name || lastActualRoutedModel.id) !== (nextRoutedModel.name || nextRoutedModel.id)\n ) {\n lastActualRoutedModel = nextRoutedModel;\n routedModelChanged = true;\n }\n }\n\n // Record recent sample (even when usage is missing, for trend diagnosis)\n if (statsModel) {\n const sk = sessionModelKey(statsModel);\n const missingFields = usage === undefined || (usage.cacheRead === 0 && usage.cacheWrite === 0 && usage.totalInput === 0)\n ? true\n : hasMissingUsageFields(event.message, adapter);\n recordRecentSample(sk, usage ?? { cacheRead: 0, cacheWrite: 0, totalInput: 0 }, missingFields);\n }\n\n if (!usage) {\n if (routedModelChanged) schedulePersistCacheStats(ctx);\n return;\n }\n\n await rollOverStatsIfNeeded(ctx);\n\n // Update session, process-local, and cumulative buckets for the actual\n // routed model. The process bucket is intentionally never persisted.\n if (statsModel) {\n const sk = sessionModelKey(statsModel);\n addUsageToCacheStats(getOrCreateStatsByModelKey(sk), usage);\n addUsageToCacheStats(getOrCreateProcessStatsForModel(statsModel), usage);\n addUsageToCacheStats(getOrCreateTotalStatsForModel(statsModel), usage);\n } else {\n addUsageToCacheStats(getStatsForModel(undefined, adapter), usage);\n }\n\n schedulePersistCacheStats(ctx);\n await publishStatus(ctx, statsModel);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Register /cache-optimizer command\n // Subcommands:\n // enable — enable runtime prompt/cache optimizations for this process\n // disable — disable runtime prompt/cache optimizations for this process\n // doctor — show current model/provider/api/baseUrl/compat status\n // with low-hit diagnosis\n // stats — show active model stats bucket, recent trend, usage\n // compat — show compat suggestion with file path\n // config footer-mode total|session|process — persist footer mode override\n // fix — auto-fix compat issues (writes models.json, requires UI)\n // reset — reset current provider/model footer stats bucket (local only)\n // (no args) — interactive menu (with UI) or help summary\n // ────────────────────────────────────────────────────────────────\n async function handleCacheOptimizerCommand(\n args: string,\n cmdCtx: ExtensionCommandContext,\n ): Promise<void> {\n syncSessionHash(cmdCtx);\n const selectedModel = cmdCtx.model;\n const model = resolveRouteModel(selectedModel, cmdCtx) ?? selectedModel;\n const commandParts = args.trim().toLowerCase().split(/\\s+/).filter(Boolean);\n const subcommand = commandParts[0] || \"help\";\n\n if (subcommand === \"enable\") {\n setRuntimeOptimizerEnabled(true);\n resetCurrentSessionStats();\n await flushPersistCacheStats(cmdCtx);\n await publishStatus(cmdCtx, model);\n cmdCtx.ui.notify(`✅ Pi Cache Optimizer enabled for this Pi process. Local footer stats were reset for before/after comparison.\\n${formatOptimizerRuntimeMode()}`, \"info\");\n } else if (subcommand === \"disable\") {\n setRuntimeOptimizerEnabled(false);\n resetCurrentSessionStats();\n await flushPersistCacheStats(cmdCtx);\n await publishStatus(cmdCtx, model);\n cmdCtx.ui.notify(`⏸️ Pi Cache Optimizer disabled for this Pi process. Local footer stats were reset and will keep collecting while disabled for comparison.\\n${formatOptimizerRuntimeMode()}`, \"warning\");\n } else if (subcommand === \"doctor\") {\n if (!model) {\n cmdCtx.ui.notify(\"No active model selected. Select a model first with /model or pi --model.\", \"warning\");\n return;\n }\n const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)), anthropicTtlOrderError: anthropicTtlOrderErrorModels.has(modelKey(model)), sessionAffinity403: sendSessionAffinityHeaders403Models.has(modelKey(model)), openAISdkHeader403: openAISdkHeader403Models.has(modelKey(model)) });\n const adapter = selectAdapterForModel(model);\n const sk = model ? sessionModelKey(model) : undefined;\n const statsState = model ? cacheStatsTotalsByModel[modelKey(model)] : undefined;\n const samples = sk ? getRecentSamples(sk) : [];\n const lowHitLines = buildLowHitDiagnosis(model, adapter, statsState, samples);\n const fullDiagnosis = lowHitLines.length > 0\n ? diagnosis + \"\\n\" + lowHitLines.join(\"\\n\")\n : diagnosis;\n cmdCtx.ui.notify(fullDiagnosis, \"info\");\n } else if (subcommand === \"stats\") {\n if (!model) {\n cmdCtx.ui.notify(\"No active model selected. Select a model first with /model or pi --model.\", \"warning\");\n return;\n }\n const adapter = selectAdapterForModel(model);\n const sk = model ? sessionModelKey(model) : undefined;\n const statsState = model ? cacheStatsTotalsByModel[modelKey(model)] : undefined;\n const samples = sk ? getRecentSamples(sk) : [];\n const output = buildStatsOutput(model, adapter, statsState, samples);\n cmdCtx.ui.notify(output, \"info\");\n } else if (subcommand === \"config\") {\n const configKey = commandParts[1];\n const requestedMode = commandParts[2];\n if (configKey !== \"footer-mode\" || !requestedMode || ![\"session\", \"total\", \"process\"].includes(requestedMode)) {\n const resolved = resolveFooterStatsMode(persistedFooterStatsMode);\n cmdCtx.ui.notify(\n `Usage: /cache-optimizer config footer-mode total|session|process\\n` +\n `Current footer mode: ${resolved.mode} (${resolved.source})`,\n \"info\",\n );\n return;\n }\n\n const nextMode = requestedMode as FooterStatsMode;\n try {\n await writePersistedFooterMode(nextMode);\n persistedFooterStatsMode = nextMode;\n lastStatusText = undefined;\n await publishStatus(cmdCtx, model);\n const resolved = resolveFooterStatsMode(persistedFooterStatsMode);\n cmdCtx.ui.notify(\n `✅ Footer mode set to ${resolved.mode}. Persistent config overrides ${FOOTER_MODE_ENV}.`,\n \"info\",\n );\n } catch (error) {\n cmdCtx.ui.notify(\n `❌ Could not update footer mode config: ${error instanceof Error ? error.message : String(error)}`,\n \"error\",\n );\n }\n } else if (subcommand === \"compat\") {\n if (!model) {\n cmdCtx.ui.notify(\"No active model selected. Select a model first with /model or pi --model.\", \"warning\");\n return;\n }\n const compatResult = buildCompatDiagnosis(model);\n if (compatResult) {\n cmdCtx.ui.notify(compatResult, \"warning\");\n } else {\n cmdCtx.ui.notify(\n isAdaptiveThinkingCompatApplicable(model) || isDeepSeekCompatCheckApplicable(model) || isCompatCheckApplicable(model)\n ? \"✅ Compat fully configured.\"\n : getCompatCheckNotApplicableLines(model).join(\"\\n\"),\n \"info\",\n );\n }\n } else if (subcommand === \"reset\") {\n if (!model) {\n cmdCtx.ui.notify(\"No active model selected. Select a model first with /model or pi --model.\", \"warning\");\n return;\n }\n const adapter = selectAdapterForModel(model);\n if (!adapter) {\n cmdCtx.ui.notify(\"ℹ️ Active model does not match a cache adapter. No stats to reset.\", \"info\");\n return;\n }\n\n const displayKey = modelKey(model);\n\n // Reset local footer stats for the effective active model. If the\n // selected model is a virtual router and the protocol exposes a live\n // route, this clears the real upstream bucket, not the router shell.\n resetStatsForModel(model);\n\n // Persist immediately.\n await flushPersistCacheStats(cmdCtx);\n\n // Update footer to show 0/0.\n await publishStatus(cmdCtx, model);\n\n cmdCtx.ui.notify(\n `✅ Reset local footer cache stats for \"${displayKey}\". ` +\n \"Upstream provider prompt cache was not modified. \" +\n \"New requests will start a fresh local stats bucket for this provider/model.\",\n \"info\",\n );\n } else if (subcommand === \"fix\") {\n if (!model) {\n cmdCtx.ui.notify(\"No active model selected. Select a model first with /model or pi --model.\", \"warning\");\n return;\n }\n\n const suggestion = buildCommandFixSuggestion(model);\n\n if (!suggestion) {\n const key = modelKey(model);\n cmdCtx.ui.notify(`✅ Nothing to fix for \"${key}\". Compat already configured.`, \"info\");\n return;\n }\n\n if (!cmdCtx.hasUI) {\n // No UI — refuse to write, show manual guidance instead.\n const compatResult = buildCompatDiagnosis(model);\n const snippet = formatMissingEntryManualSnippet(\n suggestion.providerLabel, suggestion.modelId, suggestion.compatKeys,\n );\n const manualLines = [\n `❌ Non-interactive terminal detected. Auto-fix requires UI confirmation.`,\n \"\",\n `Edit ${getModelsJsonDisplayPath()} and run /reload.`,\n ];\n if (promptCacheRetention400Models.has(modelKey(model))) {\n manualLines.push(\n \"\",\n \"💡 This model returned HTTP 400 for prompt_cache_retention.\",\n \"Create or edit the entry below to override supportsLongCacheRetention to false.\",\n );\n }\n if (anthropicTtlOrderErrorModels.has(modelKey(model))) {\n manualLines.push(\n \"\",\n \"💡 This model returned an Anthropic cache-control TTL ordering error.\",\n \"Create or edit the entry below to override supportsLongCacheRetention to false.\",\n );\n }\n if (sendSessionAffinityHeaders403Models.has(modelKey(model))) {\n manualLines.push(\n \"\",\n \"💡 This model returned HTTP 403 while sendSessionAffinityHeaders was enabled.\",\n \"Create or edit the entry below to override sendSessionAffinityHeaders to false.\",\n );\n }\n manualLines.push(\n \"\",\n \"Add these compat keys at Pi's highest-precedence model override path:\",\n `providers[\"${suggestion.providerLabel}\"] -> modelOverrides -> \"${suggestion.modelId}\" -> compat:`,\n formatCompatKeysForInsertion(suggestion.compatKeys),\n );\n if (snippet.length > 0) {\n manualLines.push(\n \"\",\n \"If the provider/model is missing (common for API-logged-in channels such as\",\n `opencode go), add a minimal entry under \"providers\" (keep existing auth as-is):`,\n \"\",\n snippet,\n );\n }\n if (compatResult) {\n manualLines.push(\"\", compatResult);\n }\n cmdCtx.ui.notify(manualLines.join(\"\\n\"), \"warning\");\n return;\n }\n\n // Read the models.json file\n let originalText: string;\n try {\n originalText = await readFile(MODELS_JSON_PATH, \"utf8\");\n } catch {\n cmdCtx.ui.notify(`❌ Could not read ${MODELS_JSON_PATH}. File may not exist.`, \"error\");\n return;\n }\n\n // Locate the model entry. API-logged-in providers (e.g. opencode go)\n // may not appear in models.json at all.\n const location = locateModelInJsonc(originalText, suggestion.providerLabel, suggestion.modelId);\n if (!location) {\n const diagnosis = analyzeModelsJsonForMissingEntry(originalText, suggestion.providerLabel, suggestion.modelId);\n if (diagnosis && cmdCtx.hasUI) {\n const overrideLocation = locateModelOverrideInJsonc(\n originalText, suggestion.providerLabel, suggestion.modelId,\n );\n const repairsExistingOverride = (overrideLocation?.modelOverrideObjectBrace ?? -1) >= 0;\n // Prefer a modelOverrides edit when models[] has no target entry.\n const plan = composeModelOverrideInsertion(\n originalText, suggestion.providerLabel, suggestion.modelId, suggestion.compatKeys,\n );\n if (!plan) {\n cmdCtx.ui.notify(\n `❌ Could not safely locate a modelOverrides insertion point.\\n` +\n `Falling back to manual guidance. No changes were made.`,\n \"error\",\n );\n } else {\n const checkError = selfCheckMissingEntryInsertion(\n originalText, plan.modifiedText,\n suggestion.providerLabel, suggestion.modelId, suggestion.compatKeys,\n );\n if (checkError !== null) {\n // Fall through to manual guidance.\n cmdCtx.ui.notify(\n `❌ Self-check would fail for auto-created entry: ${checkError}\\n` +\n `Falling back to manual guidance. No changes were made.`,\n \"error\",\n );\n // Continue to manual guidance below.\n } else {\n const keysPreview = JSON.stringify(suggestion.compatKeys, null, 2);\n const ts = backupTimestamp();\n const backupPath = `${MODELS_JSON_PATH}.backup-cache-optimizer-${ts}`;\n const previewLines = [\n `📝 Preview of changes to ${getModelsJsonDisplayPath()}:`,\n ``,\n `Location: ${plan.placementLabel}`,\n `Compat JSON to write:`,\n keysPreview,\n ``,\n `⚠️ Risk notice:`,\n repairsExistingOverride\n ? ` 1. This updates the existing modelOverrides entry for \"${suggestion.modelId}\". Existing auth is not affected.`\n : ` 1. This creates a modelOverrides entry in models.json. Existing auth (e.g. login API tokens) is not affected.`,\n ` 2. A timestamped backup will be written to: ${backupPath}`,\n ` 3. You must run /reload or restart Pi for the change to take effect.`,\n ` 4. If the file contains comments or unusual formatting, please verify the result after write.`,\n ];\n if (promptCacheRetention400Models.has(modelKey(model))) {\n previewLines.push(\n \"\",\n \"💡 This fix overrides supportsLongCacheRetention to false because\",\n \"a 400 prompt_cache_retention error was observed for this model.\",\n \"After applying and reloading, Pi will no longer send the\",\n \"prompt_cache_retention parameter to this provider.\",\n );\n }\n previewLines.push(\"\", `Apply these changes?`);\n const confirmed = await cmdCtx.ui.confirm(\n repairsExistingOverride ? \"Cache Optimizer — Fix (model override)\" : \"Cache Optimizer — Fix (new override)\",\n previewLines.join(\"\\n\"),\n );\n if (confirmed) {\n try {\n const result = await applyModelsJsonFixTransaction(\n plan.modifiedText,\n backupPath,\n (writtenText) => selfCheckMissingEntryInsertion(\n originalText,\n writtenText,\n suggestion.providerLabel,\n suggestion.modelId,\n suggestion.compatKeys,\n ),\n );\n if (\"postCheckError\" in result) {\n cmdCtx.ui.notify(\n `❌ Post-write self-check failed: ${result.postCheckError}\\n` +\n `The backup at ${backupPath} has been restored. No changes applied.`,\n \"error\",\n );\n return;\n }\n cmdCtx.ui.notify(\n `✅ Fix applied to ${getModelsJsonDisplayPath()}.\\n` +\n `Backup saved to: ${backupPath}\\n` +\n `Run /reload or restart Pi for the change to take effect.`,\n \"info\",\n );\n } catch (e) {\n cmdCtx.ui.notify(\n `❌ Write failed: ${e instanceof Error ? e.message : String(e)}\\n` +\n `Backup may be at: ${backupPath}`,\n \"error\",\n );\n }\n return;\n }\n cmdCtx.ui.notify(\"No changes were made. Canceled by user.\", \"info\");\n return;\n }\n }\n }\n\n // Non-interactive or no diagnosis: show manual guidance.\n const snippet = diagnosis\n ? formatMissingEntryManualSnippet(suggestion.providerLabel, suggestion.modelId, suggestion.compatKeys)\n : formatCompatKeysForInsertion(suggestion.compatKeys);\n const adviceLines: string[] = [];\n if (!diagnosis) {\n adviceLines.push(\n `❌ Could not locate model \"${suggestion.modelId}\" or provider \"${suggestion.providerLabel}\" in ${getModelsJsonDisplayPath()}.`,\n \"\",\n \"Providers that were added via Pi /login API (e.g. opencode go) do not have\",\n \"entries in models.json. You can create a minimal modelOverrides entry by hand:\",\n );\n } else if (diagnosis.scenario === \"provider_missing\") {\n adviceLines.push(\n `ℹ️ Provider \"${suggestion.providerLabel}\" does not exist in ${getModelsJsonDisplayPath()}.`,\n `This is common for API-logged-in providers (e.g. /login ...).`,\n \"\",\n \"Add the following minimal block under the \\\"providers\\\" key (keep your\",\n \"existing authentication as-is):\",\n );\n } else {\n adviceLines.push(\n `ℹ️ Model \"${suggestion.modelId}\" was not found in ${getModelsJsonDisplayPath()}`,\n `under providers[\"${suggestion.providerLabel}\"].`,\n \"\",\n \"Add the following modelOverrides entry (keep existing auth):\",\n );\n }\n adviceLines.push(\"\", snippet, \"\", \"Then save and run /reload.\");\n cmdCtx.ui.notify(adviceLines.join(\"\\n\"), \"warning\");\n return;\n }\n\n // Compose the modified text — observed runtime failures are always\n // model-scoped; ordinary compat fixes use the safety-based placement.\n const decision = chooseFixPlacement(\n originalText,\n location,\n suggestion.compatKeys,\n suggestion.providerLabel,\n suggestion.forceModelLevel,\n );\n const modifiedText = composeFixInsertion(originalText, location, suggestion.compatKeys, decision.placement);\n\n // Self-check\n const checkError = selfCheckFix(originalText, modifiedText, suggestion.providerLabel, suggestion.modelId, suggestion.compatKeys, decision.placement);\n if (checkError !== null) {\n cmdCtx.ui.notify(\n `❌ Self-check failed before write: ${checkError}\\n` +\n `No changes were made. Manual edit required.`,\n \"error\",\n );\n return;\n }\n\n // Build preview snippet as copyable JSON (the surgical editor will\n // insert or repair these exact compat key/value pairs).\n const keysPreview = JSON.stringify(suggestion.compatKeys, null, 2);\n const targetHasCompat = decision.placement === \"provider\"\n ? location.providerCompatBrace >= 0\n : decision.placement === \"modelOverride\"\n ? location.modelOverrideCompatBrace >= 0\n : location.compatObjectBrace >= 0;\n const placementDesc = targetHasCompat ? `existing \"compat\" object` : `new \"compat\" object`;\n const locationDesc = decision.placement === \"provider\"\n ? `providers[\"${suggestion.providerLabel}\"] -> compat (provider level, ${placementDesc})`\n : decision.placement === \"modelOverride\"\n ? `providers[\"${suggestion.providerLabel}\"] -> modelOverrides -> \"${suggestion.modelId}\" -> compat (${placementDesc})`\n : `providers[\"${suggestion.providerLabel}\"] -> models -> \"${suggestion.modelId}\" -> compat (model level, ${placementDesc})`;\n\n const ts = backupTimestamp();\n const backupPath = `${MODELS_JSON_PATH}.backup-cache-optimizer-${ts}`;\n\n const scopeRiskLine = decision.placement === \"provider\"\n ? ` 1. This change applies to ALL ${location.allModelIds.length || 1} model(s) in the \"${suggestion.providerLabel}\" provider, across all sessions.`\n : ` 1. This change affects ALL sessions using the \"${suggestion.providerLabel}\" provider/channel (scoped to model \"${suggestion.modelId}\").`;\n\n const previewLines = [\n `📝 Preview of changes to ${getModelsJsonDisplayPath()}:`,\n ``,\n `Location: ${locationDesc}`,\n `Placement: ${decision.placement} level — ${decision.reason}`,\n `Compat JSON to write:`,\n keysPreview,\n ``,\n `⚠️ Risk notice:`,\n scopeRiskLine,\n ` 2. A timestamped backup will be written to: ${backupPath}`,\n ` 3. You must restart Pi / run /reload for the change to take effect.`,\n ` 4. If the file contains comments or unusual formatting, please verify the result after write.`,\n ];\n if (promptCacheRetention400Models.has(modelKey(model))) {\n previewLines.push(\n \"\",\n \"💡 This fix overrides supportsLongCacheRetention to false because\",\n \"a 400 prompt_cache_retention error was observed for this model.\",\n \"After applying and reloading, Pi will no longer send the\",\n \"prompt_cache_retention parameter to this provider.\",\n );\n }\n previewLines.push(\"\", `Apply these changes?`);\n\n const confirmed = await cmdCtx.ui.confirm(\"Cache Optimizer — Fix\", previewLines.join(\"\\n\"));\n if (!confirmed) {\n cmdCtx.ui.notify(\"No changes were made. Canceled by user.\", \"info\");\n return;\n }\n\n // Write: backup → temp + rename → self-check again\n try {\n const result = await applyModelsJsonFixTransaction(\n modifiedText,\n backupPath,\n (writtenText) => selfCheckFix(\n originalText,\n writtenText,\n suggestion.providerLabel,\n suggestion.modelId,\n suggestion.compatKeys,\n decision.placement,\n ),\n );\n if (\"postCheckError\" in result) {\n cmdCtx.ui.notify(\n `❌ Post-write self-check failed: ${result.postCheckError}\\n` +\n `The backup at ${backupPath} has been restored. No changes applied.`,\n \"error\",\n );\n return;\n }\n\n cmdCtx.ui.notify(\n `✅ Fix applied to ${getModelsJsonDisplayPath()}.\\n` +\n `Backup saved to: ${backupPath}\\n` +\n `Run /reload or restart Pi for the change to take effect.`,\n \"info\",\n );\n } catch (writeError) {\n cmdCtx.ui.notify(\n `❌ Write failed: ${writeError instanceof Error ? writeError.message : String(writeError)}\\n` +\n `Backup may be at: ${backupPath}`,\n \"error\",\n );\n }\n } else {\n // Try interactive selection menu when UI supports it\n if (cmdCtx.hasUI) {\n const menuOptions = [\n \"Enable — Turn on runtime optimizations\",\n \"Disable — Turn off runtime optimizations\",\n \"Doctor — Show cache configuration\",\n \"Stats — Show cache stats and trend\",\n \"Compat — Show compat suggestion\",\n \"Fix — Auto-fix compat issues (writes models.json)\",\n \"Footer mode — Choose total, session, or process stats\",\n \"Reset — Reset local provider/model stats\",\n \"Cancel\",\n ];\n const choice = await cmdCtx.ui.select(\"Cache Optimizer\", menuOptions);\n if (choice === menuOptions[0]) {\n await handleCacheOptimizerCommand(\"enable\", cmdCtx);\n } else if (choice === menuOptions[1]) {\n await handleCacheOptimizerCommand(\"disable\", cmdCtx);\n } else if (choice === menuOptions[2]) {\n await handleCacheOptimizerCommand(\"doctor\", cmdCtx);\n } else if (choice === menuOptions[3]) {\n await handleCacheOptimizerCommand(\"stats\", cmdCtx);\n } else if (choice === menuOptions[4]) {\n await handleCacheOptimizerCommand(\"compat\", cmdCtx);\n } else if (choice === menuOptions[5]) {\n await handleCacheOptimizerCommand(\"fix\", cmdCtx);\n } else if (choice === menuOptions[6]) {\n const modeOptions = [\"total — Daily cumulative totals (default)\", \"session — Current Pi conversation session\", \"process — Current Pi process only\", \"Cancel\"];\n const modeChoice = await cmdCtx.ui.select(\"Footer cache stats mode\", modeOptions);\n const nextMode = modeChoice === modeOptions[0]\n ? \"total\"\n : modeChoice === modeOptions[1]\n ? \"session\"\n : modeChoice === modeOptions[2]\n ? \"process\"\n : undefined;\n if (nextMode) await handleCacheOptimizerCommand(`config footer-mode ${nextMode}`, cmdCtx);\n } else if (choice === menuOptions[7]) {\n await handleCacheOptimizerCommand(\"reset\", cmdCtx);\n }\n // choice === \"cancel\" or undefined → no action\n return;\n }\n\n // Fallback: text help when no interactive UI\n const diagnosis: string[] = [];\n diagnosis.push(\"📋 /cache-optimizer commands:\");\n diagnosis.push(\" enable — Enable prompt/cache optimizations for this Pi process\");\n diagnosis.push(\" disable — Disable prompt/cache optimizations for this Pi process\");\n diagnosis.push(\" doctor — Show current model/provider/api/baseUrl/compat and low-hit diagnosis\");\n diagnosis.push(\" stats — Show active model stats bucket and recent trend\");\n diagnosis.push(\" compat — Show compat suggestion with edit location\");\n diagnosis.push(\" config footer-mode total|session|process — Persist the footer stats mode\");\n diagnosis.push(\" fix — Auto-fix compat issues (writes models.json, requires UI)\");\n diagnosis.push(\" reset — Reset local provider/model stats for current model (does not affect upstream)\");\n diagnosis.push(\"\");\n diagnosis.push(formatOptimizerRuntimeMode());\n const resolvedFooterMode = resolveFooterStatsMode(persistedFooterStatsMode);\n diagnosis.push(`Footer stats mode: ${resolvedFooterMode.mode} (${resolvedFooterMode.source})`);\n diagnosis.push(\"\");\n if (model) {\n const displayKey = modelKey(model);\n const missing = describeMissingCacheCompatForModel(model);\n if (missing.length > 0) {\n diagnosis.push(`⚠️ Active model \"${displayKey}\" missing compat: ${missing.join(\", \")}`);\n diagnosis.push('Run \"/cache-optimizer compat\" for edit instructions.');\n } else if (isAdaptiveThinkingCompatApplicable(model) || isDeepSeekCompatCheckApplicable(model) || isCompatCheckApplicable(model)) {\n diagnosis.push(`✅ Active model \"${displayKey}\": compat fully configured.`);\n } else {\n diagnosis.push(`ℹ️ Active model \"${displayKey}\": compat check not applicable.`);\n const detailLines = getCompatCheckNotApplicableLines(model).slice(1);\n for (const line of detailLines) diagnosis.push(line);\n }\n } else {\n diagnosis.push(\"No active model selected.\");\n }\n cmdCtx.ui.notify(diagnosis.join(\"\\n\"), \"info\");\n }\n }\n\n pi.registerCommand(\"cache-optimizer\", {\n description: \"Configure and diagnose Pi cache behavior\",\n getArgumentCompletions: getCacheOptimizerArgumentCompletions,\n handler: handleCacheOptimizerCommand,\n });\n}\n"],"x_google_ignoreList":[0],"mappings":"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AA4C,CAAY,CAEvF,CACA,MAAM,CACR,CACF,CAMA,eAAe,GACb,EACA,EACA,EACyC,CACzC,IAAM,GAAgB,MAAM,EAAK,CAAgB,EAAA,CAAG,KAAO,KAC3D,MAAM,EAAS,EAAkB,EAAYA,EAAY,aAAa,EACtE,MAAM,EAAM,EAAY,CAAY,EAEpC,IAAI,EAAiB,GACrB,GAAI,CACF,MAAM,GACJ,EACA,EACA,EACA,KACF,EACA,EAAiB,GAGjB,IAAM,EAAiB,EAAoB,MADjB,EAAS,EAAkB,MAAM,CACL,EAChD,GAAe,MAAM,EAAK,CAAgB,EAAA,CAAG,KAAO,KACpD,EAAiB,IACrB,IAAgB,EACZ,KACA,wCAAwC,EAAa,SAAS,CAAC,EAAE,MAAM,EAAY,SAAS,CAAC,KAOnG,OALI,IAAmB,KAKhB,CAAE,GAAI,EAAK,GAJhB,MAAM,GAA4B,EAAY,EAAkB,CAAY,EACrE,CAAE,GAAI,GAAO,eAAgB,CAAe,EAIvD,OAAS,EAAO,CACd,GAAI,EACF,GAAI,CACF,MAAM,GAA4B,EAAY,EAAkB,CAAY,CAC9E,OAAS,EAAc,CACrB,MAAU,eACR,CAAC,EAAO,CAAY,EACpB,kEACF,CACF,CAEF,MAAM,CACR,CACF,CAKA,MAAa,GAAwB,CACnC,yBACA,wBACA,6BACA,4BACA,yBACA,mCACA,gCACA,8BACA,8BACA,yBACA,gBAEA,2BACA,uBACA,8BACA,oBACA,oCACA,4CACA,yBACA,0BACA,8BACA,4CACA,uCACA,kCACA,0CACA,2BACA,qCACA,6BACA,uBACA,kCACA,uBACA,0CACA,6CACA,+CACA,iCACA,mCACA,sCACA,iCACA,kCACA,wCACA,0CACA,0BACA,2BACA,uCACA,kCACA,kCACA,4CACA,kDAEA,mBACA,8BACA,mBACA,8BACA,kBACA,6BACA,sBACA,iCACA,mBACA,8BACA,sBACA,iCAEA,sBACA,iCACA,mBACA,8BACA,oBACA,+BACA,uBACA,kCACA,qBACA,gCACA,iBACA,4BAEA,qBACA,gCACA,oBACA,+BACA,uBACA,kCACA,sBACA,iCACA,oBACA,+BACA,uBACA,kCACA,oBACA,+BACA,kBACA,6BACA,oBACA,+BACA,oBACA,+BAEA,yBACA,oCACA,mBACA,8BACA,mBACA,8BACA,qBACA,gCACA,mBACA,8BACA,kBACA,6BACA,uBACA,kCACA,qBACA,gCACA,qBACA,gCACA,yBACA,oCACA,uBACA,kCACA,qBACA,gCAEA,sBACA,iCACA,qBACA,gCACA,oBACA,+BACA,wBACA,mCACA,qBACA,gCACA,sBACA,iCACA,qBACA,gCACA,oBACA,+BACA,uBACA,kCACA,qBACA,gCACA,qBACA,gCACA,qBACA,gCACA,sBACA,iCACA,uBACA,kCACA,uBACA,kCACA,oBACA,+BACA,mBACA,8BACA,kBACA,6BACA,yBACA,oCACA,oCACA,4BACA,sCACA,YACA,WACA,6BACA,uCAEA,0BACA,2BACA,+BACA,4BACA,2BACA,4BACA,2BACA,8BACA,6BACA,gCACA,8BACA,yBACA,6BAEA,kCAEA,wBACA,wBACA,oCAEA,wBACA,oBACA,sBACA,wCACA,kBACA,sBACA,mBACA,4BACA,uCACA,+BAEA,sBACA,oBACA,wBACA,4BACA,kBACA,gBACA,yBACA,kBAEA,iBACA,sBACA,0BACA,mCACA,gCACA,2BACA,+BACA,4BACA,6BACA,wBACA,sCACA,2BACA,4BACA,0BACA,mBACA,oBACA,mBAEA,8BACA,yBACA,yBACA,sBACA,sBACA,8BACA,0BACA,qBACA,yBACA,4BACA,wBACA,uCACA,qCACA,sCACA,qCAEA,sBACA,oBACA,yBACA,+BACA,4BACA,2BACA,mBACA,0BACA,YAEA,mBACA,qBACA,4BACA,cACA,8BACA,2CACA,sBACA,8BACA,uBACA,gBACA,oCACA,gCACA,iCACA,kCACA,sBACA,sBACA,iCACA,yBACA,gCACA,mBACA,uCACA,+BACA,iCAEA,sBAEA,6BACA,6BACA,mCACA,sCACA,yCACA,yCACA,0CACA,0CACF,EAEA,SAAA,GAAyB,EAAkB,CACzC,IAAM,EAAe,IAAI,IACnB,EAAgC,IAAI,IACpC,EAAsC,IAAI,IAC1C,EAA4B,GAA6B,EACzD,EAA+B,EAA0B,UACzD,EAAqC,EAA0B,gBAC/D,EAAsC,IAAI,IAC1C,EAA4C,IAAI,IAChD,EAA2B,IAAI,IAC/B,EAAiC,IAAI,IACvC,EAAgD,CAAC,EACjD,EAAuD,CAAC,EACxD,EAAsD,CAAC,EACvD,EAAuE,GAAmB,EAC1F,EACA,GAA0B,GAC1B,EAAqD,KACnD,GAAiB,GAA4B,EAC/C,GAAoC,GACpC,GAAiC,GAC/B,GAA+B,IAAI,IACrC,GAA6B,GAC7B,GAAmB,GACnB,EAAqB,GACrB,EAAwB,GACxB,EACA,GAGJ,SAAS,GAAkC,EAA2C,CACpF,IAAM,EAAM,EAAS,CAAK,EACpB,EAAW,EAAI,QAAQ,GAAG,EAC1B,EAAgB,EAAW,EAAI,EAAI,MAAM,EAAG,CAAQ,EAAI,EAE9D,GACE,EAA6B,IAAI,CAAG,GACnC,GAAoC,CAAK,GAAK,EAA8B,IAAI,CAAG,EAEpF,MAAO,CACL,gBACA,QAAS,EAAM,GACf,WAAY,CAAE,2BAA4B,EAAM,EAChD,gBAAiB,EACnB,EAEF,GAAI,GAA+B,CAAK,GAAK,EAAoC,IAAI,CAAG,EACtF,MAAO,CAAE,gBAAe,QAAS,EAAM,GAAI,WAAY,CAAE,2BAA4B,EAAM,CAAE,CAGjG,CAEA,SAAS,GAA0B,EAA2C,CAC5E,IAAM,EAAU,GAAmB,CAAK,EAClC,EAAW,GAAkC,CAAK,EAGxD,OAFK,EACA,EACE,CACL,cAAe,EAAQ,cACvB,QAAS,EAAQ,QACjB,WAAY,CAAE,GAAG,EAAQ,WAAY,GAAG,EAAS,UAAW,EAC5D,gBAAiB,EAAQ,iBAAmB,EAAS,eACvD,EANsB,EADD,CAQvB,CAEA,IAAM,GAA0B,IAAI,IAEpC,SAAS,GAAgB,EAAqD,CAC5E,IAAM,EAAM,EAAI,eAAe,aAAa,EACxC,IAAQ,IAAQ,IAAoB,CAAC,KACvC,GAAmB,EACnB,EAAqB,GAAc,CAAG,EACtC,EAAwB,GACxB,EAAwB,IAAA,GAE5B,CAEA,IAAM,GAA6B,GAAyB,GAAoC,CAC9F,QAAS,EACT,SAAS,EAA0D,CACjE,GAAI,CAAC,GAA2B,GAAa,QAAQ,IAAI,GAAsB,EAAG,OAClF,IAAM,EAAO,GACR,MACD,IAAM,eAAiB,EAAK,eAAiB,EAAM,gBAAkB,EAAK,gBAC1E,IAAM,iBAAmB,EAAK,iBAAmB,EAAM,kBAAoB,EAAK,kBAChF,IAAM,gBAAkB,EAAK,gBAAkB,EAAM,iBAAmB,EAAK,iBAC7E,IAAM,kBAAoB,EAAK,kBAAoB,EAAM,mBAAqB,EAAK,mBACnF,IAAM,iBAAmB,EAAK,iBAAmB,EAAM,kBAAoB,EAAK,kBAChF,IAAM,KAAO,EAAK,KAAO,EAAM,MAAQ,EAAK,KAEhD,MAAO,CACL,aAAc,EAAK,aACnB,eAAgB,EAAK,eACrB,eAAgB,EAAK,cACvB,CACF,CACF,CAAC,EAAG,CAAE,gBAAiB,EAAkC,CAAC,EAM1D,SAAS,GAAgB,EAAiD,CAExE,MAAO,GADM,GAAsB,aACpB,GAAG,EAAM,SAAS,GAAG,EAAM,IAC5C,CAMA,SAAS,GAA0B,EAAsB,CACvD,IAAM,EAAM,EAAK,QAAQ,GAAG,EAC5B,OAAO,GAAO,EAAI,EAAK,MAAM,EAAM,CAAC,EAAI,CAC1C,CAEA,SAAS,GAAmB,EAAqB,EAAsB,EAAmC,CACxG,IAAI,EAAU,GAAwB,IAAI,CAAW,EAChD,IACH,EAAU,CAAC,EACX,GAAwB,IAAI,EAAa,CAAO,GAElD,EAAQ,KAAK,CACX,UAAW,KAAK,IAAI,EACpB,IAAK,EAAM,UAAY,EACvB,kBAAmB,EAAM,UACzB,sBAAuB,EAAM,WAC7B,iBAAkB,EAAM,WACxB,oBACF,CAAC,EACG,EAAQ,OAAS,IACnB,EAAQ,OAAO,EAAG,EAAQ,OAAS,EAAkB,CAEzD,CAEA,SAAS,GAAiB,EAAyC,CACjE,OAAO,GAAwB,IAAI,CAAW,GAAK,CAAC,CACtD,CAEA,SAAS,IAA2B,CAClC,GAAwB,MAAM,CAChC,CAEA,SAAS,IAAsC,CAC7C,MAAO,CACL,aAAc,EACd,cAAe,EACf,aAAc,EACd,GAAI,GAAyB,EACzB,CAAE,yBAA0B,EAAG,GAAqB,CAAsB,CAAE,EAC5E,CAAC,CACP,CACF,CAGA,SAAS,GAAiB,EAA4B,EAA2C,CAC/F,GAAI,EAAO,CACT,IAAM,EAAM,EAAS,CAAK,EACpB,EAAW,EAAwB,GACzC,GAAI,EAAU,OAAO,CACvB,CAGA,IAAM,EAAS,EAAuB,EAAQ,IAC9C,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAU,EAAgB,EAEhC,MADA,GAAuB,EAAQ,IAAM,EAC9B,CACT,CAGA,SAAS,GAA2B,EAAyB,CAC3D,IAAM,EAAW,EAAkB,GACnC,GAAI,EAAU,OAAO,EAErB,IAAM,EAAU,EAAgB,EAEhC,MADA,GAAkB,GAAO,EAClB,CACT,CAGA,SAAS,GAAgC,EAA4B,CACnE,IAAM,EAAM,EAAS,CAAK,EACpB,EAAW,EAAyB,GAC1C,GAAI,EAAU,OAAO,EAErB,IAAM,EAAU,EAAgB,EAEhC,MADA,GAAyB,GAAO,EACzB,CACT,CAGA,SAAS,GAA8B,EAA4B,CACjE,IAAM,EAAM,EAAS,CAAK,EACpB,EAAW,EAAwB,GACzC,GAAI,EAAU,OAAO,EAErB,IAAM,EAAU,EAAgB,EAEhC,MADA,GAAwB,GAAO,EACxB,CACT,CAEA,SAAS,GAAmB,EAAsB,CAChD,IAAM,EAAa,EAAS,CAAK,EACjC,IAAK,IAAM,KAAO,OAAO,KAAK,CAAiB,EACzC,GAA0B,CAAG,IAAM,GAAY,OAAO,EAAkB,GAE9E,OAAO,EAAwB,GAC/B,OAAO,EAAyB,GAChC,GAA6B,IAAI,CAAU,EAC3C,IAAK,IAAM,KAAO,MAAM,KAAK,GAAwB,KAAK,CAAC,EACrD,GAA0B,CAAG,IAAM,GAAY,GAAwB,OAAO,CAAG,EAEvF,EAAiB,IAAA,EACnB,CAEA,SAAS,GAAiC,CACxC,IAAM,EAAS,GAAG,GAAsB,aAAa,GACrD,IAAK,IAAM,KAAO,OAAO,KAAK,CAAiB,EACzC,EAAI,WAAW,CAAM,GAAG,OAAO,EAAkB,GAEvD,EAA2B,CAAC,EAC5B,EAA0B,CAAC,EAC3B,GAAoC,GACpC,IAAK,IAAM,KAAO,MAAM,KAAK,GAAwB,KAAK,CAAC,EACrD,EAAI,WAAW,CAAM,GAAG,GAAwB,OAAO,CAAG,EAEhE,EAAwB,IAAA,GACxB,EAAiB,IAAA,EACnB,CAEA,SAAS,IAA2C,CAClD,IAAM,EAAQ,GAAmB,EAC3B,EAAe,OAAO,YAC1B,OAAO,QAAQ,EAAM,YAAY,CAAC,CAAC,KAAK,CAAC,EAAK,KAAW,CAAC,EAAK,CAAE,GAAG,CAAM,CAAC,CAAC,CAC9E,EACM,EAAgB,OAAO,YAC3B,OAAO,QAAQ,EAAM,aAAa,CAAC,CAAC,KAAK,CAAC,EAAK,KAAW,CAAC,EAAK,CAAE,GAAG,CAAM,CAAC,CAAC,CAC/E,EACM,EAA6D,CAAC,EACpE,IAAK,IAAM,KAAM,GAAoB,CACnC,IAAM,EAAQ,EAAM,aAAa,GAC7B,IAAO,EAAa,GAAM,CAAE,GAAG,CAAM,EAC3C,CAMA,MAAO,CAAE,eAAc,gBAAe,eAAc,yBALnB,EAAM,yBACnC,OAAO,YACP,OAAO,QAAQ,EAAM,wBAAwB,CAAC,CAAC,KAAK,CAAC,EAAK,KAAW,CAAC,EAAK,CAAE,GAAG,CAAM,CAAC,CAAC,CAC1F,EACE,IAAA,EACyE,CAC/E,CAEA,SAAS,GAAkB,EAAuC,CAChE,IAAM,EAAQ,GAAwB,EAChC,EAAc,EAAwB,EAAqB,IAAA,GAC3D,EAAkB,MAAM,KAAK,EAA4B,EACzD,EAAgB,GAMtB,OAHA,GAA6B,MAAM,EACnC,GAAoC,GAE7B,GAAe,SAAY,CAChC,IAAM,EAA4B,GAClC,GAAI,CACF,MAAM,GAAyB,EAAO,EAAa,CACjD,kBACA,cAAe,GAAiB,CAClC,CAAC,EACG,IAA2B,GAAiC,GAClE,OAAS,EAAO,CAGd,GAAiC,GACjC,QAAQ,KAAK,GAAG,EAAW,iCAAkC,CAAK,EAC7D,KACH,GAA0B,GAC1B,GAAK,GAAG,OACN,GAAG,EAAW,2EACd,SACF,EAEJ,CACF,CAAC,CACH,CAMA,SAAS,GAA0B,EAA8B,CAC3D,IAAiB,MAAM,aAAa,CAAY,EACpD,EAAe,eAAiB,CAC9B,EAAe,KACf,GAAkB,CAAG,CAAC,CAAC,MAAO,GAAQ,CACpC,QAAQ,KAAK,GAAG,EAAW,4BAA6B,CAAG,CAC7D,CAAC,CACH,EAAG,GAAmB,CACxB,CAIA,eAAe,GAAuB,EAAuC,CACvE,IAAiB,OACnB,aAAa,CAAY,EACzB,EAAe,MAEjB,MAAM,GAAkB,CAAG,CAC7B,CAEA,eAAe,GAAsB,EAAuC,CAC1E,IAAM,EAAM,GAAgB,EACxB,EAAU,GAGd,IAAK,IAAM,KAAO,OAAO,KAAK,CAAiB,EAAG,CAChD,IAAM,EAAQ,EAAkB,GAC5B,GAAS,EAAM,MAAQ,IACzB,EAAkB,GAAO,EAAgB,CAAG,EAC5C,EAAU,GAEd,CAGA,IAAK,IAAM,KAAO,OAAO,KAAK,CAAwB,EAAG,CACvD,IAAM,EAAQ,EAAyB,GACnC,GAAS,EAAM,MAAQ,IACzB,EAAyB,GAAO,EAAgB,CAAG,EACnD,EAAU,GAEd,CAEA,IAAK,IAAM,KAAO,OAAO,KAAK,CAAuB,EAAG,CACtD,IAAM,EAAQ,EAAwB,GAClC,GAAS,EAAM,MAAQ,IACzB,EAAwB,GAAO,EAAgB,CAAG,EAClD,EAAU,GAEd,CAGA,IAAK,IAAM,KAAM,GAAoB,CACnC,IAAM,EAAQ,EAAuB,GACjC,GAAS,EAAM,MAAQ,IACzB,EAAuB,GAAM,EAAgB,CAAG,EAChD,EAAU,GAEd,CAEI,IACF,EAAiB,IAAA,GACjB,MAAM,GAAkB,CAAG,EAE/B,CAEA,eAAe,GAAkB,EAAgB,EAAsC,CAGrF,GAFA,GAAgB,CAAG,EAEf,IAAW,SAAU,CAIvB,EAAiB,IAAA,GACjB,GAA+B,EAC/B,GAA6B,GAC7B,GAAmB,EAEnB,IAAM,EAAY,MAAM,GAAwB,EAChD,EAAoB,GAClB,EACA,EAAwB,EAAqB,IAAA,EAC/C,EACA,EAA2B,CAAC,EAC5B,EAA0B,GAAW,eAAiB,CAAC,EACvD,EAAyB,GAAW,cAAgB,GAAmB,EACvE,EAAwB,EACpB,GAAW,2BAA2B,GACtC,IAAA,GAEJ,MAAM,GAAsB,CAAG,EAC/B,MACF,CAKA,IAAM,EAAY,MAAM,GAAwB,EAChD,EAAoB,GAClB,EACA,EAAwB,EAAqB,IAAA,EAC/C,EACA,EAA2B,CAAC,EAC5B,EAA0B,GAAW,eAAiB,CAAC,EACvD,EAAyB,GAAW,cAAgB,GAAmB,EACvE,EAAwB,EACpB,GAAW,2BAA2B,GACtC,IAAA,GACJ,EAAiB,IAAA,GACjB,MAAM,GAAsB,CAAG,CACjC,CAEA,eAAe,GAAc,EAAuB,EAA6B,EAAI,MAAsB,CACzG,GAAgB,CAAG,EACnB,MAAM,GAAsB,CAAG,EAE/B,IAAM,EAAc,GAAkB,EAAO,CAAG,EAC1C,EAAe,GAAe,EAC9B,EAAU,GAAsB,CAAY,EAC5C,EAAuB,CAAC,CAAC,GAAe,GAAsB,EAAO,CAAG,EAC1E,EACE,EAAO,GAAgB,EACvB,EAAc,EAAwB,EAAqB,IAAA,GAEjE,GAAI,CAAC,GAAW,CAAC,GAAe,EAAsB,CAMpD,GAAI,IAAmB,IAAA,GAAW,OAClC,IAAM,EAAY,GAChB,EACA,EACA,EACA,EACA,EACA,CACF,GAAK,GACH,EACA,EACA,EACA,EACA,CACF,EACA,GAAI,EAAW,CACb,IAAM,EAAY,GAAiB,EAAU,QAAS,EAAU,KAAK,EACrE,EAAa,EACT,EACA,8BAA8B,GACpC,CACF,CAEA,GAAI,EAAS,CAMX,IAAM,EAAY,GAAiB,GAHrB,EACV,GAA0B,EAAM,EAAa,EAAmB,EAAyB,EAAc,CAAwB,EAC/H,IAAA,KACiD,EAAgB,CAAC,EACtE,EAAa,EAA0B,EAAY,8BAA8B,GACnF,CAMI,IAA4B,IAAe,IAAA,KAC7C,GAA0B,gBAC1B,GAA2B,GAC3B,GAA+B,KAAK,IAAI,EAGnC,KACH,GAA6B,GAC7B,EAAI,GAAG,OACL,MAAM,EAAW,iXAMjB,SACF,IAWA,GAA2B,IAAe,IAAA,IAAa,GAKrD,GAAmB,CAAY,IAAM,IAAA,KACvC,GAA0B,cAI9B,EAAa,GAAmB,CAAU,EACtC,IAAe,IAEnB,EAAiB,EACjB,EAAI,GAAG,UAAU,iBAAY,CAAU,EACzC,CAEA,GAAsB,EAetB,SAAS,GAA8B,EAAyB,CAC9D,GAAI,CAGF,OAAO,GADQ,GADF,EAAa,EAAkB,MACf,CACuB,EAAG,EAAM,SAAU,EAAM,EAAE,CACjF,MAAQ,CACN,MAAO,EACT,CACF,CAEA,EAAG,GAAG,gBAAiB,MAAO,EAAO,IAAQ,CACvC,GAAyB,EAA0B,EACvD,MAAM,GAAkB,EAAM,OAAQ,CAAG,EACzC,MAAM,GAAc,CAAG,CACzB,CAAC,EAED,EAAG,GAAG,mBAAoB,MAAO,EAAQ,IAAQ,CAC/C,GAAI,CACF,MAAM,GAAuB,CAAG,CAClC,QAAU,CACR,GAAkB,IAAA,GAClB,OAAO,GAAkB,CAAC,CAAC,6BAC3B,GAA2B,EAC3B,EAAyB,CAA2B,CACtD,CACF,CAAC,EAED,EAAG,GAAG,eAAgB,MAAO,EAAO,IAAQ,CACtC,GAAyB,GAA0B,GAAkB,EAAM,MAAO,CAAG,GAAK,EAAM,MAAO,EAAK,CAAY,EAC5H,MAAM,GAAc,EAAK,EAAM,KAAK,CACtC,CAAC,EAED,EAAG,GAAG,qBAAsB,MAAO,EAAO,IAAS,CACjD,GAAkB,IAAA,GAIlB,OAAO,GAAkB,CAAC,CAAC,6BAC3B,IAAM,EAAgB,GAA2B,EAAK,MAAO,CAAI,EA+B3D,GA9Bc,EAChB,GAAoB,EAAK,cAAe,EAAc,SAAU,EAAc,OAAO,GAAK,GAAuB,EAAe,EAAK,KAAK,EAC1I,IAAA,KA4ByB,EAAK,MAWlC,GAVI,GAAS,GAAkC,EAAM,GAAG,GAIpD,CAAC,GAMD,GAAa,QAAQ,IAAI,GAAsB,EACjD,MAAO,CAAC,EAOV,IAAM,EAAiB,GAA0B,EAAM,YAAY,EAQ7D,EAAmB,GACvB,EACA,EAAM,mBACR,EAMM,EAAY,GAAqB,EAAkB,EAAM,mBAAmB,EAE5E,EAAiB,GAAyB,CAAI,EAC9C,EAAiB,QAAQ,IAAI,KAA4B,EAA6B,EAA6B,IAAA,GACnH,EAAe,GAA+B,CAClD,GAAkB,CAChB,cAAe,EAAwB,EAAqB,GAAuB,CAAI,EACvF,gBAAiB,GAAe,iBAAmB,EAAK,OAAO,SAC/D,eAAgB,GAAe,gBAAkB,EAAK,OAAO,GAC7D,iBAAkB,GAAe,UAAY,GAAO,SACpD,gBAAiB,GAAe,SAAW,GAAO,GAClD,IAAK,GAAO,IACZ,eACA,iBACA,iBACA,UAAW,KAAK,IAAI,CACtB,EACA,IAAM,EAAU,GAAkB,EAC9B,EACF,EAAQ,6BAA+B,EAEvC,OAAO,EAAQ,4BAEnB,EAqBA,OAnBI,EAAU,SAAW,EAAU,aAAa,KAAK,CAAC,CAAC,OAAS,GAC9D,EAAY,EAAU,YAAY,EAC3B,CAAE,aAAc,EAAU,YAAa,GAO5C,IAAqB,GAAkB,EAAiB,KAAK,CAAC,CAAC,OAAS,GAC1E,EAAY,CAAgB,EACrB,CAAE,aAAc,CAAiB,GAEtC,IAAmB,EAAM,cAAgB,EAAe,KAAK,CAAC,CAAC,OAAS,GAC1E,EAAY,CAAc,EACnB,CAAE,aAAc,CAAe,IAGxC,EAAY,EAAM,YAAY,EACvB,CAAC,EACV,CAAC,EAED,EAAG,GAAG,2BAA4B,EAAO,IAAQ,CAC/C,IAAM,EAAe,GAAkB,EAAI,MAAO,CAAG,GAAK,EAAI,MA2B9D,GApBI,GAAgB,GAAuB,EAAa,GAAG,GAErD,CADyB,GAAuC,EAAM,OAClD,GAAK,EAA6B,IAAI,EAAS,CAAY,CAAC,GAClF,GAAoC,EAAM,OAAO,EAiBjD,EAAyB,CAC3B,IAAM,EAAU,EAAM,QAClB,GAAW,OAAO,EAAQ,wBAA2B,UACnD,IACE,EAAwB,CAAY,IAE7B,EAA8B,IAAI,EAAS,CAAY,CAAC,EAEjE,OAAO,EAAQ,uBACN,GAA8B,CAAY,GAInD,OAAO,EAAQ,wBAIvB,CAEK,MAAiC,GACjC,GAAyC,CAAY,EAE1D,OAAO,GAAwB,EAAM,QAAS,GAAyB,CAAG,CAAC,CAC7E,CAAC,EAED,EAAG,GAAG,2BAA4B,EAAO,IAAQ,CAC/C,IAAM,EAAQ,GAAkB,EAAI,MAAO,CAAG,GAAK,EAAI,MACnD,MAAC,GAA2B,CAAC,GAGjC,IAAI,EAAM,SAAW,IAAK,CAExB,GADI,CAAC,GAAoC,CAAK,GAC1C,CAAC,GAAyC,EAAM,OAAO,EAAG,OAE9D,IAAM,EAAM,EAAS,CAAK,EAE1B,GADA,EAA8B,IAAI,CAAG,EACjC,EAAoC,IAAI,CAAG,EAAG,OAClD,EAAoC,IAAI,CAAG,EAC3C,EAAI,GAAG,OACL,MAAM,EAAW,IAAI,EAAI,kEACzB,GAAuC,EACvC,4DACA,SACF,EACA,MACF,CAGA,GAAI,EAAM,SAAW,IAAK,CACxB,IAAM,EAAS,EAAS,CAAK,EAI7B,GAAI,GAA+B,CAAK,EAAG,CAEzC,GADA,EAAoC,IAAI,CAAM,EAC1C,EAA0C,IAAI,CAAM,EAAG,OAC3D,EAA0C,IAAI,CAAM,EACpD,EAAI,GAAG,OACL,MAAM,EAAW,IAAI,EAAO,sSAG5B,SACF,EACA,MACF,CAOA,GAAI,GAA+B,CAAK,EAAG,CAEzC,GADA,EAAyB,IAAI,CAAM,EAC/B,EAA+B,IAAI,CAAM,EAAG,OAChD,EAA+B,IAAI,CAAM,EACzC,EAAI,GAAG,OACL,MAAM,EAAW,IAAI,EAAO,wRAG5B,SACF,EACA,MACF,CACF,CAtCA,CAuCF,CAAC,EAED,EAAG,GAAG,cAAe,MAAO,EAAO,IAAQ,CACzC,GAAgB,CAAG,EACnB,IAAM,EAAY,EAAS,EAAM,OAAO,EAMxC,GAAI,GAA2B,GAA+C,EAAM,OAAO,EAAG,CAC5F,IAAM,EAAgB,GAAkB,EAAI,MAAO,CAAG,GAAK,EAAI,MACzD,EAAe,GAA0B,EAAM,QAAS,CAAa,GAAK,EAC1E,EAAa,EACf,GAAoB,EAAI,cAAe,EAAa,SAAU,EAAa,EAAE,GAAK,EAClF,IAAA,GACJ,GAAI,GAAc,GAAoD,CAAU,EAAG,CACjF,IAAM,EAAM,EAAS,CAAU,EAC/B,EAA8B,IAAI,CAAG,EAChC,EAAoC,IAAI,CAAG,IAC9C,EAAoC,IAAI,CAAG,EAC3C,EAAI,GAAG,OACL,MAAM,EAAW,IAAI,EAAI,oCACzB,GAAuC,EACvC,4DACA,SACF,EAEJ,CACF,CAKA,GAAI,GAA+B,EAAM,OAAO,EAAG,CACjD,IAAM,EAAgB,GAAkB,EAAI,MAAO,CAAG,GAAK,EAAI,MACzD,EAAa,GAA0B,EAAM,QAAS,CAAa,GAAK,EAC9E,GAAI,GAAc,GAAuB,EAAW,GAAG,EAAG,CACxD,IAAM,EAAM,EAAS,CAAU,EAC/B,EAA6B,IAAI,CAAG,EAC/B,EAAmC,IAAI,CAAG,IAC7C,EAAmC,IAAI,CAAG,EAC1C,EAAI,GAAG,OACL,MAAM,EAAW,IAAI,EAAI,yNAGzB,SACF,EAEJ,CACF,CAEA,IAAM,EAAU,GAAiC,EAAM,QAAS,EAAI,KAAK,EAUzE,GATI,CAAC,GASD,GAAW,aAAe,SAAW,GAAW,aAAe,UACjE,OAGF,IAAM,EAAQ,EAAQ,eAAe,EAAM,OAAO,EAK9C,EAAa,GAA0B,EAAM,QAAS,EAAI,KAAK,GAAK,EAAI,MAU5E,EAAa,GAAoC,EAAY,EAAI,MAAO,CAAG,EAC3E,IAAI,EAAqB,GACzB,GAAI,GAAsB,EAAI,MAAO,CAAG,GAAK,GAAc,CAAC,GAAsB,EAAY,CAAG,EAAG,CAClG,IAAM,EAA2C,CAC/C,SAAU,EAAW,SACrB,GAAI,EAAW,GACf,KAAM,EAAW,MAAQ,EAAW,EACtC,GAEE,CAAC,GACD,EAAsB,WAAa,EAAgB,UACnD,EAAsB,KAAO,EAAgB,KAC5C,EAAsB,MAAQ,EAAsB,OAAS,EAAgB,MAAQ,EAAgB,OAEtG,EAAwB,EACxB,EAAqB,GAEzB,CAGA,GAAI,EAAY,CACd,IAAM,EAAK,GAAgB,CAAU,EAC/B,EAAgB,IAAU,IAAA,IAAc,EAAM,YAAc,GAAK,EAAM,aAAe,GAAK,EAAM,aAAe,GAElH,GAAsB,EAAM,QAAS,CAAO,EAChD,GAAmB,EAAI,GAAS,CAAE,UAAW,EAAG,WAAY,EAAG,WAAY,CAAE,EAAG,CAAa,CAC/F,CAEA,GAAI,CAAC,EAAO,CACN,GAAoB,GAA0B,CAAG,EACrD,MACF,CAEA,MAAM,GAAsB,CAAG,EAI3B,GAEF,GAAqB,GADV,GAAgB,CACsB,CAAC,EAAG,CAAK,EAC1D,GAAqB,GAAgC,CAAU,EAAG,CAAK,EACvE,GAAqB,GAA8B,CAAU,EAAG,CAAK,GAErE,GAAqB,GAAiB,IAAA,GAAW,CAAO,EAAG,CAAK,EAGlE,GAA0B,CAAG,EAC7B,MAAM,GAAc,EAAK,CAAU,CACrC,CAAC,EAgBD,eAAe,EACb,EACA,EACe,CACf,GAAgB,CAAM,EACtB,IAAM,EAAgB,EAAO,MACvB,EAAQ,GAAkB,EAAe,CAAM,GAAK,EACpD,EAAe,EAAK,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EACpE,EAAa,EAAa,IAAM,OAEpC,GAAI,IAAe,SACjB,GAA2B,EAAI,EAC/B,EAAyB,EACzB,MAAM,GAAuB,CAAM,EACnC,MAAM,GAAc,EAAQ,CAAK,EACjC,EAAO,GAAG,OAAO,iHAAiH,GAA2B,IAAK,MAAM,OACnK,GAAI,IAAe,UACxB,GAA2B,EAAK,EAChC,EAAyB,EACzB,MAAM,GAAuB,CAAM,EACnC,MAAM,GAAc,EAAQ,CAAK,EACjC,EAAO,GAAG,OAAO,8IAA8I,GAA2B,IAAK,SAAS,OACnM,GAAI,IAAe,SAAU,CAClC,GAAI,CAAC,EAAO,CACV,EAAO,GAAG,OAAO,4EAA6E,SAAS,EACvG,MACF,CACA,IAAM,EAAY,GAAqB,EAAO,CAAE,wBAAyB,EAA8B,IAAI,EAAS,CAAK,CAAC,EAAG,uBAAwB,EAA6B,IAAI,EAAS,CAAK,CAAC,EAAG,mBAAoB,EAAoC,IAAI,EAAS,CAAK,CAAC,EAAG,mBAAoB,EAAyB,IAAI,EAAS,CAAK,CAAC,CAAE,CAAC,EACnV,EAAU,GAAsB,CAAK,EACrC,EAAK,EAAQ,GAAgB,CAAK,EAAI,IAAA,GAGtC,EAAc,GAAqB,EAAO,EAF7B,EAAQ,EAAwB,EAAS,CAAK,GAAK,IAAA,GACtD,EAAK,GAAiB,CAAE,EAAI,CAAC,CAC+B,EACtE,EAAgB,EAAY,OAAS,EACvC,EAAY;EAAO,EAAY,KAAK;CAAI,EACxC,EACJ,EAAO,GAAG,OAAO,EAAe,MAAM,CACxC,MAAO,GAAI,IAAe,QAAS,CACjC,GAAI,CAAC,EAAO,CACV,EAAO,GAAG,OAAO,4EAA6E,SAAS,EACvG,MACF,CACA,IAAM,EAAU,GAAsB,CAAK,EACrC,EAAK,EAAQ,GAAgB,CAAK,EAAI,IAAA,GAGtC,EAAS,GAAiB,EAAO,EAFpB,EAAQ,EAAwB,EAAS,CAAK,GAAK,IAAA,GACtD,EAAK,GAAiB,CAAE,EAAI,CAAC,CACsB,EACnE,EAAO,GAAG,OAAO,EAAQ,MAAM,CACjC,MAAO,GAAI,IAAe,SAAU,CAClC,IAAM,EAAY,EAAa,GACzB,EAAgB,EAAa,GACnC,GAAI,IAAc,eAAiB,CAAC,GAAiB,CAAC,CAAC,UAAW,QAAS,SAAS,CAAC,CAAC,SAAS,CAAa,EAAG,CAC7G,IAAM,EAAW,GAAuB,EAAwB,EAChE,EAAO,GAAG,OACR,0FACwB,EAAS,KAAK,IAAI,EAAS,OAAO,GAC1D,MACF,EACA,MACF,CAEA,IAAM,EAAW,EACjB,GAAI,CACF,MAAM,GAAyB,CAAQ,EACvC,GAA2B,EAC3B,EAAiB,IAAA,GACjB,MAAM,GAAc,EAAQ,CAAK,EACjC,IAAM,EAAW,GAAuB,EAAwB,EAChE,EAAO,GAAG,OACR,wBAAwB,EAAS,KAAK,gCAAgC,GAAgB,GACtF,MACF,CACF,OAAS,EAAO,CACd,EAAO,GAAG,OACR,0CAA0C,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,IAC/F,OACF,CACF,CACF,MAAO,GAAI,IAAe,SAAU,CAClC,GAAI,CAAC,EAAO,CACV,EAAO,GAAG,OAAO,4EAA6E,SAAS,EACvG,MACF,CACA,IAAM,EAAe,GAAqB,CAAK,EAC3C,EACF,EAAO,GAAG,OAAO,EAAc,SAAS,EAExC,EAAO,GAAG,OACR,GAAmC,CAAK,GAAK,GAAgC,CAAK,GAAK,GAAwB,CAAK,EAChH,6BACA,GAAiC,CAAK,CAAC,CAAC,KAAK;CAAI,EACrD,MACF,CAEJ,MAAO,GAAI,IAAe,QAAS,CACjC,GAAI,CAAC,EAAO,CACV,EAAO,GAAG,OAAO,4EAA6E,SAAS,EACvG,MACF,CAEA,GAAI,CADY,GAAsB,CAC3B,EAAG,CACZ,EAAO,GAAG,OAAO,qEAAsE,MAAM,EAC7F,MACF,CAEA,IAAM,EAAa,EAAS,CAAK,EAKjC,GAAmB,CAAK,EAGxB,MAAM,GAAuB,CAAM,EAGnC,MAAM,GAAc,EAAQ,CAAK,EAEjC,EAAO,GAAG,OACR,yCAAyC,EAAW,iIAGpD,MACF,CACF,MAAO,GAAI,IAAe,MAAO,CAC/B,GAAI,CAAC,EAAO,CACV,EAAO,GAAG,OAAO,4EAA6E,SAAS,EACvG,MACF,CAEA,IAAM,EAAa,GAA0B,CAAK,EAElD,GAAI,CAAC,EAAY,CACf,IAAM,EAAM,EAAS,CAAK,EAC1B,EAAO,GAAG,OAAO,yBAAyB,EAAI,+BAAgC,MAAM,EACpF,MACF,CAEA,GAAI,CAAC,EAAO,MAAO,CAEjB,IAAM,EAAe,GAAqB,CAAK,EACzC,EAAU,GACd,EAAW,cAAe,EAAW,QAAS,EAAW,UAC3D,EACM,EAAc,CAClB,0EACA,GACA,QAAQ,EAAyB,EAAE,kBACrC,EACI,EAA8B,IAAI,EAAS,CAAK,CAAC,GACnD,EAAY,KACV,GACA,8DACA,iFACF,EAEE,EAA6B,IAAI,EAAS,CAAK,CAAC,GAClD,EAAY,KACV,GACA,wEACA,iFACF,EAEE,EAAoC,IAAI,EAAS,CAAK,CAAC,GACzD,EAAY,KACV,GACA,gFACA,iFACF,EAEF,EAAY,KACV,GACA,wEACA,cAAc,EAAW,cAAc,2BAA2B,EAAW,QAAQ,cACrF,GAA6B,EAAW,UAAU,CACpD,EACI,EAAQ,OAAS,GACnB,EAAY,KACV,GACA,8EACA,kFACA,GACA,CACF,EAEE,GACF,EAAY,KAAK,GAAI,CAAY,EAEnC,EAAO,GAAG,OAAO,EAAY,KAAK;CAAI,EAAG,SAAS,EAClD,MACF,CAGA,IAAI,EACJ,GAAI,CACF,EAAe,MAAM,EAAS,EAAkB,MAAM,CACxD,MAAQ,CACN,EAAO,GAAG,OAAO,oBAAoB,EAAiB,uBAAwB,OAAO,EACrF,MACF,CAIA,IAAM,EAAW,GAAmB,EAAc,EAAW,cAAe,EAAW,OAAO,EAC9F,GAAI,CAAC,EAAU,CACb,IAAM,EAAY,GAAiC,EAAc,EAAW,cAAe,EAAW,OAAO,EAC7G,GAAI,GAAa,EAAO,MAAO,CAI7B,IAAM,GAHmB,GACvB,EAAc,EAAW,cAAe,EAAW,OAEL,CAAC,EAAE,0BAA4B,KAAO,EAEhF,EAAO,GACX,EAAc,EAAW,cAAe,EAAW,QAAS,EAAW,UACzE,EACA,GAAI,CAAC,EACH,EAAO,GAAG,OACR;wDAEA,OACF,MACK,CACP,IAAM,EAAa,GACjB,EAAc,EAAK,aACnB,EAAW,cAAe,EAAW,QAAS,EAAW,UAC3D,EACA,GAAI,IAAe,KAEjB,EAAO,GAAG,OACR,mDAAmD,EAAW,0DAE9D,OACF,MAEK,CACL,IAAM,EAAc,KAAK,UAAU,EAAW,WAAY,KAAM,CAAC,EAC3D,EAAK,GAAgB,EACrB,EAAa,GAAG,EAAiB,0BAA0B,IAC3D,EAAe,CACnB,4BAA4B,EAAyB,EAAE,GACvD,GACA,aAAa,EAAK,iBAClB,wBACA,EACA,GACA,mBACA,EACI,4DAA4D,EAAW,QAAQ,mCAC/E,kHACJ,iDAAiD,IACjD,yEACA,iGACF,EAeA,GAdI,EAA8B,IAAI,EAAS,CAAK,CAAC,GACnD,EAAa,KACX,GACA,qEACA,kEACA,2DACA,oDACF,EAEF,EAAa,KAAK,GAAI,sBAAsB,EAKxC,MAJoB,EAAO,GAAG,QAChC,EAA0B,yCAA2C,uCACrE,EAAa,KAAK;CAAI,CACxB,EACe,CACb,GAAI,CACF,IAAM,EAAS,MAAM,GACnB,EAAK,aACL,EACC,GAAgB,GACf,EACA,EACA,EAAW,cACX,EAAW,QACX,EAAW,UACb,CACF,EACA,GAAI,mBAAoB,EAAQ,CAC9B,EAAO,GAAG,OACR,mCAAmC,EAAO,eAAe,kBACxC,EAAW,yCAC5B,OACF,EACA,MACF,CACA,EAAO,GAAG,OACR,oBAAoB,EAAyB,EAAE,sBAC3B,EAAW,4DAE/B,MACF,CACF,OAAS,EAAG,CACV,EAAO,GAAG,OACR,mBAAmB,aAAa,MAAQ,EAAE,QAAU,OAAO,CAAC,EAAE,sBACzC,IACrB,OACF,CACF,CACA,MACF,CACA,EAAO,GAAG,OAAO,0CAA2C,MAAM,EAClE,MACF,CACA,CACF,CAGA,IAAM,EAAU,EACZ,GAAgC,EAAW,cAAe,EAAW,QAAS,EAAW,UAAU,EACnG,GAA6B,EAAW,UAAU,EAChD,EAAwB,CAAC,EAC1B,EAOM,EAAU,WAAa,mBAChC,EAAY,KACV,gBAAgB,EAAW,cAAc,sBAAsB,EAAyB,EAAE,GAC1F,gEACA,GACA,uEACA,iCACF,EAEA,EAAY,KACV,aAAa,EAAW,QAAQ,qBAAqB,EAAyB,IAC9E,oBAAoB,EAAW,cAAc,KAC7C,GACA,8DACF,EApBA,EAAY,KACV,6BAA6B,EAAW,QAAQ,iBAAiB,EAAW,cAAc,OAAO,EAAyB,EAAE,GAC5H,GACA,6EACA,gFACF,EAiBF,EAAY,KAAK,GAAI,EAAS,GAAI,4BAA4B,EAC9D,EAAO,GAAG,OAAO,EAAY,KAAK;CAAI,EAAG,SAAS,EAClD,MACF,CAIA,IAAM,EAAW,GACf,EACA,EACA,EAAW,WACX,EAAW,cACX,EAAW,eACb,EACM,EAAe,GAAoB,EAAc,EAAU,EAAW,WAAY,EAAS,SAAS,EAGpG,EAAa,GAAa,EAAc,EAAc,EAAW,cAAe,EAAW,QAAS,EAAW,WAAY,EAAS,SAAS,EACnJ,GAAI,IAAe,KAAM,CACvB,EAAO,GAAG,OACR,qCAAqC,EAAW,+CAEhD,OACF,EACA,MACF,CAIA,IAAM,EAAc,KAAK,UAAU,EAAW,WAAY,KAAM,CAAC,EAM3D,GALkB,EAAS,YAAc,WAC3C,EAAS,qBAAuB,EAChC,EAAS,YAAc,gBACrB,EAAS,0BAA4B,EACrC,EAAS,mBAAqB,GACI,2BAA6B,sBAC/D,EAAe,EAAS,YAAc,WACxC,cAAc,EAAW,cAAc,gCAAgC,EAAc,GACrF,EAAS,YAAc,gBACrB,cAAc,EAAW,cAAc,2BAA2B,EAAW,QAAQ,eAAe,EAAc,GAClH,cAAc,EAAW,cAAc,mBAAmB,EAAW,QAAQ,4BAA4B,EAAc,GAEvH,EAAK,GAAgB,EACrB,EAAa,GAAG,EAAiB,0BAA0B,IAE3D,EAAgB,EAAS,YAAc,WACzC,mCAAmC,EAAS,YAAY,QAAU,EAAE,oBAAoB,EAAW,cAAc,kCACjH,oDAAoD,EAAW,cAAc,uCAAuC,EAAW,QAAQ,KAErI,EAAe,CACnB,4BAA4B,EAAyB,EAAE,GACvD,GACA,aAAa,IACb,cAAc,EAAS,UAAU,WAAW,EAAS,SACrD,wBACA,EACA,GACA,mBACA,EACA,iDAAiD,IACjD,wEACA,iGACF,EAaA,GAZI,EAA8B,IAAI,EAAS,CAAK,CAAC,GACnD,EAAa,KACX,GACA,qEACA,kEACA,2DACA,oDACF,EAEF,EAAa,KAAK,GAAI,sBAAsB,EAGxC,CAAC,MADmB,EAAO,GAAG,QAAQ,wBAAyB,EAAa,KAAK;CAAI,CAAC,EAC1E,CACd,EAAO,GAAG,OAAO,0CAA2C,MAAM,EAClE,MACF,CAGA,GAAI,CACF,IAAM,EAAS,MAAM,GACnB,EACA,EACC,GAAgB,GACf,EACA,EACA,EAAW,cACX,EAAW,QACX,EAAW,WACX,EAAS,SACX,CACF,EACA,GAAI,mBAAoB,EAAQ,CAC9B,EAAO,GAAG,OACR,mCAAmC,EAAO,eAAe,kBACxC,EAAW,yCAC5B,OACF,EACA,MACF,CAEA,EAAO,GAAG,OACR,oBAAoB,EAAyB,EAAE,sBAC3B,EAAW,4DAE/B,MACF,CACF,OAAS,EAAY,CACnB,EAAO,GAAG,OACR,mBAAmB,aAAsB,MAAQ,EAAW,QAAU,OAAO,CAAU,EAAE,sBACpE,IACrB,OACF,CACF,CACF,KAAO,CAEL,GAAI,EAAO,MAAO,CAChB,IAAM,EAAc,CAClB,yCACA,2CACA,oCACA,qCACA,kCACA,oDACA,wDACA,2CACA,QACF,EACM,EAAS,MAAM,EAAO,GAAG,OAAO,kBAAmB,CAAW,EACpE,GAAI,IAAW,EAAY,GACzB,MAAM,EAA4B,SAAU,CAAM,OAC7C,GAAI,IAAW,EAAY,GAChC,MAAM,EAA4B,UAAW,CAAM,OAC9C,GAAI,IAAW,EAAY,GAChC,MAAM,EAA4B,SAAU,CAAM,OAC7C,GAAI,IAAW,EAAY,GAChC,MAAM,EAA4B,QAAS,CAAM,OAC5C,GAAI,IAAW,EAAY,GAChC,MAAM,EAA4B,SAAU,CAAM,OAC7C,GAAI,IAAW,EAAY,GAChC,MAAM,EAA4B,MAAO,CAAM,OAC1C,GAAI,IAAW,EAAY,GAAI,CACpC,IAAM,EAAc,CAAC,4CAA6C,4CAA6C,oCAAqC,QAAQ,EACtJ,EAAa,MAAM,EAAO,GAAG,OAAO,0BAA2B,CAAW,EAC1E,EAAW,IAAe,EAAY,GACxC,QACA,IAAe,EAAY,GACzB,UACA,IAAe,EAAY,GACzB,UACA,IAAA,GACJ,GAAU,MAAM,EAA4B,sBAAsB,IAAY,CAAM,CAC1F,MAAW,IAAW,EAAY,IAChC,MAAM,EAA4B,QAAS,CAAM,EAGnD,MACF,CAGA,IAAM,EAAsB,CAAC,EAC7B,EAAU,KAAK,+BAA+B,EAC9C,EAAU,KAAK,mEAAmE,EAClF,EAAU,KAAK,oEAAoE,EACnF,EAAU,KAAK,kFAAkF,EACjG,EAAU,KAAK,6DAA6D,EAC5E,EAAU,KAAK,uDAAuD,EACtE,EAAU,KAAK,4EAA4E,EAC3F,EAAU,KAAK,sEAAsE,EACrF,EAAU,KAAK,2FAA2F,EAC1G,EAAU,KAAK,EAAE,EACjB,EAAU,KAAK,GAA2B,CAAC,EAC3C,IAAM,EAAqB,GAAuB,EAAwB,EAG1E,GAFA,EAAU,KAAK,sBAAsB,EAAmB,KAAK,IAAI,EAAmB,OAAO,EAAE,EAC7F,EAAU,KAAK,EAAE,EACb,EAAO,CACT,IAAM,EAAa,EAAS,CAAK,EAC3B,EAAU,GAAmC,CAAK,EACxD,GAAI,EAAQ,OAAS,EACnB,EAAU,KAAK,qBAAqB,EAAW,oBAAoB,EAAQ,KAAK,IAAI,GAAG,EACvF,EAAU,KAAK,sDAAsD,OAChE,GAAI,GAAmC,CAAK,GAAK,GAAgC,CAAK,GAAK,GAAwB,CAAK,EAC7H,EAAU,KAAK,mBAAmB,EAAW,4BAA4B,MACpE,CACL,EAAU,KAAK,oBAAoB,EAAW,gCAAgC,EAC9E,IAAM,EAAc,GAAiC,CAAK,CAAC,CAAC,MAAM,CAAC,EACnE,IAAK,IAAM,KAAQ,EAAa,EAAU,KAAK,CAAI,CACrD,CACF,MACE,EAAU,KAAK,2BAA2B,EAE5C,EAAO,GAAG,OAAO,EAAU,KAAK;CAAI,EAAG,MAAM,CAC/C,CACJ,CAEA,EAAG,gBAAgB,kBAAmB,CACpC,YAAa,2CACb,uBAAwB,GACxB,QAAS,CACX,CAAC,CACH"}
|