umans-gate 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/updater.ts","../src/uninstaller.ts","../src/cli.ts","../package.json","../src/banner.ts","../src/index.ts","../src/db.ts","../src/compress.ts","../src/logger.ts","../src/economics.ts","../src/usage/sse-parse.ts","../src/models/name.ts","../src/usage/helpers.ts","../src/usage/extract.ts","../src/usage/ddl.ts","../src/vision-description-store.ts","../src/shared/http-headers.ts","../src/shared/text-codec.ts","../src/shared/capture-summary.ts","../src/shared/classify-429.ts","../src/shared/weight.ts","../src/limiter/types.ts","../src/limiter/circuit-breaker.ts","../src/limiter/gate.ts","../src/metrics.ts","../src/model-info-parser.ts","../src/models/fetch-info.ts","../src/models.ts","../src/proxy.ts","../src/stamp-pipeline.ts","../src/model-policy.ts","../src/stamp-reasoning.ts","../src/stamp-temperature.ts","../src/stamp-thinking.ts","../src/stamp-topk.ts","../src/stamp.ts","../src/queue.ts","../src/rate.ts","../src/usage/fetch-usage.ts","../src/usage/parser.ts","../src/usage/reconciler.ts","../src/usage/aggregator.ts","../src/viewer.ts","../src/vision/cache.ts","../src/vision/detect.ts","../src/vision/transcode.ts","../src/vision/wrapper.ts","../src/vision/handoff.ts","../src/vision/persistent-cache.ts","../src/vision/sink.ts","../src/warmer.ts","../src/workers/worker-store.ts","../src/ws.ts"],"sourcesContent":["// Configuration: JSON config file (single source of truth) + env overrides.\n// Config path resolution (cross-OS):\n// Linux/macOS: $XDG_CONFIG_HOME/umans-gate/config.json or ~/.config/umans-gate/config.json\n// Windows: %APPDATA%/umans-gate/config.json\n// Precedence: env vars > JSON config file > built-in defaults.\n// On first run, a config.json is written to the resolved path.\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type {\n IncomingProtocol,\n OutputConfig,\n ProxyConfig,\n ThinkingConfig,\n UpstreamProtocol,\n} from \"./types.js\";\n\n/** Resolve the config directory path following OS conventions. */\nexport function resolveConfigDir(): string {\n const p = platform();\n if (p === \"win32\") {\n const appData = process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n return join(appData, \"umans-gate\");\n }\n // Linux, macOS, and fallback.\n const xdg = process.env.XDG_CONFIG_HOME;\n const base = xdg && xdg.length > 0 ? xdg : join(homedir(), \".config\");\n return join(base, \"umans-gate\");\n}\n\n/** Resolve the config file path (JSON single source of truth). */\nexport function resolveConfigPath(): string {\n return join(resolveConfigDir(), \"config.json\");\n}\n\n/**\n * Fields removed from user config (hardcoded — app is Umans-specific):\n * target → \"https://api.code.umans.ai\"\n * openai_path → \"chat/completions\"\n * warmer_path → \"/v1/models\"\n * rate_limit_window_seconds → derived from /v1/usage (inherent, not configurable)\n * vision_target → derived from target + \"/v1/chat/completions\"\n * host → hardcoded \"127.0.0.1\" (local-only; use SSH tunnel for remote access)\n */\nexport interface RawConfig {\n port?: number;\n max_captures?: number;\n db_path?: string;\n idle_timeout?: number;\n upstream_protocol?: string;\n /** When true, applies the full Claude Code stamp bundle on Anthropic requests (TTL, top_k, max_tokens, thinking, output_config, context_management). */\n stamp_claude_code_enabled?: boolean;\n /** When true, stamps reasoning_effort onto OpenAI-compatible requests (effort=max for umans-glm* models, effort=high for all others) and removes max_tokens/thinking. */\n stamp_reasoning_effort_enabled?: boolean;\n warmer_enabled?: boolean;\n warmer_interval_ms?: number;\n umans_api_key?: string;\n usage_refresh_ms?: number;\n models_refresh_ms?: number;\n concurrency_hard_cap?: number;\n concurrency_soft_limit?: number;\n /** Pro-tier rolling-window request limit. -1 = unlimited (no limiter), 0 = auto-derive from /v1/usage, >0 = explicit limit. */\n rate_limit_requests?: number;\n queue_timeout_ms?: number;\n max_queue_depth?: number;\n release_cooldown_ms?: number;\n breaker_threshold?: number;\n breaker_window_ms?: number;\n breaker_cooldown_ms?: number;\n vision_strategy?: \"never\" | \"catalog\" | \"always\";\n vision_model?: string;\n vision_prompt?: string;\n vision_prompt_version?: number;\n vision_max_images?: number;\n vision_max_description_tokens?: number;\n vision_reasoning_effort?: \"none\" | \"low\" | \"medium\" | \"high\" | null;\n vision_timeout_ms?: number;\n vision_cache_size?: number;\n vision_cache_ttl_ms?: number;\n vision_cache_max_rows?: number;\n vision_persistent_cache?: boolean;\n vision_concurrency?: number;\n vision_max_dimension?: number;\n vision_jpeg_quality?: number;\n vision_image_format?: \"jpeg\" | \"png\";\n vision_image_detail?: \"auto\" | \"low\" | \"high\";\n concurrency_main_reservation?: number;\n concurrency_vision_reservation?: number;\n /** Max captured request/response body size in bytes. 0 = unlimited. */\n capture_body_max_bytes?: number;\n /** Max depth of the write-behind response queue. Distinct from waiters queue. */\n queue_max_depth?: number;\n /** WebSocket backpressure limit in bytes. 0 = use Bun default. */\n ws_backpressure_limit?: number;\n /** Close WebSocket connections that exceed the backpressure limit. */\n ws_close_on_backpressure_limit?: boolean;\n /** Max pending vision requests to batch together. */\n vision_pending_max_batch?: number;\n /** Compress stored request/response bodies with zstd. Default true (on). */\n compression_enabled?: boolean;\n upstream_timeout_ms?: number;\n}\n\n/**\n * Input shape accepted by validateConfig/saveConfig. Numeric fields accept\n * strings because HTML form inputs and env vars produce strings. Coercion\n * to RawConfig happens inside validateConfig.\n */\nexport type RawConfigInput = {\n [K in keyof RawConfig]?: NonNullable<RawConfig[K]> extends number\n ? RawConfig[K] | string\n : RawConfig[K];\n};\n\n/** Hardcoded constants — app is Umans-specific, not user-configurable. */\nexport const UPSTREAM_TARGET = \"https://api.code.umans.ai\";\nexport const OPENAI_CHAT_PATH = \"chat/completions\";\nexport const WARMER_PATH = \"/v1/models\";\n/** Vision target derived from upstream target. */\nexport const VISION_TARGET_PATH = \"/v1/chat/completions\";\n/** Stamp TTL value used when stamp_claude_code_enabled is true. */\nexport const STAMP_CACHE_TTL_VALUE = \"1h\";\n/** Top-K value used when stamp_claude_code_enabled is true. */\nexport const STAMP_TOP_K_VALUE = 20;\n/** Temperature value forced when stamp_claude_code_enabled is true. */\nexport const STAMP_TEMPERATURE_VALUE = 1.0;\n/** Thinking block injected when stamp_claude_code_enabled is true. */\nexport const STAMP_THINKING_VALUE: ThinkingConfig = {\n type: \"adaptive\",\n};\n\n/** max_tokens injected for umans-glm* models when stamp_claude_code_enabled is true. */\nexport const STAMP_MAX_TOKENS_GLM_VALUE = 131071;\n\n/** max_tokens injected for non-GLM models when stamp_claude_code_enabled is true. */\nexport const STAMP_MAX_TOKENS_VALUE = 32767;\n\n/** output_config injected for non-GLM models when stamp_claude_code_enabled is true. */\nexport const STAMP_OUTPUT_CONFIG_VALUE: OutputConfig = {\n effort: \"high\",\n};\n\n/** output_config injected for umans-glm* models when stamp_claude_code_enabled is true. */\nexport const STAMP_OUTPUT_CONFIG_GLM_VALUE: OutputConfig = {\n effort: \"max\",\n};\n\n/** anthropic-beta header injected when stamp_claude_code_enabled is true (Anthropic requests only). */\nexport const STAMP_ANTHROPIC_BETA_HEADER =\n \"claude-code-20250219,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,thinking-token-count-2026-05-13,context-management-2025-06-27,prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07,advisor-tool-2026-03-01,effort-2025-11-24,extended-cache-ttl-2025-04-11\";\n\n/** context_management block injected when stamp_claude_code_enabled is true and anthropic-version is 2023-06-01. */\nexport const STAMP_CONTEXT_MANAGEMENT_VALUE = {\n edits: [{ type: \"clear_thinking_20251015\", keep: \"all\" }],\n} as const;\n\nexport const STAMP_REASONING_EFFORT_VALUE = \"high\" as const;\nexport const STAMP_REASONING_EFFORT_GLM_VALUE = \"max\" as const;\n\n/** The default config written on first run. */\nconst DEFAULT_CONFIG: RawConfig = {\n port: 1945,\n max_captures: 200,\n db_path: \"./umans-gate.db\",\n idle_timeout: 255,\n upstream_protocol: \"http1.1\",\n stamp_claude_code_enabled: false,\n stamp_reasoning_effort_enabled: false,\n warmer_enabled: true,\n warmer_interval_ms: 20000,\n umans_api_key: \"\",\n usage_refresh_ms: 60000,\n models_refresh_ms: 3600000,\n concurrency_hard_cap: 1,\n concurrency_soft_limit: 1,\n rate_limit_requests: 0,\n queue_timeout_ms: 30000,\n max_queue_depth: 256,\n release_cooldown_ms: 1000,\n breaker_threshold: 5,\n breaker_window_ms: 300000,\n breaker_cooldown_ms: 60000,\n vision_strategy: \"catalog\",\n vision_model: \"umans-flash\",\n vision_prompt:\n \"You are an expert visual analyst with perfect vision and meticulous attention to detail. Your task is to produce an exhaustive, accurate description of an image for a downstream text-only language model that cannot see the image.\\n\\nStructure your description as:\\n\\n1. IMAGE TYPE: What kind of image is this (photograph, screenshot, diagram, chart, illustration, document scan, UI mockup, etc.)?\\n\\n2. OVERALL CONTENT: A comprehensive summary of everything visible.\\n\\n3. TEXT/OCR: Transcribe ALL visible text exactly as written, preserving:\\n - Original spelling, formatting, and hierarchy\\n - Line breaks and spatial layout\\n - Numbers, codes, identifiers, and labels\\n - Captions, watermarks, signatures\\n If text is partially visible, transcribe what you can and mark gaps with [...].\\n\\n4. VISUAL ELEMENTS: Describe in detail:\\n - Objects, people, and their positions/relationships\\n - Colors, shapes, textures\\n - Spatial layout and composition\\n - UI elements (buttons, menus, fields, tabs) if a screenshot\\n\\n5. DATA/CHARTS: If charts, tables, or data visualizations are present:\\n - Chart type and axes\\n - Data values, ranges, and trends\\n - Table structure and cell contents\\n\\n6. CONTEXTUAL CLUES: Date/time indicators, language, cultural context, technical domain indicators.\\n\\n7. QUALITY NOTES: Any blur, artifacts, obstructions, or ambiguity.\\n\\nRules:\\n- Describe what is VISIBLE, not what you infer.\\n- Be exhaustive: omit nothing visible. When in doubt, include it.\\n- For uncertain elements, state your uncertainty rather than guessing.\\n- Do not summarize or abbreviate.\\n- Output only the description, no preamble.\",\n vision_prompt_version: 2,\n vision_max_images: 5,\n vision_max_description_tokens: 4096,\n vision_reasoning_effort: \"none\",\n vision_timeout_ms: 0,\n vision_cache_size: 1000,\n vision_cache_ttl_ms: 604800000,\n vision_cache_max_rows: 10000,\n vision_persistent_cache: true,\n vision_concurrency: 1,\n vision_max_dimension: 2048,\n vision_jpeg_quality: 92,\n vision_image_format: \"png\",\n vision_image_detail: \"high\",\n concurrency_main_reservation: 1,\n concurrency_vision_reservation: 1,\n capture_body_max_bytes: 10_000_000,\n queue_max_depth: 100,\n ws_backpressure_limit: 1_048_576,\n ws_close_on_backpressure_limit: true,\n vision_pending_max_batch: 50,\n compression_enabled: true,\n upstream_timeout_ms: 300000,\n};\n\n/** Validation result. */\nexport interface ValidationResult {\n ok: boolean;\n errors: string[];\n warnings: string[];\n normalized: RawConfig;\n}\n\n/** Reload result returned by the reload API. */\nexport interface ReloadResult {\n ok: boolean;\n errors: string[];\n warnings: string[];\n applied: string[];\n restartRequired: string[];\n configPath: string;\n}\n\n/**\n * Fields stored as integers on disk. The UI sends strings from <input> elements,\n * so coerce numeric strings to numbers before validation and before writing.\n * This is defense-in-depth: protects both the UI path and direct API callers.\n */\nconst INT_FIELDS: (keyof RawConfig)[] = [\n \"port\",\n \"max_captures\",\n \"idle_timeout\",\n \"warmer_interval_ms\",\n \"usage_refresh_ms\",\n \"models_refresh_ms\",\n \"concurrency_hard_cap\",\n \"concurrency_soft_limit\",\n \"rate_limit_requests\",\n \"queue_timeout_ms\",\n \"max_queue_depth\",\n \"release_cooldown_ms\",\n \"breaker_threshold\",\n \"breaker_window_ms\",\n \"breaker_cooldown_ms\",\n \"vision_prompt_version\",\n \"vision_max_images\",\n \"vision_max_description_tokens\",\n \"vision_timeout_ms\",\n \"vision_cache_size\",\n \"vision_cache_ttl_ms\",\n \"vision_cache_max_rows\",\n \"vision_max_dimension\",\n \"vision_jpeg_quality\",\n \"vision_concurrency\",\n \"concurrency_main_reservation\",\n \"concurrency_vision_reservation\",\n \"capture_body_max_bytes\",\n \"queue_max_depth\",\n \"ws_backpressure_limit\",\n \"vision_pending_max_batch\",\n \"upstream_timeout_ms\",\n];\n\n/**\n * Coerce a raw config patch so that numeric strings become numbers and empty\n * strings for nullable fields become null. HTML form inputs always produce\n * strings; without this, Number.isInteger(\"7777\") === false and validation\n * rejects every numeric field the UI sends.\n *\n * Returns a new object; does not mutate the input.\n */\nfunction coerceRawForValidation(raw: RawConfigInput): RawConfig {\n const out = { ...raw } as Record<string, unknown>;\n // Strip keys that are no longer in RawConfig (e.g. background_vision,\n // vision_force_intercept_capable, use_write_worker — now derived/hardcoded).\n // This prevents dead keys from persisting through save cycles.\n const knownKeys = new Set(Object.keys(DEFAULT_CONFIG));\n for (const k of Object.keys(out)) {\n if (!knownKeys.has(k)) {\n delete out[k];\n }\n }\n for (const k of INT_FIELDS) {\n const v = out[k];\n if (typeof v === \"string\" && v.length > 0) {\n const n = Number(v);\n if (!Number.isNaN(n)) {\n out[k] = n;\n }\n }\n }\n return out as unknown as RawConfig;\n}\n\ninterface FieldRule {\n name: string;\n errors: (n: RawConfig) => string[];\n}\n\ninterface WarningRule {\n name: string;\n warning: (n: RawConfig) => string | null;\n}\nconst FIELD_RULES: FieldRule[] = [\n {\n name: \"port\",\n errors: (n) =>\n n.port !== undefined && (!Number.isInteger(n.port) || n.port < 1 || n.port > 65535)\n ? [\"port must be an integer between 1 and 65535\"]\n : [],\n },\n {\n name: \"max_captures\",\n errors: (n) =>\n n.max_captures !== undefined && (!Number.isInteger(n.max_captures) || n.max_captures < 1)\n ? [\"max_captures must be a positive integer\"]\n : [],\n },\n {\n name: \"db_path\",\n errors: (n) =>\n n.db_path !== undefined && (typeof n.db_path !== \"string\" || n.db_path.length === 0)\n ? [\"db_path must be a non-empty string\"]\n : [],\n },\n {\n name: \"idle_timeout\",\n errors: (n) =>\n n.idle_timeout !== undefined &&\n (!Number.isInteger(n.idle_timeout) || n.idle_timeout < 1 || n.idle_timeout > 255)\n ? [\"idle_timeout must be an integer between 1 and 255\"]\n : [],\n },\n {\n name: \"upstream_protocol\",\n errors: (n) => {\n if (n.upstream_protocol === undefined) return [];\n const v = String(n.upstream_protocol).toLowerCase();\n return v !== \"http1.1\" && v !== \"http2\" && v !== \"h2\"\n ? [\"upstream_protocol must be 'http1.1' or 'http2'\"]\n : [];\n },\n },\n ...(\n [\"stamp_claude_code_enabled\", \"stamp_reasoning_effort_enabled\", \"compression_enabled\"] as const\n ).map((field) => ({\n name: field,\n errors: (n: RawConfig) =>\n n[field] !== undefined && typeof n[field] !== \"boolean\" ? [`${field} must be a boolean`] : [],\n })),\n {\n name: \"warmer_enabled\",\n errors: (n) =>\n n.warmer_enabled !== undefined && typeof n.warmer_enabled !== \"boolean\"\n ? [\"warmer_enabled must be a boolean\"]\n : [],\n },\n {\n name: \"warmer_interval_ms\",\n errors: (n) =>\n n.warmer_interval_ms !== undefined &&\n (!Number.isInteger(n.warmer_interval_ms) || n.warmer_interval_ms < 1000)\n ? [\"warmer_interval_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"umans_api_key\",\n errors: (n) =>\n n.umans_api_key !== undefined && typeof n.umans_api_key !== \"string\"\n ? [\"umans_api_key must be a string\"]\n : [],\n },\n {\n name: \"usage_refresh_ms\",\n errors: (n) =>\n n.usage_refresh_ms !== undefined &&\n (!Number.isInteger(n.usage_refresh_ms) || n.usage_refresh_ms < 1000)\n ? [\"usage_refresh_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"models_refresh_ms\",\n errors: (n) =>\n n.models_refresh_ms !== undefined &&\n (!Number.isInteger(n.models_refresh_ms) || n.models_refresh_ms < 1000)\n ? [\"models_refresh_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"concurrency_hard_cap\",\n errors: (n) =>\n n.concurrency_hard_cap !== undefined &&\n (!Number.isInteger(n.concurrency_hard_cap) || n.concurrency_hard_cap < 1)\n ? [\"concurrency_hard_cap must be an integer >= 1\"]\n : [],\n },\n {\n name: \"concurrency_soft_limit\",\n errors: (n) =>\n n.concurrency_soft_limit !== undefined &&\n (!Number.isInteger(n.concurrency_soft_limit) || n.concurrency_soft_limit < 1)\n ? [\"concurrency_soft_limit must be an integer >= 1\"]\n : [],\n },\n {\n // Cross-field: only checked when hard_cap is an integer >= 3.\n name: \"concurrency_main_reservation\",\n errors: (n) => {\n if (\n n.concurrency_hard_cap === undefined ||\n !Number.isInteger(n.concurrency_hard_cap) ||\n n.concurrency_main_reservation === undefined\n ) {\n return [];\n }\n const resMax = n.concurrency_hard_cap - 2;\n if (resMax < 1) return [];\n if (!Number.isInteger(n.concurrency_main_reservation) || n.concurrency_main_reservation < 1) {\n return [\"concurrency_main_reservation must be a positive integer (min 1)\"];\n }\n if (n.concurrency_main_reservation > resMax) {\n return [`concurrency_main_reservation must be <= hard_cap - 2 (=${resMax})`];\n }\n return [];\n },\n },\n {\n // Cross-field: only checked when hard_cap is an integer >= 3.\n name: \"concurrency_vision_reservation\",\n errors: (n) => {\n if (\n n.concurrency_hard_cap === undefined ||\n !Number.isInteger(n.concurrency_hard_cap) ||\n n.concurrency_vision_reservation === undefined\n ) {\n return [];\n }\n const resMax = n.concurrency_hard_cap - 2;\n if (resMax < 1) return [];\n if (\n !Number.isInteger(n.concurrency_vision_reservation) ||\n n.concurrency_vision_reservation < 1\n ) {\n return [\"concurrency_vision_reservation must be a positive integer (min 1)\"];\n }\n if (n.concurrency_vision_reservation > resMax) {\n return [`concurrency_vision_reservation must be <= hard_cap - 2 (=${resMax})`];\n }\n return [];\n },\n },\n {\n name: \"rate_limit_requests\",\n errors: (n) =>\n n.rate_limit_requests !== undefined &&\n n.rate_limit_requests !== null &&\n (!Number.isInteger(n.rate_limit_requests) || n.rate_limit_requests < -1)\n ? [\n \"rate_limit_requests must be -1 (unlimited), 0 (auto-derive from /v1/usage), or a positive integer\",\n ]\n : [],\n },\n {\n name: \"queue_timeout_ms\",\n errors: (n) =>\n n.queue_timeout_ms !== undefined &&\n (!Number.isInteger(n.queue_timeout_ms) || n.queue_timeout_ms < 100)\n ? [\"queue_timeout_ms must be an integer >= 100\"]\n : [],\n },\n {\n name: \"max_queue_depth\",\n errors: (n) =>\n n.max_queue_depth !== undefined &&\n (!Number.isInteger(n.max_queue_depth) || n.max_queue_depth < 1)\n ? [\"max_queue_depth must be a positive integer\"]\n : [],\n },\n {\n name: \"release_cooldown_ms\",\n errors: (n) =>\n n.release_cooldown_ms !== undefined &&\n (!Number.isInteger(n.release_cooldown_ms) || n.release_cooldown_ms < 0)\n ? [\"release_cooldown_ms must be a non-negative integer\"]\n : [],\n },\n {\n name: \"breaker_threshold\",\n errors: (n) =>\n n.breaker_threshold !== undefined &&\n (!Number.isInteger(n.breaker_threshold) || n.breaker_threshold < 1)\n ? [\"breaker_threshold must be a positive integer\"]\n : [],\n },\n {\n name: \"breaker_window_ms\",\n errors: (n) =>\n n.breaker_window_ms !== undefined &&\n (!Number.isInteger(n.breaker_window_ms) || n.breaker_window_ms < 1000)\n ? [\"breaker_window_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"breaker_cooldown_ms\",\n errors: (n) =>\n n.breaker_cooldown_ms !== undefined &&\n (!Number.isInteger(n.breaker_cooldown_ms) || n.breaker_cooldown_ms < 1000)\n ? [\"breaker_cooldown_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"vision_strategy\",\n errors: (n) =>\n n.vision_strategy !== undefined && ![\"never\", \"catalog\", \"always\"].includes(n.vision_strategy)\n ? [\"vision_strategy must be 'never', 'catalog', or 'always'\"]\n : [],\n },\n {\n name: \"vision_model\",\n errors: (n) =>\n n.vision_model !== undefined && typeof n.vision_model !== \"string\"\n ? [\"vision_model must be a string\"]\n : [],\n },\n {\n name: \"vision_prompt\",\n errors: (n) =>\n n.vision_prompt !== undefined &&\n (typeof n.vision_prompt !== \"string\" || n.vision_prompt.length === 0)\n ? [\"vision_prompt must be a non-empty string\"]\n : [],\n },\n {\n name: \"vision_prompt_version\",\n errors: (n) =>\n n.vision_prompt_version !== undefined &&\n (!Number.isInteger(n.vision_prompt_version) || n.vision_prompt_version < 1)\n ? [\"vision_prompt_version must be a positive integer\"]\n : [],\n },\n {\n name: \"vision_max_images\",\n errors: (n) =>\n n.vision_max_images !== undefined &&\n (!Number.isInteger(n.vision_max_images) ||\n n.vision_max_images < 1 ||\n n.vision_max_images > 100)\n ? [\"vision_max_images must be an integer between 1 and 100\"]\n : [],\n },\n {\n name: \"vision_max_description_tokens\",\n errors: (n) =>\n n.vision_max_description_tokens !== undefined &&\n (!Number.isInteger(n.vision_max_description_tokens) ||\n n.vision_max_description_tokens < 1 ||\n n.vision_max_description_tokens > 200000)\n ? [\"vision_max_description_tokens must be an integer between 1 and 200000\"]\n : [],\n },\n {\n name: \"vision_timeout_ms\",\n errors: (n) =>\n n.vision_timeout_ms !== undefined &&\n (!Number.isInteger(n.vision_timeout_ms) || n.vision_timeout_ms < 0)\n ? [\"vision_timeout_ms must be a non-negative integer (0 = no timeout)\"]\n : [],\n },\n {\n name: \"vision_cache_size\",\n errors: (n) =>\n n.vision_cache_size !== undefined &&\n (!Number.isInteger(n.vision_cache_size) || n.vision_cache_size < 100)\n ? [\"vision_cache_size must be an integer >= 100\"]\n : [],\n },\n {\n name: \"vision_concurrency\",\n errors: (n) =>\n n.vision_concurrency !== undefined &&\n (!Number.isInteger(n.vision_concurrency) ||\n n.vision_concurrency < 1 ||\n n.vision_concurrency > 20)\n ? [\"vision_concurrency must be an integer between 1 and 20\"]\n : [],\n },\n {\n name: \"vision_reasoning_effort\",\n errors: (n) =>\n n.vision_reasoning_effort !== undefined &&\n n.vision_reasoning_effort !== null &&\n ![\"none\", \"low\", \"medium\", \"high\"].includes(n.vision_reasoning_effort)\n ? [\"vision_reasoning_effort must be 'none', 'low', 'medium', 'high', or null\"]\n : [],\n },\n {\n name: \"vision_max_dimension\",\n errors: (n) =>\n n.vision_max_dimension !== undefined &&\n (!Number.isInteger(n.vision_max_dimension) ||\n n.vision_max_dimension < 256 ||\n n.vision_max_dimension > 8192)\n ? [\"vision_max_dimension must be an integer between 256 and 8192\"]\n : [],\n },\n {\n name: \"vision_jpeg_quality\",\n errors: (n) =>\n n.vision_jpeg_quality !== undefined &&\n (!Number.isInteger(n.vision_jpeg_quality) ||\n n.vision_jpeg_quality < 1 ||\n n.vision_jpeg_quality > 100)\n ? [\"vision_jpeg_quality must be an integer between 1 and 100\"]\n : [],\n },\n {\n name: \"vision_image_format\",\n errors: (n) =>\n n.vision_image_format !== undefined && ![\"jpeg\", \"png\"].includes(n.vision_image_format)\n ? [\"vision_image_format must be 'jpeg' or 'png'\"]\n : [],\n },\n {\n name: \"vision_image_detail\",\n errors: (n) =>\n n.vision_image_detail !== undefined &&\n ![\"auto\", \"low\", \"high\"].includes(n.vision_image_detail)\n ? [\"vision_image_detail must be 'auto', 'low', or 'high'\"]\n : [],\n },\n {\n name: \"vision_cache_ttl_ms\",\n errors: (n) =>\n n.vision_cache_ttl_ms !== undefined &&\n (!Number.isInteger(n.vision_cache_ttl_ms) || n.vision_cache_ttl_ms < 1000)\n ? [\"vision_cache_ttl_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"vision_cache_max_rows\",\n errors: (n) =>\n n.vision_cache_max_rows !== undefined &&\n (!Number.isInteger(n.vision_cache_max_rows) || n.vision_cache_max_rows < 100)\n ? [\"vision_cache_max_rows must be an integer >= 100\"]\n : [],\n },\n {\n name: \"vision_persistent_cache\",\n errors: (n) =>\n n.vision_persistent_cache !== undefined && typeof n.vision_persistent_cache !== \"boolean\"\n ? [\"vision_persistent_cache must be a boolean\"]\n : [],\n },\n {\n name: \"capture_body_max_bytes\",\n errors: (n) =>\n n.capture_body_max_bytes !== undefined &&\n (!Number.isInteger(n.capture_body_max_bytes) || n.capture_body_max_bytes < 0)\n ? [\"capture_body_max_bytes must be a non-negative integer (0 = unlimited)\"]\n : [],\n },\n {\n name: \"queue_max_depth\",\n errors: (n) =>\n n.queue_max_depth !== undefined &&\n (!Number.isInteger(n.queue_max_depth) || n.queue_max_depth < 1)\n ? [\"queue_max_depth must be a positive integer\"]\n : [],\n },\n {\n name: \"ws_backpressure_limit\",\n errors: (n) =>\n n.ws_backpressure_limit !== undefined &&\n (!Number.isInteger(n.ws_backpressure_limit) || n.ws_backpressure_limit < 0)\n ? [\"ws_backpressure_limit must be a non-negative integer (0 = Bun default)\"]\n : [],\n },\n {\n name: \"ws_close_on_backpressure_limit\",\n errors: (n) =>\n n.ws_close_on_backpressure_limit !== undefined &&\n typeof n.ws_close_on_backpressure_limit !== \"boolean\"\n ? [\"ws_close_on_backpressure_limit must be a boolean\"]\n : [],\n },\n {\n name: \"vision_pending_max_batch\",\n errors: (n) =>\n n.vision_pending_max_batch !== undefined &&\n (!Number.isInteger(n.vision_pending_max_batch) || n.vision_pending_max_batch < 1)\n ? [\"vision_pending_max_batch must be a positive integer\"]\n : [],\n },\n];\n\nconst WARNING_RULES: WarningRule[] = [\n {\n name: \"warmer_disabled\",\n warning: (n) =>\n n.warmer_enabled === false\n ? \"Connection warmer is disabled — first request after idle will have ~750ms cold-start penalty\"\n : null,\n },\n {\n name: \"rate_limit_disabled\",\n warning: (n) =>\n n.rate_limit_requests === -1\n ? \"Rate limiting is unlimited (rate_limit_requests=-1). No request cap is enforced.\"\n : null,\n },\n {\n name: \"stamp_claude_code_off\",\n warning: (n) =>\n n.stamp_claude_code_enabled !== true\n ? \"Claude Code stamping is off — ephemeral cache entries will have no default TTL, no top_k/max_tokens/thinking/output_config/context_management injection\"\n : null,\n },\n {\n name: \"umans_api_key_empty\",\n warning: (n) =>\n n.umans_api_key === \"\" || n.umans_api_key === undefined\n ? \"umans_api_key is empty — proxy runs in fail-safe mode (worst-case limits, priority_low=true). Set umans_api_key in the Server section to enable usage-based limits.\"\n : null,\n },\n];\n\n/**\n * Validate a raw config object. Returns errors (blocking), warnings (non-blocking),\n * and a normalized copy with defaults filled in.\n */\nexport function validateConfig(raw: RawConfigInput): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const coerced = coerceRawForValidation(raw);\n const n: RawConfig = { ...DEFAULT_CONFIG, ...coerced };\n\n for (const rule of FIELD_RULES) {\n errors.push(...rule.errors(n));\n }\n\n for (const rule of WARNING_RULES) {\n const msg = rule.warning(n);\n if (msg !== null) {\n warnings.push(msg);\n }\n }\n\n return { ok: errors.length === 0, errors, warnings, normalized: n };\n}\n\n/**\n * Write the default config template if no config file exists.\n */\nexport function ensureConfigFile(): string {\n const path = resolveConfigPath();\n if (!existsSync(path)) {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(DEFAULT_CONFIG, null, 2), \"utf-8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n // File permissions are best-effort — some filesystems (e.g. Windows) don't support chmod.\n }\n }\n return path;\n}\n\nfunction resolveUpstreamProtocol(raw: string | undefined): UpstreamProtocol {\n const v = (raw ?? \"http1.1\").toLowerCase();\n if (v === \"http2\" || v === \"h2\") return \"http2\";\n return \"http1.1\";\n}\n\nfunction num(val: number | string | undefined | null, fallback: number): number {\n if (val === undefined || val === null) return fallback;\n const n = Number(val);\n return Number.isNaN(n) ? fallback : n;\n}\n\nfunction str(val: string | undefined, fallback: string): string {\n return val ?? fallback;\n}\n\nfunction loadJsonConfig(path: string): RawConfig {\n if (!existsSync(path)) return {};\n try {\n const text = readFileSync(path, \"utf-8\");\n const parsed = JSON.parse(text) as RawConfig;\n return parsed ?? {};\n } catch {\n return {};\n }\n}\n\n/**\n * Coerce a raw config value to a boolean from various input shapes.\n * Accepts true/false, \"true\"/\"false\", 1/0.\n */\nfunction bool(val: unknown, fallback: boolean): boolean {\n if (val === undefined || val === null) return fallback;\n if (typeof val === \"boolean\") return val;\n if (typeof val === \"string\") return val === \"true\" || val === \"1\";\n if (typeof val === \"number\") return val !== 0;\n return fallback;\n}\n\nfunction envOrRawNum(\n envVal: string | undefined,\n raw: RawConfig,\n key: keyof RawConfig,\n fallback: number,\n): number {\n if (envVal !== undefined) return num(envVal, fallback);\n const rawVal = raw[key];\n return typeof rawVal === \"number\" && !Number.isNaN(rawVal) ? rawVal : fallback;\n}\n\nfunction envOrRawBool(\n envVal: string | undefined,\n raw: RawConfig,\n key: keyof RawConfig,\n fallback: boolean,\n): boolean {\n if (envVal !== undefined) return bool(envVal, fallback);\n const rawVal = raw[key];\n return typeof rawVal === \"boolean\" ? rawVal : fallback;\n}\n\n/**\n * Load configuration.\n * Precedence: env vars > JSON config file > defaults.\n * Writes the default config on first run if no file exists.\n * Removed-from-config fields (target, openai_path, warmer_path, vision_target,\n * rate_limit_window_seconds) are hardcoded — app is Umans-specific.\n * Misconfigured numeric fields fall back to their defaults.\n */\nexport function loadConfig(env: Record<string, string | undefined> = process.env): ProxyConfig {\n const configPath = ensureConfigFile();\n const raw = loadJsonConfig(configPath);\n\n const port = num(env.PORT ?? raw.port, 1945);\n const host = \"127.0.0.1\";\n const target = (env.TARGET ?? UPSTREAM_TARGET).replace(/\\/+$/, \"\");\n const maxCaptures = num(env.MAX_CAPTURES ?? raw.max_captures, 200);\n const dbPath = str(env.DB_PATH ?? raw.db_path, \"./umans-gate.db\");\n const idleTimeout = Math.min(num(env.IDLE_TIMEOUT ?? raw.idle_timeout, 255), 255);\n const upstreamProtocol = resolveUpstreamProtocol(env.UPSTREAM_PROTOCOL ?? raw.upstream_protocol);\n const stampClaudeCode = bool(\n env.STAMP_CLAUDE_CODE_ENABLED ?? raw.stamp_claude_code_enabled,\n false,\n );\n const stampReasoningEffortEnabled = bool(\n env.STAMP_REASONING_EFFORT_ENABLED ?? raw.stamp_reasoning_effort_enabled,\n false,\n );\n const stampReasoningEffort = stampReasoningEffortEnabled ? STAMP_REASONING_EFFORT_VALUE : null;\n const openaiPath = OPENAI_CHAT_PATH;\n const warmerEnabled =\n env.WARMER_ENABLED !== undefined\n ? env.WARMER_ENABLED !== \"false\" && env.WARMER_ENABLED !== \"0\"\n : raw.warmer_enabled !== false;\n const warmerIntervalMs = num(env.WARMER_INTERVAL_MS ?? raw.warmer_interval_ms, 20000);\n const warmerPath = WARMER_PATH;\n\n const umansApiKey = env.UMANS_API_KEY ?? raw.umans_api_key ?? null;\n const usageRefreshMs = num(env.USAGE_REFRESH_MS ?? raw.usage_refresh_ms, 60000);\n const modelsRefreshMs = num(env.MODELS_REFRESH_MS ?? raw.models_refresh_ms, 3600000);\n const concurrencyHardCap = num(env.CONCURRENCY_HARD_CAP ?? raw.concurrency_hard_cap, 1);\n const concurrencySoftLimit = num(env.CONCURRENCY_SOFT_LIMIT ?? raw.concurrency_soft_limit, 1);\n const rateLimitRequests = num(env.RATE_LIMIT_REQUESTS ?? raw.rate_limit_requests, 0);\n const queueTimeoutMs = num(env.QUEUE_TIMEOUT_MS ?? raw.queue_timeout_ms, 30000);\n const maxQueueDepth = num(env.MAX_QUEUE_DEPTH ?? raw.max_queue_depth, 256);\n const releaseCooldownMs = num(env.RELEASE_COOLDOWN_MS ?? raw.release_cooldown_ms, 1000);\n const breakerThreshold = num(env.BREAKER_THRESHOLD ?? raw.breaker_threshold, 5);\n const breakerWindowMs = num(env.BREAKER_WINDOW_MS ?? raw.breaker_window_ms, 300000);\n const breakerCooldownMs = num(env.BREAKER_COOLDOWN_MS ?? raw.breaker_cooldown_ms, 60000);\n\n const visionStrategy = str(env.VISION_STRATEGY ?? raw.vision_strategy, \"catalog\") as\n | \"never\"\n | \"catalog\"\n | \"always\";\n const visionTarget =\n env.VISION_TARGET ?? `${UPSTREAM_TARGET.replace(/\\/+$/, \"\")}${VISION_TARGET_PATH}`;\n const visionModel = env.VISION_MODEL ?? raw.vision_model ?? \"umans-flash\";\n const visionPrompt = str(\n env.VISION_PROMPT ?? raw.vision_prompt,\n DEFAULT_CONFIG.vision_prompt ?? \"Describe this image concisely.\",\n );\n const visionPromptVersion = num(env.VISION_PROMPT_VERSION ?? raw.vision_prompt_version, 2);\n const visionMaxImages = num(env.VISION_MAX_IMAGES ?? raw.vision_max_images, 5);\n const visionMaxDescriptionTokens = num(\n env.VISION_MAX_DESCRIPTION_TOKENS ?? raw.vision_max_description_tokens,\n 4096,\n );\n const visionReasoningEffortRaw = env.VISION_REASONING_EFFORT ?? raw.vision_reasoning_effort;\n const visionReasoningEffort =\n visionReasoningEffortRaw === undefined || visionReasoningEffortRaw === null\n ? null\n : (visionReasoningEffortRaw as \"none\" | \"low\" | \"medium\" | \"high\");\n const visionTimeoutMs = num(env.VISION_TIMEOUT_MS ?? raw.vision_timeout_ms, 0);\n const visionCacheSize = num(env.VISION_CACHE_SIZE ?? raw.vision_cache_size, 1000);\n const visionCacheTtlMs = num(env.VISION_CACHE_TTL_MS ?? raw.vision_cache_ttl_ms, 604800000);\n const visionCacheMaxRows = num(env.VISION_CACHE_MAX_ROWS ?? raw.vision_cache_max_rows, 10000);\n const visionPersistentCache =\n env.VISION_PERSISTENT_CACHE !== undefined\n ? env.VISION_PERSISTENT_CACHE !== \"false\" && env.VISION_PERSISTENT_CACHE !== \"0\"\n : raw.vision_persistent_cache !== false;\n const visionConcurrency = num(env.VISION_CONCURRENCY ?? raw.vision_concurrency, 1);\n const visionMaxDimension = num(env.VISION_MAX_DIMENSION ?? raw.vision_max_dimension, 2048);\n const visionJpegQuality = num(env.VISION_JPEG_QUALITY ?? raw.vision_jpeg_quality, 92);\n const visionImageFormat = (env.VISION_IMAGE_FORMAT ?? raw.vision_image_format ?? \"png\") as\n | \"jpeg\"\n | \"png\";\n const visionImageDetail = (env.VISION_IMAGE_DETAIL ?? raw.vision_image_detail ?? \"high\") as\n | \"auto\"\n | \"low\"\n | \"high\";\n // Derived from vision_strategy: \"catalog\" uses cache-first (background) mode,\n // \"always\" forces intercept even for vision-capable models.\n const backgroundVision = visionStrategy === \"catalog\";\n const concurrencyMainReservation = num(\n env.CONCURRENCY_MAIN_RESERVATION ?? raw.concurrency_main_reservation,\n 1,\n );\n const concurrencyVisionReservation = num(\n env.CONCURRENCY_VISION_RESERVATION ?? raw.concurrency_vision_reservation,\n 1,\n );\n\n const visionForceInterceptCapable = visionStrategy === \"always\";\n\n const captureBodyMaxBytes = envOrRawNum(\n env.CAPTURE_BODY_MAX_BYTES,\n raw,\n \"capture_body_max_bytes\",\n DEFAULT_CONFIG.capture_body_max_bytes ?? 1_000_000,\n );\n const queueMaxDepth = envOrRawNum(\n env.QUEUE_MAX_DEPTH,\n raw,\n \"queue_max_depth\",\n DEFAULT_CONFIG.queue_max_depth ?? 100,\n );\n const wsBackpressureLimit = envOrRawNum(\n env.WS_BACKPRESSURE_LIMIT,\n raw,\n \"ws_backpressure_limit\",\n DEFAULT_CONFIG.ws_backpressure_limit ?? 1_048_576,\n );\n const wsCloseOnBackpressureLimit = envOrRawBool(\n env.WS_CLOSE_ON_BACKPRESSURE_LIMIT,\n raw,\n \"ws_close_on_backpressure_limit\",\n DEFAULT_CONFIG.ws_close_on_backpressure_limit ?? true,\n );\n const visionPendingMaxBatch = envOrRawNum(\n env.VISION_PENDING_MAX_BATCH,\n raw,\n \"vision_pending_max_batch\",\n DEFAULT_CONFIG.vision_pending_max_batch ?? 50,\n );\n const compressionEnabled = envOrRawBool(\n env.COMPRESSION_ENABLED,\n raw,\n \"compression_enabled\",\n DEFAULT_CONFIG.compression_enabled ?? true,\n );\n const useWriteWorker = false;\n const upstreamTimeoutMs = envOrRawNum(\n env.UPSTREAM_TIMEOUT_MS,\n raw,\n \"upstream_timeout_ms\",\n DEFAULT_CONFIG.upstream_timeout_ms ?? 300000,\n );\n\n return {\n port,\n host,\n target,\n maxCaptures,\n dbPath,\n viewerPrefix: \"/dashboard\",\n flushIntervalMs: 50,\n flushBatch: 25,\n idleTimeout,\n upstreamProtocol,\n incomingProtocol: \"http1.1\" as IncomingProtocol,\n stampClaudeCode,\n stampReasoningEffort,\n openaiPath,\n warmerEnabled,\n warmerIntervalMs,\n warmerPath,\n umansApiKey,\n usageRefreshMs,\n modelsRefreshMs,\n concurrencyHardCap,\n concurrencySoftLimit,\n rateLimitRequests,\n queueTimeoutMs,\n maxQueueDepth,\n releaseCooldownMs,\n breakerThreshold,\n breakerWindowMs,\n breakerCooldownMs,\n visionStrategy,\n visionTarget,\n visionModel,\n visionPrompt,\n visionPromptVersion,\n visionMaxImages,\n visionMaxDescriptionTokens,\n visionReasoningEffort,\n visionTimeoutMs,\n visionCacheSize,\n visionCacheTtlMs,\n visionCacheMaxRows,\n visionPersistentCache,\n visionConcurrency,\n visionMaxDimension,\n visionJpegQuality,\n visionImageFormat,\n visionImageDetail,\n backgroundVision,\n concurrencyMainReservation,\n concurrencyVisionReservation,\n visionForceInterceptCapable,\n captureBodyMaxBytes,\n queueMaxDepth,\n wsBackpressureLimit,\n wsCloseOnBackpressureLimit,\n visionPendingMaxBatch,\n compressionEnabled,\n useWriteWorker,\n upstreamTimeoutMs,\n };\n}\n\n/**\n * Read the raw config.json from disk (for the config UI).\n * Returns defaults merged with the file contents (no env override).\n */\nexport function readConfigFile(): RawConfig {\n const path = ensureConfigFile();\n const raw = loadJsonConfig(path);\n return { ...DEFAULT_CONFIG, ...raw };\n}\n\n/**\n * Save a partial config to disk (validate first, merge with existing).\n * Returns validation result + the merged config that was written.\n */\nexport function saveConfig(patch: RawConfigInput): {\n ok: boolean;\n errors: string[];\n warnings: string[];\n written: RawConfig | null;\n} {\n const existing = readConfigFile();\n const merged: RawConfig = { ...existing, ...coerceRawForValidation(patch) };\n const result = validateConfig(merged);\n if (!result.ok) {\n return { ok: false, errors: result.errors, warnings: result.warnings, written: null };\n }\n const path = resolveConfigPath();\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(result.normalized, null, 2), \"utf-8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n // Best-effort: not all platforms support chmod.\n }\n return { ok: true, errors: [], warnings: result.warnings, written: result.normalized };\n}\n\n/**\n * Reset config to defaults on disk, preserving `umans_api_key` so the user is\n * not locked out of the upstream. Returns the written config.\n */\nexport function resetConfig(): { ok: boolean; written: RawConfig | null } {\n const existing = readConfigFile();\n const reset: RawConfig = {\n ...DEFAULT_CONFIG,\n umans_api_key: existing.umans_api_key ?? DEFAULT_CONFIG.umans_api_key,\n };\n const path = resolveConfigPath();\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(reset, null, 2), \"utf-8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n // Best-effort: not all platforms support chmod.\n }\n return { ok: true, written: reset };\n}\n\n/**\n * Fields that require a server restart to take effect (cannot be hot-reloaded).\n * Everything else can be applied to the live ProxyConfig in-place via reloadConfig().\n */\nconst RESTART_REQUIRED_FIELDS = new Set<keyof RawConfig>([\n \"port\",\n \"max_captures\",\n \"db_path\",\n \"idle_timeout\",\n \"upstream_protocol\",\n \"warmer_enabled\",\n \"warmer_interval_ms\",\n \"usage_refresh_ms\",\n \"umans_api_key\",\n \"models_refresh_ms\",\n \"vision_strategy\",\n \"vision_model\",\n \"vision_prompt\",\n \"vision_prompt_version\",\n \"vision_max_images\",\n \"vision_max_description_tokens\",\n \"vision_reasoning_effort\",\n \"vision_timeout_ms\",\n \"vision_cache_size\",\n \"vision_cache_ttl_ms\",\n \"vision_cache_max_rows\",\n \"vision_persistent_cache\",\n \"vision_max_dimension\",\n \"vision_jpeg_quality\",\n \"vision_image_format\",\n \"vision_image_detail\",\n \"vision_concurrency\",\n]);\n\n/**\n * Table of hot-reloadable raw keys and the in-place assignment each performs\n * on the live ProxyConfig. Drives the data-driven apply loop below.\n */\nconst RELOAD_FIELDS: Array<{\n rawKey: keyof RawConfig;\n apply: (live: ProxyConfig, fresh: ProxyConfig) => void;\n}> = [\n {\n rawKey: \"stamp_claude_code_enabled\",\n apply: (live, fresh) => {\n live.stampClaudeCode = fresh.stampClaudeCode;\n },\n },\n {\n rawKey: \"stamp_reasoning_effort_enabled\",\n apply: (live, fresh) => {\n live.stampReasoningEffort = fresh.stampReasoningEffort;\n },\n },\n {\n rawKey: \"rate_limit_requests\",\n apply: (live, fresh) => {\n live.rateLimitRequests = fresh.rateLimitRequests;\n },\n },\n {\n rawKey: \"queue_timeout_ms\",\n apply: (live, fresh) => {\n live.queueTimeoutMs = fresh.queueTimeoutMs;\n },\n },\n {\n rawKey: \"max_queue_depth\",\n apply: (live, fresh) => {\n live.maxQueueDepth = fresh.maxQueueDepth;\n },\n },\n {\n rawKey: \"release_cooldown_ms\",\n apply: (live, fresh) => {\n live.releaseCooldownMs = fresh.releaseCooldownMs;\n },\n },\n {\n rawKey: \"breaker_threshold\",\n apply: (live, fresh) => {\n live.breakerThreshold = fresh.breakerThreshold;\n },\n },\n {\n rawKey: \"breaker_window_ms\",\n apply: (live, fresh) => {\n live.breakerWindowMs = fresh.breakerWindowMs;\n },\n },\n {\n rawKey: \"breaker_cooldown_ms\",\n apply: (live, fresh) => {\n live.breakerCooldownMs = fresh.breakerCooldownMs;\n },\n },\n {\n rawKey: \"concurrency_main_reservation\",\n apply: (live, fresh) => {\n live.concurrencyMainReservation = fresh.concurrencyMainReservation;\n },\n },\n {\n rawKey: \"concurrency_vision_reservation\",\n apply: (live, fresh) => {\n live.concurrencyVisionReservation = fresh.concurrencyVisionReservation;\n },\n },\n {\n rawKey: \"concurrency_hard_cap\",\n apply: (live, fresh) => {\n live.concurrencyHardCap = fresh.concurrencyHardCap;\n },\n },\n {\n rawKey: \"concurrency_soft_limit\",\n apply: (live, fresh) => {\n live.concurrencySoftLimit = fresh.concurrencySoftLimit;\n },\n },\n {\n rawKey: \"compression_enabled\",\n apply: (live, fresh) => {\n live.compressionEnabled = fresh.compressionEnabled;\n },\n },\n {\n rawKey: \"capture_body_max_bytes\",\n apply: (live, fresh) => {\n live.captureBodyMaxBytes = fresh.captureBodyMaxBytes;\n },\n },\n];\n\n/**\n * Apply reloaded config to a live ProxyConfig in-place.\n * Only applies fields that can be hot-reloaded; flags restart-required changes.\n * Returns lists of applied fields and restart-required fields.\n */\nexport function applyReloadToConfig(\n live: ProxyConfig,\n fresh: ProxyConfig,\n oldRaw: RawConfig,\n newRaw: RawConfig,\n): { applied: string[]; restartRequired: string[] } {\n const applied: string[] = [];\n const restartRequired: string[] = [];\n\n // Compare raw keys to detect changes.\n for (const key of Object.keys(newRaw) as (keyof RawConfig)[]) {\n const oldVal = oldRaw[key];\n const newVal = newRaw[key];\n const changed = JSON.stringify(oldVal) !== JSON.stringify(newVal);\n if (!changed) continue;\n\n if (RESTART_REQUIRED_FIELDS.has(key)) {\n restartRequired.push(key);\n } else {\n applied.push(key);\n }\n }\n\n // Apply hot-reloadable fields to the live ProxyConfig.\n // These are the fields that proxy.ts and stamp.ts read per-request.\n for (const { rawKey, apply } of RELOAD_FIELDS) {\n if (applied.includes(rawKey)) apply(live, fresh);\n }\n\n return { applied, restartRequired };\n}\n","// Self-update logic for umans-gate.\n// Detects install method (npm global, standalone executable, or bun dev)\n// and performs the appropriate update action.\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\n\nconst GITHUB_API = \"https://api.github.com/repos/codegiveness/umans-gate/releases/latest\";\n\ninterface GithubRelease {\n tag_name: string;\n assets: Array<{ name: string; browser_download_url: string }>;\n}\n\n/** Fetch latest version from GitHub Releases. */\nasync function fetchLatestVersion(): Promise<string | null> {\n try {\n const resp = await fetch(GITHUB_API, {\n headers: { \"User-Agent\": \"umans-gate-updater\" },\n });\n if (!resp.ok) return null;\n const data = (await resp.json()) as GithubRelease;\n return data.tag_name.replace(/^v/, \"\");\n } catch {\n return null;\n }\n}\n\n/** Check if running as a compiled standalone executable. */\nfunction isCompiledExecutable(): boolean {\n // In compiled mode, process.execPath points to the binary itself,\n // not to a bun/node executable.\n const execPath = process.execPath;\n return existsSync(execPath) && execPath.includes(\"umans-gate\");\n}\n\n/** Check if running as npm global install. */\nfunction isNpmGlobal(): boolean {\n try {\n const npmRoot = execSync(\"npm root -g\", { encoding: \"utf-8\" }).trim();\n return existsSync(`${npmRoot}/umans-gate`);\n } catch {\n return false;\n }\n}\n\n/** Compare two semver strings (returns -1, 0, 1). */\nfunction compareVersions(a: string, b: string): number {\n const pa = a.split(\".\").map(Number);\n const pb = b.split(\".\").map(Number);\n for (let i = 0; i < Math.max(pa.length, pb.length); i++) {\n const va = pa[i] ?? 0;\n const vb = pb[i] ?? 0;\n if (va < vb) return -1;\n if (va > vb) return 1;\n }\n return 0;\n}\n\n/** Check for available update without installing. Prints result and exits. */\nexport async function checkForUpdate(currentVersion: string): Promise<void> {\n console.log(\"Checking for updates...\");\n const latest = await fetchLatestVersion();\n if (!latest) {\n console.error(\"Could not fetch latest version from GitHub.\");\n process.exit(1);\n }\n\n const cmp = compareVersions(currentVersion, latest);\n if (cmp < 0) {\n console.log(`Update available: ${currentVersion} → ${latest}`);\n console.log(\"Run `umans-gate update` to install.\");\n } else if (cmp === 0) {\n console.log(`Already up to date (v${currentVersion}).`);\n } else {\n console.log(`Running ahead of latest release (${currentVersion} > ${latest}).`);\n }\n}\n\n/** Perform the update based on install method. */\nexport async function performUpdate(currentVersion: string): Promise<void> {\n console.log(\"Checking for updates...\");\n const latest = await fetchLatestVersion();\n if (!latest) {\n console.error(\"Could not fetch latest version from GitHub.\");\n process.exit(1);\n }\n\n const cmp = compareVersions(currentVersion, latest);\n if (cmp >= 0) {\n console.log(`Already up to date (v${currentVersion}).`);\n return;\n }\n\n console.log(`Updating: ${currentVersion} → ${latest}`);\n\n if (isNpmGlobal()) {\n console.log(\"Install method: npm global\");\n try {\n execSync(\"npm update -g umans-gate\", { stdio: \"inherit\" });\n console.log(\"Update complete.\");\n } catch {\n console.error(\"npm update failed. Try manually: npm install -g umans-gate@latest\");\n process.exit(1);\n }\n } else if (isCompiledExecutable()) {\n console.log(\"Install method: standalone executable\");\n console.log(\"Please download the latest binary from:\");\n console.log(\" https://github.com/codegiveness/umans-gate/releases/latest\");\n console.log(\"Or reinstall via the install script:\");\n console.log(\n \" curl -fsSL https://raw.githubusercontent.com/codegiveness/umans-gate/main/install.sh | sh\",\n );\n } else {\n console.log(\"Install method: development\");\n console.log(\"Pull the latest changes and reinstall:\");\n console.log(\" git pull && bun install && bun run build\");\n }\n}\n","// Uninstall logic for umans-gate.\n// Removes the standalone binary, npm global package, and optionally config files.\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, rmSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { resolveConfigPath } from \"./config.js\";\n\ninterface UninstallOptions {\n keepConfig: boolean;\n}\n\n/** Check if running as a compiled standalone executable. */\nfunction isCompiledExecutable(): boolean {\n const execPath = process.execPath;\n return existsSync(execPath) && execPath.includes(\"umans-gate\");\n}\n\n/** Check if running as npm global install. */\nfunction isNpmGlobal(): boolean {\n try {\n const npmRoot = execSync(\"npm root -g\", { encoding: \"utf-8\" }).trim();\n return existsSync(`${npmRoot}/umans-gate`);\n } catch {\n return false;\n }\n}\n\n/** Remove the standalone executable binary. */\nfunction removeStandaloneBinary(): void {\n const execPath = process.execPath;\n if (existsSync(execPath) && execPath.includes(\"umans-gate\")) {\n const dir = dirname(execPath);\n try {\n rmSync(execPath);\n console.log(`Removed binary: ${execPath}`);\n // Remove symlink if exists (e.g. in /usr/local/bin)\n const symlinkPath = join(\"/usr/local/bin\", \"umans-gate\");\n if (existsSync(symlinkPath)) {\n rmSync(symlinkPath);\n console.log(`Removed symlink: ${symlinkPath}`);\n }\n // If the binary directory is now empty, remove it\n const { readdirSync, rmdirSync } = require(\"node:fs\");\n if (readdirSync(dir).length === 0) {\n rmdirSync(dir);\n console.log(`Removed empty directory: ${dir}`);\n }\n } catch (err) {\n console.error(`Failed to remove binary: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n}\n\n/** Remove the npm global package. */\nfunction removeNpmGlobal(): void {\n try {\n execSync(\"npm uninstall -g umans-gate\", { stdio: \"inherit\" });\n console.log(\"Removed npm global package: umans-gate\");\n } catch {\n console.error(\"Failed to uninstall npm package. Try: npm uninstall -g umans-gate\");\n }\n}\n\n/** Remove configuration files. */\nfunction removeConfig(): void {\n const configPath = resolveConfigPath();\n if (existsSync(configPath)) {\n try {\n rmSync(configPath);\n console.log(`Removed config: ${configPath}`);\n } catch (err) {\n console.error(`Failed to remove config: ${err instanceof Error ? err.message : String(err)}`);\n }\n } else {\n console.log(\"No config file found.\");\n }\n}\n\n/** Perform the uninstall based on install method and options. */\nexport async function uninstall(options: UninstallOptions): Promise<void> {\n console.log(\"Uninstalling umans-gate...\\n\");\n\n let removed = false;\n\n if (isNpmGlobal()) {\n console.log(\"Install method: npm global\");\n removeNpmGlobal();\n removed = true;\n }\n\n if (isCompiledExecutable()) {\n console.log(\"Install method: standalone executable\");\n removeStandaloneBinary();\n removed = true;\n }\n\n if (!removed) {\n console.log(\"No global installation found.\");\n console.log(\"If running from source, simply delete the project directory.\");\n }\n\n if (!options.keepConfig) {\n console.log(\"\\nRemoving configuration...\");\n removeConfig();\n } else {\n console.log(\"\\nKeeping configuration files (--keep-config).\");\n }\n\n console.log(\"\\nUninstall complete.\");\n}\n","#!/usr/bin/env bun\n// CLI entry point for umans-gate.\n// Usage: bun src/cli.ts (or after build: umans-gate)\n// Point your harness base URL → http://localhost:1945\n// Open the inspector → http://localhost:1945/dashboard/\n\nimport { Command } from \"commander\";\nimport pkg from \"../package.json\" with { type: \"json\" };\nimport { readConfigFile, resolveConfigPath } from \"./config.js\";\nimport { createProxyServer } from \"./index.js\";\n\nconst VERSION: string = pkg.version;\n\nconst program = new Command();\n\nprogram\n .name(\"umans-gate\")\n .description(\"LLM capture proxy with Anthropic cache_control TTL stamping\")\n .version(VERSION)\n .option(\"--port <number>\", \"listen port\")\n .option(\"--target <url>\", \"upstream target URL\")\n .action((options) => {\n const config: Record<string, unknown> = {};\n if (options.port) config.port = Number(options.port);\n if (options.target) config.target = options.target;\n createProxyServer(Object.keys(config).length > 0 ? { config } : undefined);\n });\n\nprogram\n .command(\"update\")\n .description(\"Update umans-gate to the latest version\")\n .option(\"--check\", \"check if update is available without installing\")\n .action(async (options) => {\n const { checkForUpdate, performUpdate } = await import(\"./updater.js\");\n if (options.check) {\n await checkForUpdate(VERSION);\n } else {\n await performUpdate(VERSION);\n }\n });\n\nprogram\n .command(\"uninstall\")\n .description(\"Remove umans-gate\")\n .option(\"--keep-config\", \"keep configuration files\")\n .action(async (options) => {\n const { uninstall } = await import(\"./uninstaller.js\");\n await uninstall({ keepConfig: options.keepConfig ?? false });\n });\n\nprogram\n .command(\"config\")\n .description(\"Show or edit configuration\")\n .argument(\"[action]\", \"show | path\")\n .action((action: string | undefined) => {\n const configPath = resolveConfigPath();\n if (action === \"path\") {\n console.log(configPath);\n return;\n }\n // Default: show\n const cfg = readConfigFile();\n console.log(JSON.stringify(cfg, null, 2));\n console.log(`\\nConfig file: ${configPath}`);\n });\n\nprogram.parse(process.argv);\n","{\n \"name\": \"umans-gate\",\n \"version\": \"0.1.0\",\n \"description\": \"LLM capture proxy with Anthropic cache_control TTL stamping and live inspection dashboard\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"author\": \"umans.ai\",\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"homepage\": \"https://github.com/umans-ai/umans-gate\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/umans-ai/umans-gate.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/umans-ai/umans-gate/issues\"\n },\n \"keywords\": [\n \"llm\",\n \"proxy\",\n \"capture\",\n \"anthropic\",\n \"openai\",\n \"cache-control\",\n \"ttl\",\n \"inspector\",\n \"debugger\",\n \"websocket\"\n ],\n \"bin\": {\n \"umans-gate\": \"dist/cli.js\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"dashboard/dist\",\n \"README.md\",\n \"LICENSE\",\n \"CHANGELOG.md\"\n ],\n \"engines\": {\n \"bun\": \">=1.1.0\"\n },\n \"scripts\": {\n \"dev\": \"bun src/cli.ts\",\n \"build\": \"bun run build:dashboard && bun run build:embed-assets && bun run build:server\",\n \"build:server\": \"bash scripts/build-server.sh\",\n \"build:dashboard\": \"cd dashboard && bun run build\",\n \"build:embed-assets\": \"bun scripts/embed-assets.ts\",\n \"test\": \"bun test\",\n \"test:dashboard\": \"cd dashboard && bun run test\",\n \"test:dashboard:watch\": \"cd dashboard && bun run test:watch\",\n \"test:all\": \"bun run test && bun run test:dashboard\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"format\": \"biome format --write .\",\n \"typecheck\": \"tsc --noEmit\",\n \"clean\": \"rm -rf dist dashboard/dist dashboard/node_modules capture.db capture.db-shm capture.db-wal\",\n \"prepublishOnly\": \"bun run build\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^1.9.4\",\n \"@playwright/test\": \"^1.61.1\",\n \"playwright\": \"^1.61.1\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.7.2\"\n },\n \"dependencies\": {\n \"@types/bun\": \"^1.3.14\",\n \"commander\": \"^12\",\n \"lru-cache\": \"^11.5.1\"\n }\n}\n","// Startup banner — prints the proxy configuration to stderr.\n\nimport type { ProxyConfig } from \"./types.js\";\n\n/** Print the startup banner showing all resolved config values. */\nexport function printBanner(config: ProxyConfig): void {\n const displayHost = \"localhost\";\n const cacheDesc = config.stampClaudeCode\n ? \"on (claude code: ttl, top_k, max_tokens, thinking, output_config, context_management)\"\n : \"off (transparent passthrough)\";\n\n console.log(\"umans-gate v0.1.0 — LLM capture proxy\");\n console.log();\n console.log(` target ${config.target}`);\n console.log(` listen ${config.host}:${config.port}`);\n console.log(` proto in ${config.incomingProtocol} → out ${config.upstreamProtocol}`);\n console.log(` cache ${cacheDesc}`);\n if (config.visionStrategy !== \"never\") {\n console.log(\n ` vision ${config.visionStrategy} (model=${config.visionModel}, concurrency=${config.visionConcurrency})`,\n );\n }\n if (config.warmerEnabled) {\n console.log(` warm every ${config.warmerIntervalMs}ms → ${config.warmerPath}`);\n }\n console.log(` proxy http://${displayHost}:${config.port}/`);\n console.log(` dashboard http://${displayHost}:${config.port}${config.viewerPrefix}/`);\n console.log(` store ${config.dbPath} (keeps last ${config.maxCaptures})`);\n console.log();\n}\n","// Public API — createProxyServer() factory.\n// Exports the main server creation function and key types for programmatic use.\n\nimport { printBanner } from \"./banner.js\";\nimport {\n type RawConfig,\n type ReloadResult,\n applyReloadToConfig,\n loadConfig,\n readConfigFile,\n resolveConfigPath,\n saveConfig,\n} from \"./config.js\";\nimport { CaptureDB } from \"./db.js\";\nimport { syncPricing } from \"./economics.js\";\nimport { computeRequestWeight } from \"./helpers.js\";\nimport { ConcurrencyGate, GATE_RECONFIG_FIELDS, gateOptionsFromConfig } from \"./limiter/index.js\";\nimport { createLogger } from \"./logger.js\";\nimport { metrics } from \"./metrics.js\";\nimport { ModelsClient } from \"./models.js\";\nimport { type RateLimiterRef, createProxyHandler } from \"./proxy.js\";\nimport { WriteQueue } from \"./queue.js\";\nimport type { CaptureStore } from \"./queue.js\";\nimport { SlidingWindowRateLimiter } from \"./rate.js\";\nimport type { ProxyConfig } from \"./types.js\";\nimport { UmansUsageClient } from \"./usage.js\";\nimport { createViewerRouter } from \"./viewer.js\";\nimport { DescriptionCache } from \"./vision/cache.js\";\nimport type { VisionLookup } from \"./vision/detect.js\";\nimport { VisionHandoff } from \"./vision/handoff.js\";\nimport { PersistentDescriptionStore } from \"./vision/persistent-cache.js\";\nimport { CompositeVisionSink, DbVisionSink, WsBroadcastVisionSink } from \"./vision/sink.js\";\nimport { ConnectionWarmer } from \"./warmer.js\";\nimport { WorkerCaptureStore } from \"./workers/worker-store.js\";\nimport { type BunServerWebSocket, WsBroadcaster } from \"./ws.js\";\n\nconst log = createLogger(\"server\");\n\nexport { loadConfig, readConfigFile, saveConfig, validateConfig } from \"./config.js\";\nexport { resolveConfigDir, resolveConfigPath, ensureConfigFile } from \"./config.js\";\nexport type { RawConfig, RawConfigInput, ValidationResult, ReloadResult } from \"./config.js\";\nexport { CaptureDB } from \"./db.js\";\nexport { WsBroadcaster } from \"./ws.js\";\nexport type { BunServerWebSocket } from \"./ws.js\";\nexport { WriteQueue } from \"./queue.js\";\nexport { stampCacheTtl } from \"./stamp.js\";\nexport { ConnectionWarmer } from \"./warmer.js\";\nexport { ConcurrencyGate } from \"./limiter/index.js\";\nexport { SlidingWindowRateLimiter } from \"./rate.js\";\nexport { UmansUsageClient } from \"./usage.js\";\nexport type {\n ProxyConfig,\n CaptureConfig,\n CaptureRow,\n CaptureSummary,\n GateConfig,\n ProtocolConfig,\n QueueConfig,\n StampConfig,\n WsMessage,\n GateStats,\n UsageSnapshot,\n BreakerState,\n} from \"./types.js\";\nexport type { TimedChunk } from \"./usage-extract.js\";\n\n/** Whitelist of LLM API routes that the proxy will capture + forward.\n * Matches umans API surface (verified from app.umans.ai/offers/code/docs). */\nconst LLM_ROUTES = new Set([\n \"POST /v1/messages\",\n \"GET /v1/models\",\n \"GET /v1/models/info\",\n \"POST /v1/chat/completions\",\n]);\n\n/** Options for {@link createRequestDispatcher}. */\ninterface RequestDispatcherOptions {\n handleViewer: (url: URL, req: Request) => Promise<Response | null>;\n handleProxy: (req: Request, url: URL) => Promise<Response>;\n handleHealth: () => Response;\n handleMetrics: () => Response;\n viewerPrefix: string;\n}\n\n/**\n * Create the request dispatcher that routes incoming requests to:\n * 1. WebSocket upgrade (returns 101 on success, 400 on failure)\n * 2. Viewer routes (dashboard + REST API under the viewer prefix)\n * 3. Health check endpoint (`GET /health`)\n * 4. Metrics endpoint (`GET /metrics`)\n * 5. LLM proxy routes (whitelisted method+path combinations)\n * 6. 404 fallback for non-LLM paths\n */\nfunction createRequestDispatcher(options: RequestDispatcherOptions) {\n const { handleViewer, handleProxy, handleHealth, handleMetrics, viewerPrefix: VIEWER } = options;\n\n return async (req: Request, server: Bun.Server<undefined>): Promise<Response> => {\n const url = new URL(req.url);\n\n // WebSocket upgrade\n if (url.pathname === `${VIEWER}/ws`) {\n if (server.upgrade(req)) return new Response(null, { status: 101 });\n return new Response(\"upgrade failed\", { status: 400 });\n }\n\n // Viewer routes (dashboard + REST API)\n if (url.pathname === VIEWER || url.pathname.startsWith(`${VIEWER}/`)) {\n const resp = await handleViewer(url, req);\n return resp ?? new Response(\"not found\", { status: 404 });\n }\n\n // Health check endpoint\n if (url.pathname === \"/health\" && req.method === \"GET\") {\n return handleHealth();\n }\n\n // Metrics endpoint (Prometheus text format)\n if (url.pathname === \"/metrics\" && req.method === \"GET\") {\n return handleMetrics();\n }\n\n // Reject non-LLM paths (favicon, preflight, health checks, etc.)\n const routeKey = `${req.method} ${url.pathname}`;\n if (!LLM_ROUTES.has(routeKey)) {\n return new Response(JSON.stringify({ error: \"not_an_llm_endpoint\", path: url.pathname }), {\n status: 404,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n\n // Proxy route — disable idle timeout for long streaming responses\n server.timeout(req, 0);\n return handleProxy(req, url);\n };\n}\n\nconst DEFAULT_RATE_WINDOW_SECONDS = 18000;\n\nfunction createRateLimiter(\n rateLimitRequests: number,\n snap: { requestsHardCap: number | null; requestsWindowSeconds: number | null } | null,\n): SlidingWindowRateLimiter | null {\n if (rateLimitRequests === -1) return null;\n if (rateLimitRequests > 0) {\n const windowSeconds = snap?.requestsWindowSeconds ?? DEFAULT_RATE_WINDOW_SECONDS;\n return new SlidingWindowRateLimiter({ limit: rateLimitRequests, windowSeconds });\n }\n // rateLimitRequests === 0: auto-derive from usage snapshot\n if (snap && snap.requestsHardCap !== null && snap.requestsHardCap > 0) {\n const windowSeconds = snap.requestsWindowSeconds ?? DEFAULT_RATE_WINDOW_SECONDS;\n return new SlidingWindowRateLimiter({ limit: snap.requestsHardCap, windowSeconds });\n }\n return null;\n}\n\n/** Options for creating a proxy server. */\nexport interface CreateProxyServerOptions {\n /** Override env config. Pass a partial config to merge with defaults.\n * Note: `host` is hardcoded to `127.0.0.1` and cannot be overridden. */\n config?: Omit<Partial<ProxyConfig>, \"host\">;\n /** Use an existing CaptureDB instance instead of creating one. */\n db?: CaptureDB;\n /** Use an existing WsBroadcaster instead of creating one. */\n ws?: WsBroadcaster;\n /** Print the startup banner (default: true). */\n banner?: boolean;\n}\n\n/** The running proxy server handle. */\nexport interface ProxyServer {\n server: ReturnType<typeof Bun.serve>;\n db: CaptureDB;\n ws: WsBroadcaster;\n queue: WriteQueue;\n warmer: ConnectionWarmer | null;\n gate: ConcurrencyGate;\n usage: UmansUsageClient;\n models: ModelsClient;\n rate: SlidingWindowRateLimiter | null;\n config: ProxyConfig;\n /** Reload config from disk and apply hot-reloadable fields. */\n reloadConfig(): ReloadResult;\n shutdown(): Promise<void>;\n}\n\n/**\n * Create and start the LLM capture proxy server.\n *\n * This is the main programmatic entry point. It sets up the database,\n * WebSocket broadcaster, write-behind queue, viewer router, and proxy handler,\n * then starts listening on the configured port.\n */\nexport function createProxyServer(options: CreateProxyServerOptions = {}): ProxyServer {\n const envConfig = loadConfig();\n const config: ProxyConfig = { ...envConfig, ...options.config, host: \"127.0.0.1\" };\n\n const db = options.db ?? new CaptureDB(config);\n const ws = options.ws ?? new WsBroadcaster();\n const writeStore: CaptureStore = config.useWriteWorker\n ? new WorkerCaptureStore(config.dbPath, config.compressionEnabled)\n : db;\n const queue = new WriteQueue(writeStore, config, (messages) => {\n for (const msg of messages) {\n ws.broadcast(msg);\n }\n });\n const warmer = config.warmerEnabled ? new ConnectionWarmer(config) : null;\n\n const usage = new UmansUsageClient(config);\n const models = new ModelsClient({\n target: config.target,\n apiKey: config.umansApiKey,\n refreshMs: config.modelsRefreshMs,\n });\n models.start();\n models.onChange(() => {\n try {\n syncPricing(db.rawDb, models.list());\n } catch (err) {\n log.error(\"pricing sync failed\", { error: err instanceof Error ? err.message : String(err) });\n }\n });\n try {\n syncPricing(db.rawDb, models.list());\n } catch {\n // Models not fetched yet — onChange will sync after first poll.\n }\n const gate = new ConcurrencyGate(gateOptionsFromConfig(config));\n const rateRef: RateLimiterRef = { current: createRateLimiter(config.rateLimitRequests, null) };\n\n usage.onChange((snap) => {\n gate.setSoftLimit(snap.concurrencySoftLimit);\n let effective = snap.concurrencySoftLimit;\n if (snap.priorityLow) effective = Math.max(1, effective - 1);\n const boxed = snap.boxedUntil !== null && snap.boxedUntil > Date.now();\n if (boxed && snap.boxedReason !== \"rate_limited\") {\n gate.resize(1);\n } else {\n gate.resize(effective);\n }\n // Auto-derive rate limiter from usage snapshot when rate_limit_requests=0\n if (config.rateLimitRequests === 0 && snap.requestsHardCap !== null) {\n if (rateRef.current === null) {\n rateRef.current = createRateLimiter(0, snap);\n }\n } else if (config.rateLimitRequests > 0 && snap.requestsWindowSeconds !== null) {\n if (rateRef.current === null) {\n rateRef.current = createRateLimiter(config.rateLimitRequests, snap);\n }\n }\n ws.broadcast({ type: \"gate\", stats: gate.getStats(snap) });\n });\n gate.onStatsChange(() => {\n ws.broadcast({ type: \"gate\", stats: gate.getStats(usage.getSnapshot()) });\n });\n usage.start();\n\n function applyLimitsFromSource(\n source: { hardCap: number; softLimit: number },\n persist = false,\n ): void {\n if (persist) {\n saveConfig({\n concurrency_hard_cap: source.hardCap,\n concurrency_soft_limit: source.softLimit,\n });\n }\n config.concurrencyHardCap = source.hardCap;\n config.concurrencySoftLimit = source.softLimit;\n gate.setHardCap(source.hardCap);\n gate.setSoftLimit(source.softLimit);\n ws.broadcast({ type: \"gate\", stats: gate.getStats(usage.getSnapshot()) });\n }\n\n const persistentStore = config.visionPersistentCache\n ? new PersistentDescriptionStore(\n db,\n config.visionCacheTtlMs,\n config.visionCacheMaxRows,\n config.visionPendingMaxBatch,\n )\n : null;\n const visionCache = new DescriptionCache(\n config.visionCacheSize,\n config.visionCacheTtlMs,\n persistentStore,\n );\n\n const catalog: VisionLookup | null = config.visionStrategy !== \"never\" ? models : null;\n\n const visionModelName = config.visionModel ?? \"\";\n const visionWeight = computeRequestWeight(visionModelName, models);\n\n const visionSink = new CompositeVisionSink([\n new DbVisionSink(db, config),\n new WsBroadcastVisionSink(ws, config),\n ]);\n const vision =\n config.visionStrategy !== \"never\"\n ? new VisionHandoff(\n {\n strategy: config.visionStrategy,\n target: config.visionTarget,\n model: config.visionModel,\n prompt: config.visionPrompt,\n promptVersion: config.visionPromptVersion,\n maxImages: config.visionMaxImages,\n maxDescriptionTokens: config.visionMaxDescriptionTokens,\n reasoningEffort: config.visionReasoningEffort,\n timeoutMs: config.visionTimeoutMs,\n cacheSize: config.visionCacheSize,\n cacheTtlMs: config.visionCacheTtlMs,\n cacheMaxRows: config.visionCacheMaxRows,\n persistentCache: config.visionPersistentCache,\n concurrency: config.visionConcurrency,\n apiKey: config.umansApiKey || null,\n forceInterceptCapable: config.visionForceInterceptCapable,\n maxDimension: config.visionMaxDimension,\n jpegQuality: config.visionJpegQuality,\n imageFormat: config.visionImageFormat,\n imageDetail: config.visionImageDetail,\n visionWeight,\n backgroundVision: config.backgroundVision,\n },\n visionCache,\n catalog,\n gate,\n db,\n visionSink,\n config,\n )\n : null;\n\n if (persistentStore && config.visionPersistentCache) {\n const warmed = persistentStore.warmIntoCache(\n (key, description) => visionCache.warm(key, description),\n config.visionCacheSize,\n );\n if (warmed > 0) {\n console.log(`[vision] Warmed ${warmed} descriptions from persistent store`);\n }\n }\n\n const { handleProxy } = createProxyHandler(\n db,\n ws,\n queue,\n config,\n gate,\n rateRef,\n vision,\n models,\n () => warmer?.notifyTraffic(),\n );\n let lastRawConfig: RawConfig = readConfigFile();\n\n const reloadConfig = (): ReloadResult => {\n const oldRaw = lastRawConfig;\n const newRaw = readConfigFile();\n const fresh = loadConfig();\n const { applied, restartRequired } = applyReloadToConfig(config, fresh, oldRaw, newRaw);\n lastRawConfig = newRaw;\n\n if (applied.some((k) => GATE_RECONFIG_FIELDS.has(k as keyof ProxyConfig))) {\n gate.reconfigure(gateOptionsFromConfig(config));\n }\n\n if (applied.includes(\"concurrency_hard_cap\")) {\n gate.setHardCap(config.concurrencyHardCap);\n }\n if (applied.includes(\"concurrency_soft_limit\")) {\n gate.setSoftLimit(config.concurrencySoftLimit);\n }\n\n if (applied.includes(\"compression_enabled\")) {\n db.compressionEnabled = config.compressionEnabled;\n }\n\n if (applied.includes(\"rate_limit_requests\")) {\n rateRef.current = createRateLimiter(config.rateLimitRequests, usage.getSnapshot());\n }\n\n ws.broadcast({ type: \"gate\", stats: gate.getStats(usage.getSnapshot()) });\n\n return {\n ok: true,\n errors: [],\n warnings: [],\n applied,\n restartRequired,\n configPath: resolveConfigPath(),\n };\n };\n\n const refreshLimits = async (): Promise<\n | {\n ok: true;\n hardCap: number;\n softLimit: number;\n requestsLimit: number | null;\n requestsHardCap: number | null;\n requestsWindowSeconds: number | null;\n }\n | { ok: false; error: string }\n > => {\n const r = await usage.fetchLimitsFromSource();\n if (r.ok) {\n applyLimitsFromSource({ hardCap: r.hardCap, softLimit: r.softLimit }, true);\n const rl = await usage.fetchRequestsLimit();\n if (rl.ok) {\n const snap = usage.getSnapshot();\n if (config.rateLimitRequests === 0 && rl.hardCap !== null && rl.hardCap > 0) {\n rateRef.current = createRateLimiter(0, {\n requestsHardCap: rl.hardCap,\n requestsWindowSeconds: rl.windowSeconds,\n });\n } else if (config.rateLimitRequests > 0 && rl.windowSeconds !== null) {\n rateRef.current = createRateLimiter(config.rateLimitRequests, {\n requestsHardCap: rl.hardCap,\n requestsWindowSeconds: rl.windowSeconds,\n });\n } else if (config.rateLimitRequests === 0 && rl.hardCap === null) {\n // Upstream reports unlimited (e.g. Code Max) — persist -1 so the\n // config UI reflects the effective state instead of staying at 0.\n saveConfig({ rate_limit_requests: -1 });\n config.rateLimitRequests = -1;\n rateRef.current = null;\n }\n ws.broadcast({ type: \"gate\", stats: gate.getStats(snap) });\n return {\n ok: true,\n hardCap: r.hardCap,\n softLimit: r.softLimit,\n requestsLimit: rl.limit,\n requestsHardCap: rl.hardCap,\n requestsWindowSeconds: rl.windowSeconds,\n };\n }\n return {\n ok: true,\n hardCap: r.hardCap,\n softLimit: r.softLimit,\n requestsLimit: null,\n requestsHardCap: null,\n requestsWindowSeconds: null,\n };\n }\n return r;\n };\n\n if (config.umansApiKey && config.concurrencyHardCap <= 1) {\n void refreshLimits();\n }\n\n const { handleViewer, VIEWER } = createViewerRouter({\n db,\n ws,\n config,\n gate,\n usage,\n vision,\n models,\n reloadConfig,\n refreshLimits,\n restart: () => {\n shutdown().finally(() => process.exit(0));\n },\n });\n\n const handleHealth = (): Response => {\n const stats = gate.getStats(usage.getSnapshot());\n return new Response(\n JSON.stringify({\n status: \"ok\",\n uptime: process.uptime(),\n pendingRequests: server.pendingRequests,\n pendingWebSockets: server.pendingWebSockets,\n gateActive: stats.active,\n gateLimit: stats.softLimit,\n queueDepth: queue.length,\n }),\n { status: 200, headers: { \"content-type\": \"application/json\" } },\n );\n };\n\n const handleMetrics = (): Response => {\n const stats = gate.getStats(usage.getSnapshot());\n metrics.set(\"umans_gate_uptime_seconds\", process.uptime(), \"Process uptime in seconds\");\n metrics.set(\"umans_gate_pending_requests\", server.pendingRequests, \"In-flight HTTP requests\");\n metrics.set(\n \"umans_gate_pending_websockets\",\n server.pendingWebSockets,\n \"Connected WebSocket clients\",\n );\n metrics.set(\"umans_gate_gate_active\", stats.active, \"Active concurrency permits\");\n metrics.set(\"umans_gate_gate_limit\", stats.softLimit, \"Concurrency soft limit\");\n metrics.set(\"umans_gate_queue_depth\", queue.length, \"Write queue depth\");\n return new Response(metrics.format(), {\n status: 200,\n headers: { \"content-type\": \"text/plain; version=0.0.4; charset=utf-8\" },\n });\n };\n\n const fetch = createRequestDispatcher({\n handleViewer,\n handleProxy,\n handleHealth,\n handleMetrics,\n viewerPrefix: VIEWER,\n });\n\n const server = Bun.serve({\n port: config.port,\n hostname: config.host,\n reusePort: true,\n idleTimeout: config.idleTimeout,\n fetch,\n error(err): Response {\n log.error(\"uncaught server error\", {\n message: err.message,\n stack: err.stack,\n });\n return new Response(\n JSON.stringify({ error: \"internal_error\", message: \"Internal proxy error\" }),\n { status: 500, headers: { \"content-type\": \"application/json\" } },\n );\n },\n websocket: {\n open(socket) {\n ws.add(socket as unknown as BunServerWebSocket);\n },\n message() {},\n close(socket) {\n ws.remove(socket as unknown as BunServerWebSocket);\n },\n backpressureLimit: config.wsBackpressureLimit > 0 ? config.wsBackpressureLimit : undefined,\n closeOnBackpressureLimit: config.wsCloseOnBackpressureLimit,\n },\n });\n\n if (options.banner !== false) {\n printBanner(config);\n }\n\n warmer?.start();\n\n let shuttingDown = false;\n const shutdown = async (): Promise<void> => {\n if (shuttingDown) return;\n shuttingDown = true;\n\n log.info(\"graceful shutdown: draining in-flight requests...\");\n\n warmer?.stop();\n models.stop();\n usage.stop();\n\n server.stop(false);\n\n const drainDeadline = Date.now() + 5000;\n while (server.pendingRequests > 0 && Date.now() < drainDeadline) {\n await Bun.sleep(100);\n }\n if (server.pendingRequests > 0) {\n log.warn(`drain timeout: ${server.pendingRequests} requests still in-flight`);\n }\n\n gate.shutdown();\n await queue.flushNow();\n if (writeStore instanceof WorkerCaptureStore) {\n await writeStore.close();\n }\n db.close();\n\n process.removeListener(\"SIGINT\", sigHandler);\n process.removeListener(\"SIGTERM\", sigHandler);\n };\n\n const sigHandler = () => {\n shutdown().finally(() => process.exit(0));\n };\n\n process.once(\"SIGINT\", sigHandler);\n process.once(\"SIGTERM\", sigHandler);\n\n process.on(\"unhandledRejection\", (reason) => {\n log.error(\"unhandledRejection\", { reason: String(reason) });\n });\n process.on(\"uncaughtException\", (err) => {\n log.error(\"uncaughtException\", { message: err.message, stack: err.stack });\n });\n\n return {\n server,\n db,\n ws,\n queue,\n warmer,\n gate,\n usage,\n models,\n rate: rateRef.current,\n config,\n reloadConfig,\n shutdown,\n };\n}\n","// SQLite capture store using bun:sqlite.\n// WAL mode + write-behind queue for non-blocking captures.\n\nimport { Database } from \"bun:sqlite\";\nimport { compressText, decompressText } from \"./compress.js\";\nimport { accountCaptureUsage, backfillFromCaptures, migrateEconomicsSchema } from \"./economics.js\";\nimport type { CaptureRow, CaptureState, ProxyConfig } from \"./types.js\";\nimport {\n LATEST_N_PER_MODEL_VIEW,\n PERFORMANCE_STATS_SQL,\n USAGE_COLUMNS_DDL,\n} from \"./usage-extract.js\";\nimport type { PerformanceStatsRow, UsageMetrics } from \"./usage-extract.js\";\nimport { VisionDescriptionStore } from \"./vision-description-store.js\";\nimport type { VisionCallRecord } from \"./vision/handoff.js\";\n\n/** Prepared statement parameter types. */\ninterface InsertParams {\n $method: string;\n $path: string;\n $url: string;\n $rh: string;\n $rb: string;\n $rs: number;\n $st: number;\n $state: string;\n $inp: string;\n $outp: string;\n}\n\nexport interface UpdateParams {\n $id: number;\n $status: number;\n $rh: string;\n $rb: string;\n $rs: number;\n $ct: string;\n $sse: number;\n $dur: number;\n $fin: number;\n $status_source: \"upstream\" | \"gate\" | null;\n $gate_reason: string | null;\n $usage?: UsageMetrics | null;\n $model?: string | null;\n}\n\ninterface VisionInsertParams {\n $method: string;\n $path: string;\n $url: string;\n $rh: string;\n $rb: string;\n $rs: number;\n $status: number | null;\n $rh2: string;\n $rb2: string;\n $rs2: number;\n $ct: string;\n $dur: number;\n $state: CaptureState;\n $started_at: number;\n $finished_at: number;\n $inp: string;\n $outp: string;\n $model: string;\n $parent_capture_id: number | null;\n $vision_meta: string | null;\n $provider: string | null;\n $streaming: number | null;\n $input_tokens: number | null;\n $output_tokens: number | null;\n $cache_creation_tokens: number | null;\n $cache_read_tokens: number | null;\n $total_input_tokens: number | null;\n $total_output_tokens: number | null;\n $thinking_tokens: number | null;\n $ttft_ms: number | null;\n $tps: number | null;\n $usage_missing: number | null;\n $metrics_extracted_at: number | null;\n}\n\nexport interface VisionUpdateParams {\n $id: number;\n $status: number | null;\n $rh: string;\n $rb: string;\n $rs: number;\n $ct: string;\n $sse: number;\n $dur: number;\n $fin: number;\n $status_source: \"upstream\" | \"gate\" | null;\n $gate_reason: string | null;\n $vision_meta: string | null;\n $model: string | null;\n $provider: string | null;\n $streaming: number | null;\n $input_tokens: number | null;\n $output_tokens: number | null;\n $cache_creation_tokens: number | null;\n $cache_read_tokens: number | null;\n $total_input_tokens: number | null;\n $total_output_tokens: number | null;\n $thinking_tokens: number | null;\n $ttft_ms: number | null;\n $tps: number | null;\n $usage_missing: number | null;\n $metrics_extracted_at: number | null;\n}\n\n/** Flatten a UsageMetrics object into the `$`-prefixed params for stmtUpdate.\n * Returns nulls when usage is absent so a single prepared statement suffices. */\nexport function flattenUsage(usage: UsageMetrics | null | undefined) {\n if (!usage) {\n return {\n $provider: null,\n $streaming: null,\n $input_tokens: null,\n $output_tokens: null,\n $cache_creation_tokens: null,\n $cache_read_tokens: null,\n $total_input_tokens: null,\n $total_output_tokens: null,\n $thinking_tokens: null,\n $ttft_ms: null,\n $tps: null,\n $usage_missing: null,\n $metrics_extracted_at: null,\n };\n }\n return {\n $provider: usage.provider,\n $streaming: usage.streaming ? 1 : 0,\n $input_tokens: usage.input_tokens,\n $output_tokens: usage.output_tokens,\n $cache_creation_tokens: usage.cache_creation_tokens,\n $cache_read_tokens: usage.cache_read_tokens,\n $total_input_tokens: usage.total_input_tokens,\n $total_output_tokens: usage.total_output_tokens,\n $thinking_tokens: usage.thinking_tokens,\n $ttft_ms: usage.ttft_ms,\n $tps: usage.tps,\n $usage_missing: usage.usage_missing ? 1 : 0,\n $metrics_extracted_at: Date.now(),\n };\n}\n\n/** Add a column only if it doesn't already exist (migration safety). */\nfunction addColumnIfMissing(db: Database, name: string, type: string): void {\n try {\n db.exec(`ALTER TABLE captures ADD COLUMN ${name} ${type}`);\n } catch {\n // Column already exists — expected for existing DBs.\n }\n}\n\n/** Run all schema migrations/pragmas for a capture database.\n * Idempotent: safe to call on every startup, including existing databases. */\nexport function migrateCaptureSchema(db: Database): void {\n db.exec(\"PRAGMA journal_mode = WAL;\");\n db.exec(\"PRAGMA synchronous = NORMAL;\");\n db.exec(\"PRAGMA temp_store = MEMORY;\");\n db.exec(\"PRAGMA cache_size = -64000;\"); // 64MB page cache (was 20MB)\n db.exec(\"PRAGMA mmap_size = 268435456;\"); // 256MB memory-mapped I/O\n db.exec(\"PRAGMA journal_size_limit = 67108864;\"); // 64MB WAL cap\n db.exec(\"PRAGMA busy_timeout = 5000;\"); // 5s wait on lock contention\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS captures (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n method TEXT NOT NULL,\n path TEXT NOT NULL,\n url TEXT NOT NULL,\n request_headers TEXT,\n request_body TEXT,\n request_size INTEGER DEFAULT 0,\n response_status INTEGER,\n response_headers TEXT,\n response_body TEXT,\n response_size INTEGER DEFAULT 0,\n content_type TEXT,\n is_sse INTEGER DEFAULT 0,\n duration_ms INTEGER DEFAULT 0,\n state TEXT DEFAULT 'streaming',\n started_at INTEGER,\n finished_at INTEGER,\n incoming_protocol TEXT,\n upstream_protocol TEXT\n );\n `);\n\n // Add columns to existing DBs (no-op if already present).\n addColumnIfMissing(db, \"incoming_protocol\", \"TEXT\");\n addColumnIfMissing(db, \"upstream_protocol\", \"TEXT\");\n // Vision merge: flag vision-call rows + link to parent capture.\n addColumnIfMissing(db, \"is_vision\", \"INTEGER DEFAULT 0\");\n addColumnIfMissing(db, \"parent_capture_id\", \"INTEGER\");\n // Vision metadata JSON (status, httpStatus, latencyMs, description, error,\n // imageHash, imageSize, model, target) stored separately so request_body\n // and response_body can hold the actual HTTP request/response exchanged\n // with the vision model.\n addColumnIfMissing(db, \"vision_meta\", \"TEXT\");\n // Status source: \"upstream\" = response from upstream API, \"gate\" = proxy-generated.\n addColumnIfMissing(db, \"status_source\", \"TEXT\");\n // Human-readable explanation when the proxy (gate) generated the HTTP status.\n addColumnIfMissing(db, \"gate_reason\", \"TEXT\");\n\n // Token-usage columns: split DDL on ';', strip SQL comments, exec each individually so\n // SQLite doesn't halt on the first \"duplicate column\" error when an existing DB is reopened.\n for (const raw of USAGE_COLUMNS_DDL.split(\";\")) {\n const stmt = raw\n .split(\"\\n\")\n .filter((line) => !line.trim().startsWith(\"--\"))\n .join(\"\\n\")\n .trim();\n if (!stmt) continue;\n try {\n db.exec(stmt);\n } catch {\n // Column already exists — expected for existing DBs.\n }\n }\n\n // Latest-N-per-model materialized view (IF NOT EXISTS → safe on every restart).\n db.exec(LATEST_N_PER_MODEL_VIEW);\n\n // Covering indexes for fast aggregation queries.\n // Without these, every stats query scans the entire captures table.\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_captures_model_started\n ON captures(model, started_at DESC)\n WHERE model IS NOT NULL AND state = 'done';\n `);\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_captures_model_usage\n ON captures(model, usage_missing, started_at DESC)\n WHERE model IS NOT NULL;\n `);\n\n // Null out tps values computed before the 1-second generation-time floor\n // was introduced. Idempotent: after the first run no rows match because\n // computeTps() already returns null for short generations.\n db.exec(`\n UPDATE captures\n SET tps = NULL\n WHERE tps IS NOT NULL\n AND duration_ms IS NOT NULL\n AND (duration_ms - COALESCE(ttft_ms, 0)) < 1000\n `);\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS vision_descriptions (\n key TEXT PRIMARY KEY,\n image_hash TEXT NOT NULL,\n model TEXT NOT NULL,\n prompt_version INTEGER NOT NULL,\n description TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n last_accessed_at INTEGER NOT NULL\n );\n `);\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_vision_desc_last_accessed\n ON vision_descriptions(last_accessed_at ASC);\n `);\n\n // Economics schema (model_pricing + daily_usage tables, usage_accounted column).\n migrateEconomicsSchema(db);\n // Account captures from before the economics migration.\n backfillFromCaptures(db);\n}\n\n/** Capture database — wraps a bun:sqlite Database with prepared statements. */\nexport class CaptureDB {\n private db: Database;\n private stmtInsert: ReturnType<Database[\"prepare\"]>;\n private stmtUpdate: ReturnType<Database[\"prepare\"]>;\n private stmtDeleteOld: ReturnType<Database[\"prepare\"]>;\n private stmtGet: ReturnType<Database[\"prepare\"]>;\n private stmtList: ReturnType<Database[\"prepare\"]>;\n private stmtCount: ReturnType<Database[\"prepare\"]>;\n private stmtSetState: ReturnType<Database[\"prepare\"]>;\n private stmtUpdateRequestBody: ReturnType<Database[\"prepare\"]>;\n private stmtPerformanceStats: ReturnType<Database[\"prepare\"]>;\n private stmtInsertVision: ReturnType<Database[\"prepare\"]>;\n private stmtUpdateVision: ReturnType<Database[\"prepare\"]>;\n private stmtListVision: ReturnType<Database[\"prepare\"]>;\n private stmtClearVision: ReturnType<Database[\"prepare\"]>;\n private stmtListVisionRecords: ReturnType<Database[\"prepare\"]>;\n private readonly visionDescStore: VisionDescriptionStore;\n private rowCount: number;\n readonly maxCaptures: number;\n compressionEnabled: boolean;\n\n constructor(\n config: Pick<ProxyConfig, \"dbPath\" | \"maxCaptures\"> &\n Partial<Pick<ProxyConfig, \"compressionEnabled\">>,\n ) {\n this.db = new Database(config.dbPath);\n this.maxCaptures = config.maxCaptures;\n this.compressionEnabled = config.compressionEnabled ?? true;\n\n migrateCaptureSchema(this.db);\n\n this.stmtInsert = this.db.prepare(`\n INSERT INTO captures (method, path, url, request_headers, request_body, request_size, started_at, state, incoming_protocol, upstream_protocol)\n VALUES ($method, $path, $url, $rh, $rb, $rs, $st, $state, $inp, $outp)\n `);\n this.stmtUpdate = this.db.prepare(`\n UPDATE captures SET\n response_status = $status,\n response_headers = $rh,\n response_body = $rb,\n response_size = $rs,\n content_type = $ct,\n is_sse = $sse,\n duration_ms = $dur,\n state = 'done',\n finished_at = $fin,\n status_source = $status_source,\n gate_reason = $gate_reason,\n provider = $provider,\n streaming = $streaming,\n model = $model,\n input_tokens = $input_tokens,\n output_tokens = $output_tokens,\n cache_creation_tokens = $cache_creation_tokens,\n cache_read_tokens = $cache_read_tokens,\n total_input_tokens = $total_input_tokens,\n total_output_tokens = $total_output_tokens,\n thinking_tokens = $thinking_tokens,\n ttft_ms = $ttft_ms,\n tps = $tps,\n usage_missing = $usage_missing,\n metrics_extracted_at = $metrics_extracted_at\n WHERE id = $id\n `);\n this.stmtDeleteOld = this.db.prepare(\n \"DELETE FROM captures WHERE id NOT IN (SELECT id FROM captures ORDER BY id DESC LIMIT $n)\",\n );\n this.sweepStaleCaptures();\n this.stmtGet = this.db.prepare(\"SELECT * FROM captures WHERE id = $id\");\n this.stmtList = this.db.prepare(\n `SELECT id, method, path, response_status, is_sse, content_type,\n request_size, response_size, duration_ms, state, started_at, finished_at,\n incoming_protocol, upstream_protocol, model, usage_missing,\n ttft_ms, tps, input_tokens, output_tokens,\n cache_creation_tokens, cache_read_tokens,\n total_input_tokens, total_output_tokens, is_vision,\n status_source, gate_reason\n FROM captures ORDER BY id DESC LIMIT ?`,\n );\n this.stmtCount = this.db.prepare(\"SELECT COUNT(*) AS c FROM captures\");\n this.stmtSetState = this.db.prepare(\"UPDATE captures SET state = $state WHERE id = $id\");\n this.stmtUpdateRequestBody = this.db.prepare(\n \"UPDATE captures SET request_body = $rb, request_size = $rs WHERE id = $id\",\n );\n this.stmtPerformanceStats = this.db.prepare(PERFORMANCE_STATS_SQL);\n this.stmtInsertVision = this.db.prepare(\n `INSERT INTO captures\n (method, path, url, request_headers, request_body, request_size,\n response_status, response_headers, response_body, response_size,\n content_type, is_sse, duration_ms, state, started_at, finished_at,\n incoming_protocol, upstream_protocol, model,\n is_vision, parent_capture_id, vision_meta,\n provider, streaming, input_tokens, output_tokens,\n cache_creation_tokens, cache_read_tokens,\n total_input_tokens, total_output_tokens, thinking_tokens,\n ttft_ms, tps, usage_missing, metrics_extracted_at)\n VALUES\n ($method, $path, $url, $rh, $rb, $rs,\n $status, $rh2, $rb2, $rs2,\n $ct, 0, $dur, $state, $started_at, $finished_at,\n $inp, $outp, $model,\n 1, $parent_capture_id, $vision_meta,\n $provider, $streaming, $input_tokens, $output_tokens,\n $cache_creation_tokens, $cache_read_tokens,\n $total_input_tokens, $total_output_tokens, $thinking_tokens,\n $ttft_ms, $tps, $usage_missing, $metrics_extracted_at)`,\n );\n this.stmtUpdateVision = this.db.prepare(`\n UPDATE captures SET\n response_status = $status,\n response_headers = $rh,\n response_body = $rb,\n response_size = $rs,\n content_type = $ct,\n is_sse = $sse,\n duration_ms = $dur,\n state = 'done',\n finished_at = $fin,\n status_source = $status_source,\n gate_reason = $gate_reason,\n vision_meta = $vision_meta,\n provider = $provider,\n streaming = $streaming,\n model = $model,\n input_tokens = $input_tokens,\n output_tokens = $output_tokens,\n cache_creation_tokens = $cache_creation_tokens,\n cache_read_tokens = $cache_read_tokens,\n total_input_tokens = $total_input_tokens,\n total_output_tokens = $total_output_tokens,\n thinking_tokens = $thinking_tokens,\n ttft_ms = $ttft_ms,\n tps = $tps,\n usage_missing = $usage_missing,\n metrics_extracted_at = $metrics_extracted_at\n WHERE id = $id\n `);\n this.stmtListVision = this.db.prepare(\n `SELECT id, method, path, response_status, is_sse, content_type,\n request_size, response_size, duration_ms, state, started_at, finished_at,\n incoming_protocol, upstream_protocol, model, usage_missing,\n ttft_ms, tps, input_tokens, output_tokens,\n cache_creation_tokens, cache_read_tokens,\n total_input_tokens, total_output_tokens, is_vision, parent_capture_id,\n status_source, gate_reason\n FROM captures WHERE is_vision = 1\n ORDER BY id DESC LIMIT ?`,\n );\n this.stmtClearVision = this.db.prepare(\"DELETE FROM captures WHERE is_vision = 1\");\n this.stmtListVisionRecords = this.db.prepare(\n `SELECT id, response_body, request_body, vision_meta, started_at, finished_at, parent_capture_id, model,\n state, incoming_protocol, upstream_protocol\n FROM captures WHERE is_vision = 1\n ORDER BY id DESC LIMIT ?`,\n );\n this.visionDescStore = new VisionDescriptionStore(this.db);\n this.rowCount = (this.stmtCount.get() as { c: number }).c;\n }\n\n /** Mark captures stuck in \"streaming\" or \"enqueued\" from a previous run as \"done\".\n * Called once on startup so the dashboard doesn't show phantom \"running\" rows. */\n private sweepStaleCaptures(): void {\n this.db\n .prepare(\"UPDATE captures SET state = 'done' WHERE state IN ('streaming', 'enqueued')\")\n .run();\n }\n\n /** Insert a new capture row and enforce the ring buffer. Returns the new id. */\n startCapture(params: InsertParams): number {\n const compressed = {\n ...params,\n $rh: compressText(params.$rh, this.compressionEnabled),\n $rb: compressText(params.$rb, this.compressionEnabled),\n };\n let id = 0;\n this.db.transaction(() => {\n id = Number(this.stmtInsert.run(compressed as unknown as never).lastInsertRowid);\n if (++this.rowCount > this.maxCaptures) {\n this.stmtDeleteOld.run({ $n: this.maxCaptures });\n this.rowCount = this.maxCaptures;\n }\n })();\n return id;\n }\n\n /** Update a capture row with response data. */\n updateCapture(params: UpdateParams): void {\n const compressed = {\n ...params,\n $rh: compressText(params.$rh, this.compressionEnabled),\n $rb: compressText(params.$rb, this.compressionEnabled),\n };\n this.db.transaction(() => {\n this.stmtUpdate.run(compressed as unknown as never);\n accountCaptureUsage(this.db, params.$id);\n })();\n }\n\n /** Transition a capture's state (enqueued → streaming → done). */\n setState(id: number, state: \"enqueued\" | \"streaming\" | \"done\"): void {\n this.stmtSetState.run({ $state: state, $id: id });\n }\n\n /** Update request_body and request_size after in-flight modification (e.g. vision handoff). */\n updateRequestBody(id: number, body: string, size: number): void {\n const compressedBody = compressText(body, this.compressionEnabled);\n this.stmtUpdateRequestBody.run({ $rb: compressedBody, $rs: size, $id: id });\n }\n\n /** Batch-update multiple captures in a single transaction. */\n async batchUpdate(items: Array<{ id: number; res: Omit<UpdateParams, \"$id\"> }>): Promise<void> {\n this.db.transaction(() => {\n for (const it of items) {\n this.stmtUpdate.run({\n ...it.res,\n $rh: compressText(it.res.$rh, this.compressionEnabled),\n $rb: compressText(it.res.$rb, this.compressionEnabled),\n ...flattenUsage(it.res.$usage),\n $model: it.res.$model ?? null,\n $id: it.id,\n } as unknown as never);\n accountCaptureUsage(this.db, it.id);\n }\n })();\n }\n\n /** Get a single capture by id (full row including bodies). */\n get(id: number): CaptureRow | null {\n const raw = this.stmtGet.get({ $id: id }) as Record<string, unknown> | undefined;\n if (!raw) return null;\n raw.request_headers = decompressText(raw.request_headers as string | Uint8Array | null);\n raw.request_body = decompressText(raw.request_body as string | Uint8Array | null);\n raw.response_headers = decompressText(raw.response_headers as string | Uint8Array | null);\n raw.response_body = decompressText(raw.response_body as string | Uint8Array | null);\n return raw as unknown as CaptureRow;\n }\n\n /** List recent capture summaries (no body data). */\n list(limit: number): CaptureRow[] {\n return this.stmtList.all(Math.min(limit, 1000)) as CaptureRow[];\n }\n\n /** Compute per-model performance stats entirely in SQL (p10/p50/p95, sums, cached%).\n * Uses the v_latest_requests_per_model view + nearest-rank percentiles.\n * Returns PerformanceStatsRow[] — no JS post-processing needed. */\n getPerformanceStats(): PerformanceStatsRow[] {\n const rows = this.stmtPerformanceStats.all() as Array<Record<string, unknown>>;\n return rows.map((row) => ({\n model: row.model as string,\n provider: (row.provider as \"anthropic\" | \"openai\") ?? \"anthropic\",\n request_count: Number(row.request_count) || 0,\n streaming_count: Number(row.streaming_count) || 0,\n total_input_tokens: Number(row.total_input_tokens) || 0,\n total_output_tokens: Number(row.total_output_tokens) || 0,\n total_cache_read_tokens: Number(row.total_cache_read_tokens) || 0,\n total_thinking_tokens: Number(row.total_thinking_tokens) || 0,\n cached_pct: Number(row.cached_pct) || 0,\n ttft_mean: (row.ttft_mean as number | null) ?? null,\n ttft_p10: (row.ttft_p10 as number | null) ?? null,\n ttft_p50: (row.ttft_p50 as number | null) ?? null,\n ttft_p95: (row.ttft_p95 as number | null) ?? null,\n ttft_outlier_count: 0,\n tps_mean: (row.tps_mean as number | null) ?? null,\n tps_p10: (row.tps_p10 as number | null) ?? null,\n tps_p50: (row.tps_p50 as number | null) ?? null,\n tps_p95: (row.tps_p95 as number | null) ?? null,\n tps_outlier_count: 0,\n }));\n }\n\n /** Delete all captures. */\n clear(): void {\n this.db.prepare(\"DELETE FROM captures\").run();\n this.rowCount = 0;\n }\n\n /** Insert a vision-call capture row. Returns the new row id. */\n insertVisionCapture(params: VisionInsertParams): number {\n const compressed = {\n ...params,\n $rh: compressText(params.$rh, this.compressionEnabled),\n $rb: compressText(params.$rb, this.compressionEnabled),\n $rh2: compressText(params.$rh2, this.compressionEnabled),\n $rb2: compressText(params.$rb2, this.compressionEnabled),\n };\n let id = 0;\n this.db.transaction(() => {\n id = Number(this.stmtInsertVision.run(compressed as unknown as never).lastInsertRowid);\n if (++this.rowCount > this.maxCaptures) {\n this.stmtDeleteOld.run({ $n: this.maxCaptures });\n this.rowCount = this.maxCaptures;\n }\n })();\n return id;\n }\n\n /** Update a vision-call capture row with final response data and mark it done. */\n updateVisionCapture(params: VisionUpdateParams): void {\n const compressed = {\n ...params,\n $rh: compressText(params.$rh, this.compressionEnabled),\n $rb: compressText(params.$rb, this.compressionEnabled),\n };\n this.db.transaction(() => {\n this.stmtUpdateVision.run(compressed as unknown as never);\n accountCaptureUsage(this.db, params.$id);\n })();\n }\n\n /** List recent vision-call capture summaries (is_vision=1). */\n listVisionCaptures(limit: number): CaptureRow[] {\n return this.stmtListVision.all(Math.min(limit, 1000)) as CaptureRow[];\n }\n\n /** Delete only vision-call captures (is_vision=1). */\n clearVisionCaptures(): void {\n const deleted = this.stmtClearVision.run();\n this.rowCount = Math.max(0, this.rowCount - (deleted.changes ?? 0));\n }\n\n /**\n * Reconstruct VisionCallRecord[] from stored vision-call rows.\n * The metadata is persisted as JSON in vision_meta by addRecord().\n */\n getVisionCallRecords(limit: number): VisionCallRecord[] {\n const rows = this.stmtListVisionRecords.all(Math.min(limit, 1000)) as Array<{\n id: number;\n response_body: string | Uint8Array | null;\n request_body: string | Uint8Array | null;\n vision_meta: string | null;\n started_at: number | null;\n finished_at: number | null;\n parent_capture_id: number | null;\n model: string | null;\n state: string | null;\n incoming_protocol: string | null;\n upstream_protocol: string | null;\n }>;\n for (const row of rows) {\n row.response_body = decompressText(row.response_body);\n row.request_body = decompressText(row.request_body);\n }\n const records: VisionCallRecord[] = [];\n for (const row of rows) {\n if (!row.vision_meta) continue;\n try {\n const data = JSON.parse(row.vision_meta) as {\n status: VisionCallRecord[\"status\"];\n httpStatus: number | null;\n latencyMs: number;\n description: string;\n error: string | null;\n imageHash: string | null;\n imageSize: number;\n model: string;\n target: string;\n };\n records.push({\n id: row.id,\n timestamp: row.finished_at ?? row.started_at ?? Date.now(),\n captureId: row.parent_capture_id,\n model: data.model ?? row.model ?? \"\",\n target: data.target ?? \"\",\n imageSize: data.imageSize ?? 0,\n imageHash: data.imageHash ?? null,\n status: data.status,\n httpStatus: data.httpStatus ?? null,\n latencyMs: data.latencyMs ?? 0,\n description: data.description ?? \"\",\n error: data.error ?? null,\n incomingProtocol: row.incoming_protocol ?? \"\",\n upstreamProtocol: row.upstream_protocol ?? \"\",\n state: (row.state ?? \"done\") as CaptureState,\n });\n } catch {\n // Corrupt JSON — skip this row.\n }\n }\n return records;\n }\n\n /** Close the database connection. */\n close(): void {\n this.db.close();\n }\n\n /** Access the raw bun:sqlite Database (for economics queries). */\n get rawDb(): Database {\n return this.db;\n }\n\n /** Insert or update a vision description in the persistent store. */\n upsertVisionDescription(params: {\n $key: string;\n $image_hash: string;\n $model: string;\n $prompt_version: number;\n $description: string;\n $now: number;\n }): void {\n this.visionDescStore.upsert(params);\n }\n\n /** Look up a vision description by key. Returns null if not found. */\n getVisionDescription(key: string): { description: string; created_at: number } | null {\n return this.visionDescStore.get(key);\n }\n\n deleteVisionDescription(key: string): void {\n this.visionDescStore.delete(key);\n }\n\n evictVisionDescriptions(cutoff: number, maxRows: number): number {\n return this.visionDescStore.evict(cutoff, maxRows);\n }\n\n /** List recent non-expired vision descriptions for cache warming. Returns up to `limit` entries. */\n listVisionDescriptionsForWarming(\n limit: number,\n cutoff: number,\n ): Array<{ key: string; description: string }> {\n return this.visionDescStore.listForWarming(limit, cutoff);\n }\n}\n","export const COMPRESSION_THRESHOLD = 256;\n\nexport function compressText(value: string | null, enabled: boolean): string | Uint8Array | null {\n if (value === null) {\n return null;\n }\n if (!enabled) {\n return value;\n }\n // Fast-path: if char count >= threshold, byte count is guaranteed >= threshold\n // (UTF-8 uses 1-4 bytes per char, so byte count >= char count). Skip the scan.\n if (value.length >= COMPRESSION_THRESHOLD) {\n return Bun.zstdCompressSync(Buffer.from(value, \"utf-8\"), { level: 3 });\n }\n // Short strings: check byte count for multi-byte content (CJK, emoji).\n if (Buffer.byteLength(value, \"utf-8\") < COMPRESSION_THRESHOLD) {\n return value;\n }\n return Bun.zstdCompressSync(Buffer.from(value, \"utf-8\"), { level: 3 });\n}\n\nexport function decompressText(value: string | Uint8Array | null): string | null {\n if (value === null) {\n return null;\n }\n if (typeof value === \"string\") {\n return value;\n }\n if (value instanceof Uint8Array) {\n try {\n return Bun.zstdDecompressSync(value).toString(\"utf-8\");\n } catch {\n return null;\n }\n }\n return null;\n}\n","type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nconst ENV_LEVEL = (process.env.UMANS_LOG_LEVEL ?? \"info\") as LogLevel;\nconst globalMinPriority = LEVEL_PRIORITY[ENV_LEVEL] ?? LEVEL_PRIORITY.info;\n\ninterface LogContext {\n captureId?: string | number;\n module?: string;\n [k: string]: unknown;\n}\n\nexport interface Logger {\n debug(msg: string, ctx?: LogContext): void;\n info(msg: string, ctx?: LogContext): void;\n warn(msg: string, ctx?: LogContext): void;\n error(msg: string, ctx?: LogContext): void;\n child(ctx: LogContext): Logger;\n}\n\nexport function createLogger(module: string): Logger {\n return makeLogger({ module });\n}\n\nfunction makeLogger(baseCtx: LogContext): Logger {\n const log = (level: LogLevel, msg: string, ctx?: LogContext) => {\n if (LEVEL_PRIORITY[level] < globalMinPriority) return;\n const merged = ctx ? { ...baseCtx, ...ctx } : baseCtx;\n const ctxStr =\n Object.keys(merged).length > 0\n ? ` ${Object.entries(merged)\n .map(([k, v]) => `${k}=${v}`)\n .join(\" \")}`\n : \"\";\n const line = `[${level}] ${msg}${ctxStr}`;\n if (level === \"error\" || level === \"warn\") {\n console.error(line);\n } else {\n console.log(line);\n }\n };\n return {\n debug: (m, c) => log(\"debug\", m, c),\n info: (m, c) => log(\"info\", m, c),\n warn: (m, c) => log(\"warn\", m, c),\n error: (m, c) => log(\"error\", m, c),\n child: (ctx) => makeLogger({ ...baseCtx, ...ctx }),\n };\n}\n","// Economics module — persistent daily usage accumulation + model pricing sync.\n//\n// This module provides:\n// - model_pricing table: stores per-model pricing (synced from /v1/models API)\n// - daily_usage table: accumulates per-day, per-model token totals + costs\n// - Captures usage_accounted flag on the captures table for idempotent accounting\n//\n// Design constraints (from user):\n// 1. Centralized sync: piggyback on existing ModelsClient poll — no extra API calls.\n// 2. Price-as-of-day: costs are frozen at accumulation time; historical rows are\n// never recomputed when pricing changes.\n// 3. New model mitigation: unknown models accumulate with $0 cost + pricing_known=0;\n// when sync discovers pricing, those rows are backfilled once.\n\nimport type { Database } from \"bun:sqlite\";\nimport { createLogger } from \"./logger.js\";\nimport type { ModelEntry } from \"./models.js\";\n\nconst log = createLogger(\"economics\");\n\n/** Ratio of cache_read price to input price (derived from Umans pricing page). */\nconst CACHE_READ_RATIO = 0.186;\n\n/** Seed pricing for known Umans models (used when API hasn't returned pricing yet). */\nconst SEED_PRICING: Record<string, { input: number; output: number; cache_read: number }> = {\n \"umans-glm-5.2\": { input: 1.4, output: 4.4, cache_read: 0.26 },\n \"umans-kimi-k2.7\": { input: 0.95, output: 4.0, cache_read: 0.19 },\n \"umans-coder\": { input: 0.95, output: 4.0, cache_read: 0.19 },\n \"umans-flash\": { input: 0.15, output: 1.0, cache_read: 0.05 },\n \"umans-qwen3.6-35b-a3b\": { input: 0.15, output: 1.0, cache_read: 0.05 },\n};\n\n/** SQL DDL for economics tables. Idempotent — safe to run on every startup. */\nexport const ECONOMICS_DDL = `\nCREATE TABLE IF NOT EXISTS model_pricing (\n model_id TEXT PRIMARY KEY,\n input_per_mtoken REAL NOT NULL DEFAULT 0,\n output_per_mtoken REAL NOT NULL DEFAULT 0,\n cache_read_per_mtoken REAL NOT NULL DEFAULT 0,\n pricing_known INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL DEFAULT 0\n);\n\nCREATE TABLE IF NOT EXISTS daily_usage (\n date TEXT NOT NULL,\n model TEXT NOT NULL,\n requests INTEGER NOT NULL DEFAULT 0,\n input_tokens INTEGER NOT NULL DEFAULT 0,\n output_tokens INTEGER NOT NULL DEFAULT 0,\n cache_read_tokens INTEGER NOT NULL DEFAULT 0,\n cache_creation_tokens INTEGER NOT NULL DEFAULT 0,\n thinking_tokens INTEGER NOT NULL DEFAULT 0,\n cost_input REAL NOT NULL DEFAULT 0,\n cost_output REAL NOT NULL DEFAULT 0,\n cost_cache_read REAL NOT NULL DEFAULT 0,\n cost_cache_creation REAL NOT NULL DEFAULT 0,\n cost_total REAL NOT NULL DEFAULT 0,\n pricing_known INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (date, model)\n) WITHOUT ROWID;\n` as const;\n\n/** SQL to add the usage_accounted column to captures (idempotency flag). */\nexport const USAGE_ACCOUNTED_DDL =\n \"ALTER TABLE captures ADD COLUMN usage_accounted INTEGER DEFAULT 0\";\n\n/** Result of a pricing lookup for a single model. */\ninterface PricingRow {\n input_per_mtoken: number;\n output_per_mtoken: number;\n cache_read_per_mtoken: number;\n pricing_known: number;\n}\n\n/** Run economics schema migrations. Idempotent. */\nexport function migrateEconomicsSchema(db: Database): void {\n db.exec(ECONOMICS_DDL);\n\n // Add usage_accounted column to captures (no-op if already exists).\n try {\n db.exec(USAGE_ACCOUNTED_DDL);\n } catch {\n // Column already exists — expected for existing DBs.\n }\n\n // Covering index for unaccounted captures (fast incremental accounting).\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_captures_usage_accounted\n ON captures(usage_accounted, id)\n WHERE usage_accounted = 0;\n `);\n\n // Seed pricing for known models if model_pricing is empty.\n const count = (db.prepare(\"SELECT COUNT(*) AS c FROM model_pricing\").get() as { c: number }).c;\n if (count === 0) {\n const now = Date.now();\n const stmt = db.prepare(\n `INSERT INTO model_pricing\n (model_id, input_per_mtoken, output_per_mtoken, cache_read_per_mtoken, pricing_known, updated_at)\n VALUES ($model_id, $input, $output, $cache_read, 0, $now)`,\n );\n for (const [modelId, pricing] of Object.entries(SEED_PRICING)) {\n stmt.run({\n $model_id: modelId,\n $input: pricing.input,\n $output: pricing.output,\n $cache_read: pricing.cache_read,\n $now: now,\n });\n }\n log.info(`seeded ${Object.keys(SEED_PRICING).length} model pricing entries`);\n }\n}\n\n/** Get pricing for a model, or null if unknown. */\nfunction getPricing(db: Database, modelId: string): PricingRow | null {\n const row = db\n .prepare(\n `SELECT input_per_mtoken, output_per_mtoken, cache_read_per_mtoken, pricing_known\n FROM model_pricing WHERE model_id = $model_id`,\n )\n .get({ $model_id: modelId }) as PricingRow | undefined;\n return row ?? null;\n}\n\n/**\n * Account a single capture's usage into daily_usage.\n * Idempotent: only processes captures where usage_accounted=0.\n * Must be called within the same transaction as the capture UPDATE.\n *\n * Costs are computed using current model_pricing at accumulation time\n * and frozen in daily_usage (price-as-of-day constraint).\n */\nexport function accountCaptureUsage(db: Database, captureId: number): void {\n const capture = db\n .prepare(\n `SELECT model, input_tokens, output_tokens, cache_read_tokens,\n cache_creation_tokens, thinking_tokens, usage_missing, started_at\n FROM captures\n WHERE id = $id AND usage_accounted = 0`,\n )\n .get({ $id: captureId }) as\n | {\n model: string | null;\n input_tokens: number | null;\n output_tokens: number | null;\n cache_read_tokens: number | null;\n cache_creation_tokens: number | null;\n thinking_tokens: number | null;\n usage_missing: number | null;\n started_at: number | null;\n }\n | undefined;\n\n if (!capture) return; // Already accounted or not found.\n\n // Skip if no model or usage is missing.\n if (!capture.model || capture.usage_missing) {\n db.prepare(\"UPDATE captures SET usage_accounted = 1 WHERE id = $id\").run({\n $id: captureId,\n });\n return;\n }\n\n const model = capture.model;\n const startedAt = capture.started_at ?? Date.now();\n const date = new Date(startedAt).toISOString().slice(0, 10); // YYYY-MM-DD\n\n const inputTokens = capture.input_tokens ?? 0;\n const outputTokens = capture.output_tokens ?? 0;\n const cacheReadTokens = capture.cache_read_tokens ?? 0;\n const cacheCreationTokens = capture.cache_creation_tokens ?? 0;\n const thinkingTokens = capture.thinking_tokens ?? 0;\n\n // Get pricing (may be null for unknown models).\n const pricing = getPricing(db, model);\n\n // Compute costs: $ = tokens / 1_000_000 * price_per_mtoken.\n // Unknown models accumulate with $0 cost + pricing_known=0.\n const inputPrice = pricing?.input_per_mtoken ?? 0;\n const outputPrice = pricing?.output_per_mtoken ?? 0;\n const cacheReadPrice = pricing?.cache_read_per_mtoken ?? 0;\n const pricingKnown = pricing?.pricing_known ?? 0;\n\n // Cache creation is priced at the input rate (Anthropic convention).\n const cacheCreationPrice = inputPrice;\n\n const costInput = (inputTokens / 1_000_000) * inputPrice;\n const costOutput = (outputTokens / 1_000_000) * outputPrice;\n const costCacheRead = (cacheReadTokens / 1_000_000) * cacheReadPrice;\n const costCacheCreation = (cacheCreationTokens / 1_000_000) * cacheCreationPrice;\n const costTotal = costInput + costOutput + costCacheRead + costCacheCreation;\n\n // UPSERT into daily_usage (accumulate on conflict).\n db.prepare(\n `INSERT INTO daily_usage\n (date, model, requests, input_tokens, output_tokens,\n cache_read_tokens, cache_creation_tokens, thinking_tokens,\n cost_input, cost_output, cost_cache_read, cost_cache_creation,\n cost_total, pricing_known)\n VALUES\n ($date, $model, 1, $input_tokens, $output_tokens,\n $cache_read_tokens, $cache_creation_tokens, $thinking_tokens,\n $cost_input, $cost_output, $cost_cache_read, $cost_cache_creation,\n $cost_total, $pricing_known)\n ON CONFLICT(date, model) DO UPDATE SET\n requests = requests + excluded.requests,\n input_tokens = input_tokens + excluded.input_tokens,\n output_tokens = output_tokens + excluded.output_tokens,\n cache_read_tokens = cache_read_tokens + excluded.cache_read_tokens,\n cache_creation_tokens = cache_creation_tokens + excluded.cache_creation_tokens,\n thinking_tokens = thinking_tokens + excluded.thinking_tokens,\n cost_input = cost_input + excluded.cost_input,\n cost_output = cost_output + excluded.cost_output,\n cost_cache_read = cost_cache_read + excluded.cost_cache_read,\n cost_cache_creation = cost_cache_creation + excluded.cost_cache_creation,\n cost_total = cost_total + excluded.cost_total,\n pricing_known = excluded.pricing_known`,\n ).run({\n $date: date,\n $model: model,\n $input_tokens: inputTokens,\n $output_tokens: outputTokens,\n $cache_read_tokens: cacheReadTokens,\n $cache_creation_tokens: cacheCreationTokens,\n $thinking_tokens: thinkingTokens,\n $cost_input: costInput,\n $cost_output: costOutput,\n $cost_cache_read: costCacheRead,\n $cost_cache_creation: costCacheCreation,\n $cost_total: costTotal,\n $pricing_known: pricingKnown,\n });\n\n // Mark capture as accounted.\n db.prepare(\"UPDATE captures SET usage_accounted = 1 WHERE id = $id\").run({\n $id: captureId,\n });\n}\n\n/**\n * Backfill daily_usage from existing captures that haven't been accounted yet.\n * Called once on migration. Each capture is accounted individually to ensure\n * correct date bucketing (based on started_at).\n */\nexport function backfillFromCaptures(db: Database): number {\n const ids = db\n .prepare(\n `SELECT id FROM captures WHERE usage_accounted = 0 AND model IS NOT NULL AND usage_missing = 0\n ORDER BY id ASC`,\n )\n .all() as Array<{ id: number }>;\n\n let count = 0;\n db.transaction(() => {\n for (const { id } of ids) {\n accountCaptureUsage(db, id);\n count++;\n }\n })();\n\n if (count > 0) {\n log.info(`backfilled ${count} captures into daily_usage`);\n }\n return count;\n}\n\n/**\n * Sync pricing from ModelsClient into model_pricing table.\n * Called periodically — uses models.list() which is a local map lookup (no API call).\n *\n * For each model from the API:\n * - If exists in model_pricing: update input/output from API, preserve cache_read\n * (API doesn't return cache_read; we keep the seed value or last known).\n * - If new: insert with API pricing + estimated cache_read = input * 0.186, pricing_known=0.\n *\n * After syncing, backfill any daily_usage rows that were accumulated with $0 cost\n * (pricing_known=0) for models that now have pricing.\n */\nexport function syncPricing(\n db: Database,\n models: ModelEntry[],\n): {\n updated: number;\n inserted: number;\n backfilled: number;\n} {\n const now = Date.now();\n let updated = 0;\n let inserted = 0;\n\n const upsertStmt = db.prepare(\n `INSERT INTO model_pricing\n (model_id, input_per_mtoken, output_per_mtoken, cache_read_per_mtoken, pricing_known, updated_at)\n VALUES ($model_id, $input, $output, $cache_read, $pricing_known, $now)\n ON CONFLICT(model_id) DO UPDATE SET\n input_per_mtoken = $input,\n output_per_mtoken = $output,\n cache_read_per_mtoken = CASE\n WHEN model_pricing.pricing_known = 0 AND $pricing_known = 1 THEN $cache_read\n WHEN model_pricing.cache_read_per_mtoken = 0 THEN $cache_read\n ELSE model_pricing.cache_read_per_mtoken\n END,\n pricing_known = MAX(model_pricing.pricing_known, $pricing_known),\n updated_at = $now`,\n );\n\n db.transaction(() => {\n for (const model of models) {\n if (!model.pricing) continue; // Skip models without pricing data.\n\n const existing = getPricing(db, model.id);\n const inputPerMtoken = model.pricing.input;\n const outputPerMtoken = model.pricing.output;\n\n // Estimate cache_read if not already known.\n const estimatedCacheRead = Math.round(inputPerMtoken * CACHE_READ_RATIO * 1000) / 1000;\n\n if (existing) {\n // Update input/output from API; preserve cache_read if already set.\n const cacheRead =\n existing.cache_read_per_mtoken > 0 ? existing.cache_read_per_mtoken : estimatedCacheRead;\n upsertStmt.run({\n $model_id: model.id,\n $input: inputPerMtoken,\n $output: outputPerMtoken,\n $cache_read: cacheRead,\n $pricing_known: 1,\n $now: now,\n });\n updated++;\n } else {\n // New model — insert with estimated cache_read.\n upsertStmt.run({\n $model_id: model.id,\n $input: inputPerMtoken,\n $output: outputPerMtoken,\n $cache_read: estimatedCacheRead,\n $pricing_known: 1,\n $now: now,\n });\n inserted++;\n }\n }\n })();\n\n // Backfill daily_usage rows that were accumulated with pricing_known=0\n // for models that now have pricing. This is a one-time correction per model.\n const backfilled = backfillUnpricedDailyUsage(db);\n\n if (updated + inserted > 0) {\n log.info(\n `pricing sync: ${updated} updated, ${inserted} inserted, ${backfilled} daily_usage rows backfilled`,\n );\n }\n\n return { updated, inserted, backfilled };\n}\n\n/**\n * Backfill daily_usage rows that were accumulated with pricing_known=0\n * (unknown models with $0 cost) for models that now have pricing.\n *\n * Recomputes costs for those rows using current model_pricing.\n * Only affects rows where pricing_known=0 and the model now has pricing.\n */\nfunction backfillUnpricedDailyUsage(db: Database): number {\n // Find daily_usage rows with pricing_known=0 that have a matching model_pricing entry.\n const rows = db\n .prepare(\n `SELECT du.date, du.model, du.requests, du.input_tokens, du.output_tokens,\n du.cache_read_tokens, du.cache_creation_tokens, du.thinking_tokens,\n mp.input_per_mtoken, mp.output_per_mtoken, mp.cache_read_per_mtoken\n FROM daily_usage du\n JOIN model_pricing mp ON du.model = mp.model_id\n WHERE du.pricing_known = 0`,\n )\n .all() as Array<{\n date: string;\n model: string;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n thinking_tokens: number;\n input_per_mtoken: number;\n output_per_mtoken: number;\n cache_read_per_mtoken: number;\n }>;\n\n if (rows.length === 0) return 0;\n\n const updateStmt = db.prepare(\n `UPDATE daily_usage SET\n cost_input = $cost_input,\n cost_output = $cost_output,\n cost_cache_read = $cost_cache_read,\n cost_cache_creation = $cost_cache_creation,\n cost_total = $cost_total,\n pricing_known = 1\n WHERE date = $date AND model = $model`,\n );\n\n db.transaction(() => {\n for (const row of rows) {\n const costInput = (row.input_tokens / 1_000_000) * row.input_per_mtoken;\n const costOutput = (row.output_tokens / 1_000_000) * row.output_per_mtoken;\n const costCacheRead = (row.cache_read_tokens / 1_000_000) * row.cache_read_per_mtoken;\n const costCacheCreation = (row.cache_creation_tokens / 1_000_000) * row.input_per_mtoken;\n const costTotal = costInput + costOutput + costCacheRead + costCacheCreation;\n\n updateStmt.run({\n $date: row.date,\n $model: row.model,\n $cost_input: costInput,\n $cost_output: costOutput,\n $cost_cache_read: costCacheRead,\n $cost_cache_creation: costCacheCreation,\n $cost_total: costTotal,\n });\n }\n })();\n\n return rows.length;\n}\n\n// ---------------------------------------------------------------------------\n// Query functions for the dashboard API\n// ---------------------------------------------------------------------------\n\nexport interface DailyUsageRow {\n date: string;\n model: string;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n thinking_tokens: number;\n cost_input: number;\n cost_output: number;\n cost_cache_read: number;\n cost_cache_creation: number;\n cost_total: number;\n pricing_known: number;\n}\n\nexport interface MonthSummary {\n year: number;\n month: number;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n thinking_tokens: number;\n cost_input: number;\n cost_output: number;\n cost_cache_read: number;\n cost_cache_creation: number;\n cost_total: number;\n has_unpriced: boolean;\n per_model: Array<{\n model: string;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n cost_total: number;\n }>;\n}\n\nexport interface ModelPricingRow {\n model_id: string;\n input_per_mtoken: number;\n output_per_mtoken: number;\n cache_read_per_mtoken: number;\n pricing_known: number;\n updated_at: number;\n}\n\n/** Get daily usage rows (most recent first, limited). */\nexport function getDailyUsage(db: Database, limit = 90): DailyUsageRow[] {\n const rows = db\n .prepare(\n `SELECT date, model, requests, input_tokens, output_tokens,\n cache_read_tokens, cache_creation_tokens, thinking_tokens,\n cost_input, cost_output, cost_cache_read, cost_cache_creation,\n cost_total, pricing_known\n FROM daily_usage\n ORDER BY date DESC, model ASC\n LIMIT $limit`,\n )\n .all({ $limit: limit }) as DailyUsageRow[];\n return rows;\n}\n\n/** Get month summary (totals + per-model breakdown). */\nexport function getMonthSummary(db: Database, year: number, month: number): MonthSummary {\n const monthPrefix = `${year}-${String(month).padStart(2, \"0\")}%`;\n const totals = db\n .prepare(\n `SELECT\n COUNT(*) as request_count,\n SUM(requests) as requests,\n SUM(input_tokens) as input_tokens,\n SUM(output_tokens) as output_tokens,\n SUM(cache_read_tokens) as cache_read_tokens,\n SUM(cache_creation_tokens) as cache_creation_tokens,\n SUM(thinking_tokens) as thinking_tokens,\n SUM(cost_input) as cost_input,\n SUM(cost_output) as cost_output,\n SUM(cost_cache_read) as cost_cache_read,\n SUM(cost_cache_creation) as cost_cache_creation,\n SUM(cost_total) as cost_total,\n MAX(CASE WHEN pricing_known = 0 THEN 1 ELSE 0 END) as has_unpriced\n FROM daily_usage\n WHERE date LIKE $prefix`,\n )\n .get({ $prefix: monthPrefix }) as {\n request_count: number;\n requests: number | null;\n input_tokens: number | null;\n output_tokens: number | null;\n cache_read_tokens: number | null;\n cache_creation_tokens: number | null;\n thinking_tokens: number | null;\n cost_input: number | null;\n cost_output: number | null;\n cost_cache_read: number | null;\n cost_cache_creation: number | null;\n cost_total: number | null;\n has_unpriced: number | null;\n };\n\n const perModel = db\n .prepare(\n `SELECT model,\n SUM(requests) as requests,\n SUM(input_tokens) as input_tokens,\n SUM(output_tokens) as output_tokens,\n SUM(cache_read_tokens) as cache_read_tokens,\n SUM(cache_creation_tokens) as cache_creation_tokens,\n SUM(cost_total) as cost_total\n FROM daily_usage\n WHERE date LIKE $prefix\n GROUP BY model\n ORDER BY cost_total DESC`,\n )\n .all({ $prefix: monthPrefix }) as Array<{\n model: string;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n cost_total: number;\n }>;\n\n return {\n year,\n month,\n requests: totals.requests ?? 0,\n input_tokens: totals.input_tokens ?? 0,\n output_tokens: totals.output_tokens ?? 0,\n cache_read_tokens: totals.cache_read_tokens ?? 0,\n cache_creation_tokens: totals.cache_creation_tokens ?? 0,\n thinking_tokens: totals.thinking_tokens ?? 0,\n cost_input: totals.cost_input ?? 0,\n cost_output: totals.cost_output ?? 0,\n cost_cache_read: totals.cost_cache_read ?? 0,\n cost_cache_creation: totals.cost_cache_creation ?? 0,\n cost_total: totals.cost_total ?? 0,\n has_unpriced: totals.has_unpriced === 1,\n per_model: perModel,\n };\n}\n\n/** Get available months (year-month pairs that have data). */\nexport function getAvailableMonths(db: Database): Array<{ year: number; month: number }> {\n const rows = db\n .prepare(\"SELECT DISTINCT substr(date, 1, 7) as ym FROM daily_usage ORDER BY ym DESC\")\n .all() as Array<{ ym: string }>;\n return rows.map((r) => ({\n year: Number(r.ym.slice(0, 4)),\n month: Number(r.ym.slice(5, 7)),\n }));\n}\n\n/** Get current pricing table. */\nexport function getPricingTable(db: Database): ModelPricingRow[] {\n return db\n .prepare(\n `SELECT model_id, input_per_mtoken, output_per_mtoken, cache_read_per_mtoken,\n pricing_known, updated_at\n FROM model_pricing\n ORDER BY model_id ASC`,\n )\n .all() as ModelPricingRow[];\n}\n","// SSE parsing helpers for Anthropic and OpenAI streaming responses.\n\nimport type { AnthropicSseEvent, OpenAIStreamChunk, TimedChunk } from \"./types.js\";\n\n/**\n * Parse an Anthropic SSE response from timed chunks into events with\n * timing markers. Each event gets the timestamp of the chunk it was found\n * in, fixing the old 1:1 round-robin assignment that broke when a single\n * HTTP chunk contained multiple SSE events.\n */\nexport function parseAnthropicSse(chunks: TimedChunk[]): AnthropicSseEvent[] {\n const events: AnthropicSseEvent[] = [];\n\n const parseEventBlock = (block: string, evType: string): AnthropicSseEvent | null => {\n const lines = block.split(\"\\n\");\n let type = evType;\n let dataLine: string | null = null;\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n type = line.slice(7).trim();\n } else if (line.startsWith(\"data: \")) {\n dataLine = line.slice(6);\n }\n }\n if (dataLine == null) return null;\n if (dataLine === \"[DONE]\") return null;\n try {\n const parsed = JSON.parse(dataLine) as Record<string, unknown>;\n return {\n type: (parsed.type as string) ?? type,\n ...parsed,\n };\n } catch {\n return null;\n }\n };\n\n let buf = \"\";\n let evType = \"\";\n for (const { text, time } of chunks) {\n buf += text;\n // Process complete SSE event blocks (delimited by \\n\\n).\n let idx = buf.indexOf(\"\\n\\n\");\n while (idx !== -1) {\n const block = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n // Extract event type from the block if present.\n for (const line of block.split(\"\\n\")) {\n if (line.startsWith(\"event: \")) evType = line.slice(7).trim();\n }\n const ev = parseEventBlock(block, evType);\n if (ev) {\n ev.received_at = time;\n events.push(ev);\n }\n idx = buf.indexOf(\"\\n\\n\");\n }\n }\n // Parse any remaining incomplete block in the buffer.\n if (buf.trim()) {\n const ev = parseEventBlock(buf, evType);\n if (ev) {\n const lastTime = chunks[chunks.length - 1]?.time;\n if (lastTime != null) ev.received_at = lastTime;\n events.push(ev);\n }\n }\n return events;\n}\n\n/**\n * Parse an OpenAI SSE response from timed chunks into chunks with timing\n * markers. Each chunk is parsed individually and its timestamp is assigned\n * to all events found within it.\n */\nexport function parseOpenAiSse(chunks: TimedChunk[]): OpenAIStreamChunk[] {\n const result: OpenAIStreamChunk[] = [];\n\n const parseDataLine = (data: string): OpenAIStreamChunk | null => {\n if (data === \"[DONE]\") return null;\n try {\n return JSON.parse(data) as OpenAIStreamChunk;\n } catch {\n return null;\n }\n };\n\n let buf = \"\";\n for (const { text, time } of chunks) {\n buf += text;\n let idx = buf.indexOf(\"\\n\\n\");\n while (idx !== -1) {\n const block = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n for (const line of block.split(\"\\n\")) {\n if (line.startsWith(\"data: \")) {\n const ch = parseDataLine(line.slice(6));\n if (ch) {\n ch.received_at = time;\n result.push(ch);\n }\n }\n }\n idx = buf.indexOf(\"\\n\\n\");\n }\n }\n if (buf.trim()) {\n for (const line of buf.split(\"\\n\")) {\n if (line.startsWith(\"data: \")) {\n const ch = parseDataLine(line.slice(6));\n if (ch) {\n const lastTime = chunks[chunks.length - 1]?.time;\n if (lastTime != null) ch.received_at = lastTime;\n result.push(ch);\n }\n }\n }\n }\n return result;\n}\n","/**\n * Extract a model name from a parsed request body.\n * Returns the string value of `body.model` when the body is a non-null object\n * and `model` is a string; otherwise returns `undefined`.\n */\nexport function extractModelName(body: unknown): string | undefined {\n if (body === null || typeof body !== \"object\") return undefined;\n const model = (body as { model?: unknown }).model;\n if (typeof model !== \"string\") return undefined;\n return model;\n}\n","// Internal numeric helpers for usage extraction.\n// Not re-exported by the barrel — internal to the usage module.\n\nimport type { UsageMetrics } from \"./types.js\";\n\nexport function num(v: unknown): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : 0;\n}\n\nexport function numOr(v: unknown, fallback: number): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\n\nexport function computeTps(\n output: number | null,\n durationMs: number | null,\n ttftMs: number | null,\n): number | null {\n if (output == null || output <= 0) return null;\n if (durationMs == null) return null;\n const genMs = ttftMs != null && ttftMs > 0 ? durationMs - ttftMs : durationMs;\n // Require at least 1 second of generation time — below this the rate is\n // dominated by timing noise and produces nonsensical values (e.g. 44k t/s).\n if (genMs < 1000) return null;\n return (output / genMs) * 1000;\n}\n\nexport function emptyMetrics(\n provider: \"anthropic\" | \"openai\",\n streaming: boolean,\n durationMs: number | null,\n): UsageMetrics {\n return {\n provider,\n streaming,\n input_tokens: null,\n output_tokens: null,\n cache_creation_tokens: null,\n cache_read_tokens: null,\n total_input_tokens: null,\n total_output_tokens: null,\n thinking_tokens: null,\n ttft_ms: null,\n duration_ms: durationMs,\n tps: null,\n usage_missing: true,\n };\n}\n","// Extractors for LLM token usage metrics from Anthropic and OpenAI responses.\n//\n// Attribution rules are derived from official API docs:\n// Anthropic: https://platform.claude.com/docs/en/build-with-claude/streaming\n// OpenAI: https://developers.openai.com/api/docs/api-reference/chat\n\nimport { extractModelName } from \"../models/name.js\";\nimport { computeTps, emptyMetrics, num, numOr } from \"./helpers.js\";\nimport { parseAnthropicSse, parseOpenAiSse } from \"./sse-parse.js\";\nimport type {\n AnthropicSseEvent,\n AnthropicUsage,\n OpenAIStreamChunk,\n OpenAIUsage,\n TimedChunk,\n UsageMetrics,\n} from \"./types.js\";\n\ntype ResponseLike = {\n responseBody: string;\n chunks: TimedChunk[];\n durationMs: number;\n requestStartedAt: number;\n};\n\ntype UsageExtractResult = UsageMetrics;\n\ntype Extractor = (res: ResponseLike) => UsageExtractResult;\n\ntype ProviderExtractors = { stream: Extractor; batch: Extractor };\n\nconst EXTRACTORS: Record<string, ProviderExtractors> = {\n anthropic: {\n stream: (res) =>\n extractAnthropicStreaming(\n parseAnthropicSse(res.chunks),\n res.requestStartedAt,\n res.durationMs,\n ),\n batch: (res) => extractAnthropicNonStreaming(JSON.parse(res.responseBody), res.durationMs),\n },\n openai: {\n stream: (res) =>\n extractOpenAiStreaming(parseOpenAiSse(res.chunks), res.requestStartedAt, res.durationMs),\n batch: (res) => extractOpenAiNonStreaming(JSON.parse(res.responseBody), res.durationMs),\n },\n};\n\n/**\n * Extract Anthropic usage from a NON-STREAMING response body.\n *\n * Rules (per https://platform.claude.com/docs/en/build-with-claude/working-with-messages):\n * - usage lives at top-level `usage`\n * - cache_creation_input_tokens / cache_read_input_tokens are null when no caching, 0 when caching enabled but nothing cached\n * - total input = input_tokens + cache_creation_input_tokens + cache_read_input_tokens\n */\nexport function extractAnthropicNonStreaming(\n body: unknown,\n durationMs: number | null,\n): UsageMetrics {\n const b = body as { usage?: AnthropicUsage };\n const u = b?.usage;\n if (!u) {\n return emptyMetrics(\"anthropic\", false, durationMs);\n }\n const input = num(u.input_tokens);\n const output = num(u.output_tokens);\n const cacheCreate = numOr(u.cache_creation_input_tokens, 0);\n const cacheRead = numOr(u.cache_read_input_tokens, 0);\n const totalInput = input + cacheCreate + cacheRead;\n const thinking = u.output_tokens_details?.thinking_tokens ?? null;\n return {\n provider: \"anthropic\",\n streaming: false,\n input_tokens: input,\n output_tokens: output,\n cache_creation_tokens: cacheCreate,\n cache_read_tokens: cacheRead,\n total_input_tokens: totalInput,\n total_output_tokens: output,\n thinking_tokens: thinking,\n ttft_ms: null, // TTFT not derivable from non-streaming response\n duration_ms: durationMs,\n tps: computeTps(output, durationMs, null),\n usage_missing: false,\n };\n}\n\n/**\n * Extract Anthropic usage from a STREAMING response (array of SSE events).\n *\n * Rules (per https://platform.claude.com/docs/en/build-with-claude/streaming):\n * - message_start.message.usage seeds input_tokens + cache_* (null → 0)\n * - message_delta.usage.output_tokens is CUMULATIVE — always overwrite\n * - message_delta.usage.cache_* / input_tokens only overwrite if != null\n * - ping events carry no usage\n * - TTFT = delta from request start to first content_block_delta event (any delta type)\n */\nexport function extractAnthropicStreaming(\n events: AnthropicSseEvent[],\n requestStartedAt: number,\n wallClockDurationMs?: number,\n): UsageMetrics {\n let input: number | null = null;\n let output: number | null = null;\n let cacheCreate = 0;\n let cacheRead = 0;\n let thinking: number | null = null;\n let ttftMs: number | null = null;\n let lastEventAt: number | null = null;\n let sawUsage = false;\n\n for (const ev of events) {\n if (ev.received_at) lastEventAt = ev.received_at;\n\n if (ev.type === \"message_start\") {\n const u = ev.message?.usage;\n if (u) {\n input = num(u.input_tokens);\n cacheCreate = numOr(u.cache_creation_input_tokens, 0);\n cacheRead = numOr(u.cache_read_input_tokens, 0);\n // output_tokens here is ~1 (placeholder) — do NOT use as final\n sawUsage = true;\n }\n } else if (ev.type === \"message_delta\") {\n const u = ev.usage;\n if (u) {\n // output_tokens is cumulative — ALWAYS overwrite\n if (u.output_tokens != null) {\n output = num(u.output_tokens);\n sawUsage = true;\n }\n // cache / input fields only overwrite if present and non-null\n if (u.input_tokens != null) input = num(u.input_tokens);\n if (u.cache_creation_input_tokens != null) cacheCreate = num(u.cache_creation_input_tokens);\n if (u.cache_read_input_tokens != null) cacheRead = num(u.cache_read_input_tokens);\n if (u.output_tokens_details?.thinking_tokens != null) {\n thinking = u.output_tokens_details.thinking_tokens;\n }\n }\n } else if (ev.type === \"content_block_delta\" && ttftMs === null && ev.received_at) {\n ttftMs = ev.received_at - requestStartedAt;\n }\n }\n\n const eventDurationMs = lastEventAt ? lastEventAt - requestStartedAt : null;\n // Wall-clock floor: proxy flush runs after the last chunk, so wallClockDurationMs\n // is always >= eventBasedDuration. This prevents collapse when all SSE events\n // share one HTTP chunk timestamp.\n const durationMs =\n wallClockDurationMs != null\n ? Math.max(eventDurationMs ?? 0, wallClockDurationMs)\n : eventDurationMs;\n const totalInput = input != null ? input + cacheCreate + cacheRead : null;\n\n return {\n provider: \"anthropic\",\n streaming: true,\n input_tokens: input,\n output_tokens: output,\n cache_creation_tokens: cacheCreate,\n cache_read_tokens: cacheRead,\n total_input_tokens: totalInput,\n total_output_tokens: output,\n thinking_tokens: thinking,\n ttft_ms: ttftMs,\n duration_ms: durationMs,\n tps: computeTps(output, durationMs, ttftMs),\n usage_missing: !sawUsage,\n };\n}\n\n/**\n * Extract OpenAI usage from a NON-STREAMING response body.\n *\n * Rules (per https://developers.openai.com/api/docs/api-reference/chat):\n * - usage always present with prompt_tokens, completion_tokens, total_tokens\n * - prompt_tokens_details.cached_tokens defaults to 0 but may be omitted by compatible providers\n * - completion_tokens_details.reasoning_tokens present for reasoning models\n */\nexport function extractOpenAiNonStreaming(body: unknown, durationMs: number | null): UsageMetrics {\n const b = body as { usage?: OpenAIUsage };\n const u = b?.usage;\n if (!u) {\n return emptyMetrics(\"openai\", false, durationMs);\n }\n const prompt = num(u.prompt_tokens);\n const completion = num(u.completion_tokens);\n const cached = numOr(u.prompt_tokens_details?.cached_tokens, 0);\n const reasoning = u.completion_tokens_details?.reasoning_tokens ?? null;\n return {\n provider: \"openai\",\n streaming: false,\n input_tokens: prompt,\n output_tokens: completion,\n cache_creation_tokens: null, // OpenAI has no \"cache creation\" concept\n cache_read_tokens: cached,\n total_input_tokens: prompt,\n total_output_tokens: completion,\n thinking_tokens: reasoning,\n ttft_ms: null,\n duration_ms: durationMs,\n tps: computeTps(completion, durationMs, null),\n usage_missing: false,\n };\n}\n\n/**\n * Extract OpenAI usage from a STREAMING response (array of chunks).\n *\n * Rules (per https://developers.openai.com/api/docs/api-reference/chat):\n * - Without stream_options.include_usage, every chunk has usage: null → no usage\n * - With include_usage, the FINAL chunk before [DONE] carries the full usage object\n * (per spec choices: [], but compatible providers may include a non-empty choices array)\n * - TTFT = delta from request start to first chunk with a non-empty delta\n * (content, reasoning_content, or tool_calls — whichever arrives first)\n * - If stream is aborted, the usage chunk may never arrive\n */\nexport function extractOpenAiStreaming(\n chunks: OpenAIStreamChunk[],\n requestStartedAt: number,\n wallClockDurationMs?: number,\n): UsageMetrics {\n let usage: OpenAIUsage | null = null;\n let ttftMs: number | null = null;\n let lastChunkAt: number | null = null;\n\n for (const ch of chunks) {\n if (ch.received_at) lastChunkAt = ch.received_at;\n // TTFT = time to first non-empty delta of any kind: content, reasoning, or tool calls.\n // Reasoning models emit reasoning_content first; tool-calling responses may only have tool_calls.\n if (ttftMs === null && ch.received_at && Array.isArray(ch.choices) && ch.choices.length > 0) {\n const d = ch.choices[0]?.delta;\n if (d && (d.content || d.reasoning_content || d.tool_calls)) {\n ttftMs = ch.received_at - requestStartedAt;\n }\n }\n // Usage chunk: usage is non-null. Per OpenAI spec this is the final chunk\n // with choices: [], but compatible providers (e.g. umans-coder) send it\n // with a non-empty choices array containing an empty delta — so we key\n // solely on the presence of the usage object.\n if (ch.usage != null) {\n usage = ch.usage;\n }\n }\n\n const eventDurationMs = lastChunkAt ? lastChunkAt - requestStartedAt : null;\n // Wall-clock floor: proxy flush runs after the last chunk, so wallClockDurationMs\n // is always >= eventBasedDuration. This prevents collapse when all SSE events\n // share one HTTP chunk timestamp.\n const durationMs =\n wallClockDurationMs != null\n ? Math.max(eventDurationMs ?? 0, wallClockDurationMs)\n : eventDurationMs;\n\n if (!usage) {\n const m = emptyMetrics(\"openai\", true, durationMs);\n m.ttft_ms = ttftMs;\n m.tps = computeTps(null, durationMs, ttftMs);\n m.usage_missing = true;\n return m;\n }\n\n const prompt = num(usage.prompt_tokens);\n const completion = num(usage.completion_tokens);\n const cached = numOr(usage.prompt_tokens_details?.cached_tokens, 0);\n const reasoning = usage.completion_tokens_details?.reasoning_tokens ?? null;\n\n return {\n provider: \"openai\",\n streaming: true,\n input_tokens: prompt,\n output_tokens: completion,\n cache_creation_tokens: null,\n cache_read_tokens: cached,\n total_input_tokens: prompt,\n total_output_tokens: completion,\n thinking_tokens: reasoning,\n ttft_ms: ttftMs,\n duration_ms: durationMs,\n tps: computeTps(completion, durationMs, ttftMs),\n usage_missing: false,\n };\n}\n\n/** Extract model name from a request body (both Anthropic and OpenAI shapes). */\nexport function extractModel(requestBody: unknown): string {\n const model = extractModelName(requestBody);\n return model && model.length > 0 ? model : \"unknown\";\n}\n\n/**\n * Unified usage extraction entry point. Dispatches to the correct extractor\n * based on provider + streaming flags, parses the request body for the model\n * name, and stamps the model onto the returned metrics.\n *\n * @param opts.provider \"anthropic\" | \"openai\"\n * @param opts.streaming whether the response was SSE-streamed\n * @param opts.requestBody raw request body JSON string\n * @param opts.responseBody raw response body (SSE string for streaming, JSON for non-streaming)\n * @param opts.durationMs full request duration in ms (used for non-streaming + fallback)\n * @param opts.requestStartedAt epoch ms when the request started (used for streaming TTFT/duration)\n * @returns `{ model, metrics }` — model is the extracted model name, metrics has model stamped on it\n */\nexport function extractUsage(opts: {\n provider: \"anthropic\" | \"openai\";\n streaming: boolean;\n requestBody: string;\n responseBody: string;\n durationMs: number;\n requestStartedAt: number;\n chunks?: TimedChunk[];\n}): { model: string; metrics: UsageMetrics } {\n const parsedBody = JSON.parse(opts.requestBody);\n const model = extractModel(parsedBody);\n const key = opts.streaming ? \"stream\" : \"batch\";\n const extractor = EXTRACTORS[opts.provider]?.[key] ?? EXTRACTORS.openai[key];\n const metrics = extractor({\n responseBody: opts.responseBody,\n chunks: opts.chunks ?? [],\n durationMs: opts.durationMs,\n requestStartedAt: opts.requestStartedAt,\n });\n metrics.model = model;\n return { model, metrics };\n}\n","// SQL DDL: schema + view + per-model ring-buffer retention.\n//\n// These DDL constants define the SQL schema for capturing usage metrics.\n// They are imported by db.ts (for schema migration) and by tests.\n\n/**\n * SQL DDL to add token-usage columns to the captures table.\n * Run after the existing CREATE TABLE (uses ALTER TABLE for migration safety).\n */\nexport const USAGE_COLUMNS_DDL = `\n-- Token usage columns (added to captures table)\nALTER TABLE captures ADD COLUMN provider TEXT; -- 'anthropic' | 'openai'\nALTER TABLE captures ADD COLUMN model TEXT; -- e.g. 'claude-sonnet-4-5', 'gpt-4o'\nALTER TABLE captures ADD COLUMN streaming INTEGER DEFAULT 0;\nALTER TABLE captures ADD COLUMN input_tokens INTEGER;\nALTER TABLE captures ADD COLUMN output_tokens INTEGER;\nALTER TABLE captures ADD COLUMN cache_creation_tokens INTEGER;\nALTER TABLE captures ADD COLUMN cache_read_tokens INTEGER;\nALTER TABLE captures ADD COLUMN total_input_tokens INTEGER;\nALTER TABLE captures ADD COLUMN total_output_tokens INTEGER;\nALTER TABLE captures ADD COLUMN thinking_tokens INTEGER;\nALTER TABLE captures ADD COLUMN ttft_ms REAL;\nALTER TABLE captures ADD COLUMN tps REAL;\nALTER TABLE captures ADD COLUMN usage_missing INTEGER DEFAULT 0;\nALTER TABLE captures ADD COLUMN metrics_extracted_at INTEGER;\n` as const;\n\n/**\n * SQL view: latest N requests per model with all metrics.\n * Replace :N with the desired window size (default 100).\n *\n * Uses ROW_NUMBER() window function (SQLite 3.25+).\n */\nexport const LATEST_N_PER_MODEL_VIEW = `\nCREATE VIEW IF NOT EXISTS v_latest_requests_per_model AS\nWITH ranked AS (\n SELECT\n c.*,\n ROW_NUMBER() OVER (PARTITION BY c.model ORDER BY c.started_at DESC) AS rn\n FROM captures c\n WHERE c.model IS NOT NULL\n AND c.state = 'done'\n)\nSELECT * FROM ranked WHERE rn <= 100;\n` as const;\n\n/**\n * SQL: optimized per-model performance summary for the dashboard.\n *\n * Computes mean TTFT/TPS and nearest-rank percentiles (p10/p50/p95), plus\n * SUM of input/output/cached tokens and cached% in a single scan over the\n * latest 100 done requests per model. IQR/stddev removed — average is shown\n * as the primary metric.\n */\nexport const PERFORMANCE_STATS_SQL = `\nWITH latest_per_model AS (\n SELECT model, provider, streaming,\n ttft_ms, tps,\n total_input_tokens, total_output_tokens,\n cache_read_tokens, thinking_tokens,\n ROW_NUMBER() OVER (PARTITION BY model ORDER BY started_at DESC) AS rn\n FROM captures\n WHERE model IS NOT NULL AND state = 'done' AND usage_missing = 0\n),\nbase AS (\n SELECT model, provider, streaming,\n ttft_ms, tps,\n total_input_tokens, total_output_tokens,\n cache_read_tokens, thinking_tokens\n FROM latest_per_model\n WHERE rn <= 100\n),\nranked AS (\n SELECT\n model, provider, streaming,\n ttft_ms, tps,\n total_input_tokens, total_output_tokens, cache_read_tokens, thinking_tokens,\n ROW_NUMBER() OVER (PARTITION BY model ORDER BY ttft_ms NULLS LAST) AS ttft_rn,\n COUNT(ttft_ms) OVER (PARTITION BY model) AS ttft_cnt,\n ROW_NUMBER() OVER (PARTITION BY model ORDER BY tps NULLS LAST) AS tps_rn,\n COUNT(tps) OVER (PARTITION BY model) AS tps_cnt\n FROM base\n),\npctiles AS (\n SELECT\n model,\n MAX(CASE WHEN ttft_rn = CAST(CEIL(0.10 * ttft_cnt) AS INT) THEN ttft_ms END) AS ttft_p10,\n MAX(CASE WHEN ttft_rn = CAST(CEIL(0.50 * ttft_cnt) AS INT) THEN ttft_ms END) AS ttft_p50,\n MAX(CASE WHEN ttft_rn = CAST(CEIL(0.95 * ttft_cnt) AS INT) THEN ttft_ms END) AS ttft_p95,\n MAX(CASE WHEN tps_rn = CAST(CEIL(0.10 * tps_cnt) AS INT) THEN tps END) AS tps_p10,\n MAX(CASE WHEN tps_rn = CAST(CEIL(0.50 * tps_cnt) AS INT) THEN tps END) AS tps_p50,\n MAX(CASE WHEN tps_rn = CAST(CEIL(0.95 * tps_cnt) AS INT) THEN tps END) AS tps_p95\n FROM ranked\n GROUP BY model\n)\nSELECT\n r.model,\n r.provider,\n COUNT(*) AS request_count,\n SUM(CASE WHEN r.streaming = 1 THEN 1 ELSE 0 END) AS streaming_count,\n SUM(r.total_input_tokens) AS total_input_tokens,\n SUM(r.total_output_tokens) AS total_output_tokens,\n SUM(r.cache_read_tokens) AS total_cache_read_tokens,\n SUM(r.thinking_tokens) AS total_thinking_tokens,\n CASE\n WHEN SUM(r.total_input_tokens) > 0\n THEN CAST(SUM(r.cache_read_tokens) AS REAL) / SUM(r.total_input_tokens) * 100.0\n ELSE 0\n END AS cached_pct,\n AVG(r.ttft_ms) AS ttft_mean,\n MAX(p.ttft_p10) AS ttft_p10,\n MAX(p.ttft_p50) AS ttft_p50,\n MAX(p.ttft_p95) AS ttft_p95,\n AVG(r.tps) AS tps_mean,\n MAX(p.tps_p10) AS tps_p10,\n MAX(p.tps_p50) AS tps_p50,\n MAX(p.tps_p95) AS tps_p95\nFROM ranked r\nLEFT JOIN pctiles p USING (model)\nGROUP BY r.model, r.provider\nORDER BY request_count DESC;\n` as const;\n","// SQLite-backed persistent store for vision descriptions.\n// Extracted from CaptureDB to isolate the vision-description cache lifecycle\n// (upsert / get / delete / evict / warming-list) from capture persistence.\n\nimport type { Database } from \"bun:sqlite\";\n\ntype PreparedStatement = ReturnType<Database[\"prepare\"]>;\n\n/** Parameters for upsertVisionDescription. */\nexport interface VisionDescriptionUpsertParams {\n $key: string;\n $image_hash: string;\n $model: string;\n $prompt_version: number;\n $description: string;\n $now: number;\n}\n\n/**\n * Persistent vision-description store backed by the `vision_descriptions`\n * SQLite table. The table is created/managed by CaptureDB's schema migration;\n * this class only owns the prepared statements and method bodies that operate\n * on that table.\n */\nexport class VisionDescriptionStore {\n private readonly db: Database;\n private readonly stmtInsert: PreparedStatement;\n private readonly stmtGet: PreparedStatement;\n private readonly stmtTouch: PreparedStatement;\n private readonly stmtDelete: PreparedStatement;\n private readonly stmtEvict: PreparedStatement;\n private readonly stmtCount: PreparedStatement;\n private readonly stmtListForWarming: PreparedStatement;\n\n constructor(db: Database) {\n this.db = db;\n this.stmtInsert = db.prepare(\n `INSERT INTO vision_descriptions (key, image_hash, model, prompt_version, description, created_at, last_accessed_at)\n VALUES ($key, $image_hash, $model, $prompt_version, $description, $now, $now)\n ON CONFLICT(key) DO UPDATE SET\n description = excluded.description,\n created_at = excluded.created_at,\n last_accessed_at = excluded.last_accessed_at`,\n );\n this.stmtGet = db.prepare(\n \"SELECT description, created_at FROM vision_descriptions WHERE key = $key\",\n );\n this.stmtTouch = db.prepare(\n \"UPDATE vision_descriptions SET last_accessed_at = $now WHERE key = $key\",\n );\n this.stmtDelete = db.prepare(\"DELETE FROM vision_descriptions WHERE key = $key\");\n this.stmtEvict = db.prepare(\n `DELETE FROM vision_descriptions\n WHERE key IN (\n SELECT key FROM vision_descriptions\n WHERE created_at < $cutoff\n ORDER BY last_accessed_at ASC\n LIMIT $n\n )`,\n );\n this.stmtCount = db.prepare(\"SELECT COUNT(*) AS c FROM vision_descriptions\");\n this.stmtListForWarming = db.prepare(\n `SELECT key, description FROM vision_descriptions\n WHERE created_at > $cutoff\n ORDER BY last_accessed_at DESC\n LIMIT $n`,\n );\n }\n\n /** Insert or update a vision description in the persistent store. */\n upsert(params: VisionDescriptionUpsertParams): void {\n this.stmtInsert.run(params as unknown as never);\n }\n\n /** Look up a vision description by key. Returns null if not found.\n * Updates last_accessed_at on hit. */\n get(key: string): { description: string; created_at: number } | null {\n const row = this.stmtGet.get({ $key: key }) as\n | { description: string; created_at: number }\n | undefined;\n if (!row) return null;\n this.stmtTouch.run({ $key: key, $now: Date.now() });\n return row;\n }\n\n delete(key: string): void {\n this.stmtDelete.run({ $key: key });\n }\n\n /** Evict expired entries (created_at < cutoff) and enforce a max row ceiling.\n * Returns the number of rows deleted. */\n evict(cutoff: number, maxRows: number): number {\n let deleted = 0;\n this.db.transaction(() => {\n const expiredRes = this.stmtEvict.run({ $cutoff: cutoff, $n: 1000 }) as {\n changes: number;\n };\n deleted += expiredRes.changes;\n const count = (this.stmtCount.get() as { c: number }).c;\n if (count > maxRows) {\n const excess = count - maxRows;\n const overflowRes = this.db\n .prepare(\n \"DELETE FROM vision_descriptions WHERE key IN (SELECT key FROM vision_descriptions ORDER BY last_accessed_at ASC LIMIT ?)\",\n )\n .run(excess) as { changes: number };\n deleted += overflowRes.changes;\n }\n })();\n return deleted;\n }\n\n /** List recent non-expired vision descriptions for cache warming.\n * Returns up to `limit` entries. */\n listForWarming(limit: number, cutoff: number): Array<{ key: string; description: string }> {\n return this.stmtListForWarming.all({ $n: limit, $cutoff: cutoff }) as Array<{\n key: string;\n description: string;\n }>;\n }\n}\n","// HTTP header utilities.\n\n/** Hop-by-hop headers that must not be forwarded by a proxy. */\nexport const HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"te\",\n \"trailers\",\n \"transfer-encoding\",\n \"upgrade\",\n \"content-length\",\n \"host\",\n]);\n\n/** Convert a Headers object to a plain object (lowercase keys, comma-joined dups). */\nexport function headersToObject(h: Headers): Record<string, string> {\n const obj: Record<string, string> = {};\n h.forEach((v, k) => {\n const lk = k.toLowerCase();\n obj[lk] = obj[lk] === undefined ? v : `${obj[lk]}, ${v}`;\n });\n return obj;\n}\n\n/** Header names whose values must never be persisted to storage/dashboard. */\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\n\n/** Return a shallow copy of `headers` with sensitive values replaced by \"[REDACTED]\". */\nexport function redactHeaders(headers: Record<string, string>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) {\n out[k] = REDACTED_HEADERS.has(k.toLowerCase()) ? \"[REDACTED]\" : v;\n }\n return out;\n}\n","// Text encoder/decoder utilities.\n\nconst textDecoder = new TextDecoder(\"utf-8\", { fatal: true });\nconst textEncoder = new TextEncoder();\n\n/** Decode bytes to text, falling back to base64 prefix for invalid UTF-8. */\nexport function decodeText(buf: Uint8Array): string {\n try {\n return textDecoder.decode(buf);\n } catch {\n return `__B64__${Buffer.from(buf).toString(\"base64\")}`;\n }\n}\n\nexport { textDecoder, textEncoder };\n","// Capture summary builders.\n\nimport type {\n CaptureRow,\n CaptureState,\n CaptureSummary,\n ProtocolConfig,\n RequestMeta,\n ResponseMeta,\n} from \"../types.js\";\n\n/** Build a summary from a full capture row (no body data). */\nexport function summary(row: CaptureRow): CaptureSummary {\n return {\n id: row.id,\n method: row.method,\n path: row.path,\n response_status: row.response_status,\n is_sse: !!row.is_sse,\n content_type: row.content_type,\n request_size: row.request_size,\n response_size: row.response_size,\n duration_ms: row.duration_ms,\n state: row.state,\n started_at: row.started_at,\n finished_at: row.finished_at,\n incoming_protocol: row.incoming_protocol ?? \"\",\n upstream_protocol: row.upstream_protocol ?? \"\",\n model: row.model,\n usage_missing: row.usage_missing == null ? null : !!row.usage_missing,\n ttft_ms: row.ttft_ms ?? null,\n tps: row.tps ?? null,\n cache_creation_tokens: row.cache_creation_tokens ?? null,\n cache_read_tokens: row.cache_read_tokens ?? null,\n total_input_tokens: row.total_input_tokens ?? null,\n output_tokens: row.output_tokens ?? null,\n total_output_tokens: row.total_output_tokens ?? null,\n is_vision: !!row.is_vision,\n status_source: (row.status_source as \"upstream\" | \"gate\" | null) ?? null,\n gate_reason: row.gate_reason ?? null,\n };\n}\n\n/** Build a summary for a newly-started capture (before response). */\nexport function newSummary(\n id: number,\n reqMeta: RequestMeta,\n config: ProtocolConfig,\n state: CaptureState = \"streaming\",\n model: string | null = null,\n): CaptureSummary {\n return {\n id,\n method: reqMeta.method,\n path: reqMeta.path,\n response_status: null,\n is_sse: false,\n content_type: null,\n request_size: reqMeta.request_size,\n response_size: 0,\n duration_ms: 0,\n state,\n started_at: reqMeta.started_at,\n finished_at: null,\n incoming_protocol: config.incomingProtocol,\n upstream_protocol: config.upstreamProtocol,\n model,\n usage_missing: null,\n ttft_ms: null,\n tps: null,\n cache_creation_tokens: null,\n cache_read_tokens: null,\n total_input_tokens: null,\n output_tokens: null,\n total_output_tokens: null,\n is_vision: false,\n status_source: null,\n gate_reason: null,\n };\n}\n\n/** Build a summary from queued response metadata. */\nexport function buildSummary(\n id: number,\n reqMeta: RequestMeta,\n res: ResponseMeta,\n config: ProtocolConfig,\n): CaptureSummary {\n const u = res.$usage ?? null;\n return {\n id,\n method: reqMeta.method,\n path: reqMeta.path,\n response_status: res.$status,\n is_sse: !!res.$sse,\n content_type: res.$ct,\n request_size: reqMeta.request_size,\n response_size: res.$rs,\n duration_ms: res.$dur,\n state: \"done\",\n started_at: reqMeta.started_at,\n finished_at: res.$fin,\n incoming_protocol: config.incomingProtocol,\n upstream_protocol: config.upstreamProtocol,\n model: res.$model ?? null,\n usage_missing: u?.usage_missing ?? null,\n ttft_ms: u?.ttft_ms ?? null,\n tps: u?.tps ?? null,\n cache_creation_tokens: u?.cache_creation_tokens ?? null,\n cache_read_tokens: u?.cache_read_tokens ?? null,\n total_input_tokens: u?.total_input_tokens ?? null,\n output_tokens: u?.output_tokens ?? null,\n total_output_tokens: u?.total_output_tokens ?? null,\n is_vision: false,\n status_source: res.$status_source,\n gate_reason: res.$gate_reason,\n };\n}\n","// 429 response classification.\n\n/** Classify a 429 response as concurrency, rate-limit, or gateway-cdn. */\nexport function classify429(res: Response): \"concurrency\" | \"rate_limit\" | \"gateway\" {\n const server = res.headers.get(\"server\") ?? \"\";\n if (server.includes(\"cloudflare\") || server.includes(\"fastly\")) return \"gateway\";\n const ra = res.headers.get(\"retry-after\");\n if (ra && Number(ra) <= 10) return \"concurrency\";\n return \"rate_limit\";\n}\n","// Concurrency weight computation.\n\n/** Source of model weights derived from upstream pricing. */\nexport interface WeightModelSource {\n getWeight(modelId: string): number;\n}\n\n/** Compute the concurrency weight for a request.\n *\n * Weight is derived from model pricing via the provided WeightModelSource.\n * Returns 1 if the model is unknown or no source is available.\n */\nexport function computeRequestWeight(\n modelName: string | undefined,\n models: WeightModelSource | null,\n): number {\n if (typeof modelName !== \"string\" || modelName === \"\") return 1;\n if (models) return models.getWeight(modelName);\n return 1;\n}\n","import type { BreakerState, GateConfig, ProxyConfig } from \"../types.js\";\n\nexport type { BreakerState };\n\n/** Raw config keys that should trigger a gate reconfigure on reload. */\nexport const GATE_RECONFIG_FIELDS = new Set<keyof ProxyConfig>([\n \"breakerThreshold\",\n \"breakerWindowMs\",\n \"breakerCooldownMs\",\n \"queueTimeoutMs\",\n \"maxQueueDepth\",\n \"releaseCooldownMs\",\n \"concurrencyMainReservation\",\n \"concurrencyVisionReservation\",\n]);\n\nexport const SCALE = 1000;\n\nexport interface Waiter {\n resolve: (permit: Permit) => void;\n reject: (e: GateError) => void;\n enqueuedAt: number;\n weight: number;\n intention: string;\n signal?: AbortSignal;\n onAcquire?: () => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\nexport interface Permit {\n release: () => void;\n}\n\nexport interface ConcurrencyGateOptions {\n hardCap: number;\n softLimit: number;\n releaseCooldownMs: number;\n breakerThreshold: number;\n breakerWindowMs: number;\n breakerCooldownMs: number;\n maxQueueDepth: number;\n queueTimeoutMs: number;\n intentions?: Record<string, number>;\n}\n\nexport class GateError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = \"GateError\";\n this.code = code;\n }\n}\n\nexport function gateOptionsFromConfig(config: GateConfig): ConcurrencyGateOptions {\n return {\n hardCap: config.concurrencyHardCap,\n softLimit: config.concurrencySoftLimit,\n releaseCooldownMs: config.releaseCooldownMs,\n breakerThreshold: config.breakerThreshold,\n breakerWindowMs: config.breakerWindowMs,\n breakerCooldownMs: config.breakerCooldownMs,\n maxQueueDepth: config.maxQueueDepth,\n queueTimeoutMs: config.queueTimeoutMs,\n intentions: {\n main: config.concurrencyMainReservation,\n vision: config.concurrencyVisionReservation,\n },\n };\n}\n","import type { BreakerState } from \"./types.js\";\n\nexport class CircuitBreaker {\n private state: BreakerState = \"closed\";\n private concurrency429s: number[] = [];\n private openedAt = 0;\n private threshold: number;\n private windowMs: number;\n private cooldownMs: number;\n\n constructor(threshold: number, windowMs: number, cooldownMs: number) {\n this.threshold = threshold;\n this.windowMs = windowMs;\n this.cooldownMs = cooldownMs;\n }\n\n reconfigure(opts: { threshold?: number; windowMs?: number; cooldownMs?: number }): void {\n if (opts.threshold !== undefined) this.threshold = opts.threshold;\n if (opts.windowMs !== undefined) this.windowMs = opts.windowMs;\n if (opts.cooldownMs !== undefined) this.cooldownMs = opts.cooldownMs;\n }\n\n getState(): BreakerState {\n return this.state;\n }\n\n /** If open and cooldown has elapsed, transition to half_open. Returns the\n * current state AFTER any transition. */\n maybeHalfOpen(): BreakerState {\n if (this.state === \"open\") {\n const elapsed = Date.now() - this.openedAt;\n if (elapsed >= this.cooldownMs) {\n this.state = \"half_open\";\n }\n }\n return this.state;\n }\n\n record429(type: \"concurrency\" | \"rate_limit\" | \"gateway\"): void {\n if (type !== \"concurrency\") return;\n const now = Date.now();\n this.concurrency429s.push(now);\n this.concurrency429s = this.concurrency429s.filter((t) => now - t < this.windowMs);\n if (this.concurrency429s.length >= this.threshold) {\n if (this.state === \"closed\" || this.state === \"half_open\") {\n this.state = \"open\";\n this.openedAt = now;\n }\n }\n }\n\n recordSuccess(): void {\n if (this.state === \"half_open\") {\n this.state = \"closed\";\n this.concurrency429s = [];\n }\n }\n}\n","// Concurrency gate — resizable semaphore with circuit breaker + release cooldown.\n// 9th request at hard cap enqueues; waits for a slot or rejects on timeout/full.\n//\n// Internal accounting uses 1000× scaled integers (SCALE) to avoid floating-point\n// drift from fractional reservation ratios and weights. All public getters\n// return decimal values.\n//\n// Internally composed of two cohesive classes:\n// Semaphore — active count, acquire/release, waiter queue, FIFO ordering,\n// reservations, capacity checks, release cooldown.\n// CircuitBreaker — failure counting, state (closed/open/half-open), thresholds.\n// ConcurrencyGate preserves its existing public API and delegates to both.\n\nimport { createLogger } from \"../logger.js\";\nimport type { GateStats, UsageSnapshot } from \"../types.js\";\nimport { CircuitBreaker } from \"./circuit-breaker.js\";\nimport {\n type ConcurrencyGateOptions,\n GateError,\n type Permit,\n SCALE,\n type Waiter,\n} from \"./types.js\";\n\nconst log = createLogger(\"limiter\");\n\n// ---------------------------------------------------------------------------\n// Semaphore — active count, acquire/release, waiter queue, FIFO ordering,\n// reservations, capacity checks, release cooldown.\n// ---------------------------------------------------------------------------\n\nclass Semaphore {\n private active = 0;\n private limit = SCALE;\n private hardCap: number;\n private softLimit: number;\n private releaseCooldownMs: number;\n private maxQueueDepth: number;\n private queueTimeoutMs: number;\n private waiters: Waiter[] = [];\n private reservations: Record<string, number>;\n private activeByIntention: Record<string, number>;\n private queuedByIntention: Record<string, number>;\n private cooldownTimers = new Set<ReturnType<typeof setTimeout>>();\n private pendingReleases: Array<{ weight: number; intention: string }> = [];\n private releaseTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly onEmitStats: () => void;\n\n constructor(opts: ConcurrencyGateOptions, onEmitStats: () => void) {\n this.onEmitStats = onEmitStats;\n this.hardCap = Math.max(1, Math.floor(opts.hardCap)) * SCALE;\n this.softLimit = Math.max(1, Math.floor(opts.softLimit)) * SCALE;\n this.limit = Math.max(SCALE, Math.min(this.softLimit, this.hardCap));\n this.releaseCooldownMs = opts.releaseCooldownMs;\n this.maxQueueDepth = opts.maxQueueDepth;\n this.queueTimeoutMs = opts.queueTimeoutMs;\n const src = opts.intentions ?? { main: 1 };\n this.reservations = {};\n for (const key of Object.keys(src)) {\n this.reservations[key] = Math.round(src[key] * SCALE);\n }\n this.activeByIntention = {};\n this.queuedByIntention = {};\n for (const key of Object.keys(this.reservations)) {\n this.activeByIntention[key] = 0;\n this.queuedByIntention[key] = 0;\n }\n this.assertInvariants();\n }\n\n reconfigure(opts: Partial<ConcurrencyGateOptions>): void {\n if (opts.releaseCooldownMs !== undefined) this.releaseCooldownMs = opts.releaseCooldownMs;\n if (opts.maxQueueDepth !== undefined) this.maxQueueDepth = opts.maxQueueDepth;\n if (opts.queueTimeoutMs !== undefined) this.queueTimeoutMs = opts.queueTimeoutMs;\n if (opts.intentions !== undefined) {\n this.reservations = {};\n for (const key of Object.keys(opts.intentions)) {\n this.reservations[key] = Math.round(opts.intentions[key] * SCALE);\n }\n for (const key of Object.keys(this.reservations)) {\n if (this.activeByIntention[key] === undefined) this.activeByIntention[key] = 0;\n if (this.queuedByIntention[key] === undefined) this.queuedByIntention[key] = 0;\n }\n }\n this.assertInvariants();\n }\n\n getActive(): number {\n return this.active;\n }\n\n getLimit(): number {\n return this.limit;\n }\n\n getHardCap(): number {\n return this.hardCap;\n }\n\n getSoftLimit(): number {\n return this.softLimit;\n }\n\n getWaiterCount(): number {\n return this.waiters.length;\n }\n\n getReservations(): Record<string, number> {\n return this.reservations;\n }\n\n getActiveByIntention(): Record<string, number> {\n return this.activeByIntention;\n }\n\n getQueuedByIntention(): Record<string, number> {\n return this.queuedByIntention;\n }\n\n getIntentionActive(intention: string): number {\n return (this.activeByIntention[intention] ?? 0) / SCALE;\n }\n\n getIntentionQueued(intention: string): number {\n return this.queuedByIntention[intention] ?? 0;\n }\n\n resize(newLimit: number): boolean {\n const scaled = Math.round(newLimit * SCALE);\n const clamped = Math.max(SCALE, Math.min(scaled, this.hardCap));\n if (clamped === this.limit) return false;\n this.limit = clamped;\n this.drainWaiters();\n this.assertInvariants();\n return true;\n }\n\n setHardCap(newHardCap: number): boolean {\n const cap = Math.max(1, Math.floor(newHardCap)) * SCALE;\n if (cap === this.hardCap) return false;\n this.hardCap = cap;\n if (this.softLimit > cap) this.softLimit = cap;\n if (this.limit > cap) {\n this.limit = cap;\n this.drainWaiters();\n }\n if (this.active > cap) {\n const excess = (this.active - cap) / SCALE;\n log.warn(\n `[gate.ts] setHardCap(${newHardCap}) reduced cap below active=${this.active / SCALE}; ${excess} permits remain in-flight until completion (transient over-cap — see setHardCap docs)`,\n );\n }\n this.assertInvariants();\n return true;\n }\n\n setSoftLimit(newSoftLimit: number): boolean {\n const v = Math.max(1, Math.floor(newSoftLimit)) * SCALE;\n if (v === this.softLimit) return false;\n this.softLimit = v;\n this.assertInvariants();\n return true;\n }\n\n /** Attempts to acquire a permit. Returns true if granted immediately,\n * false if the caller should enqueue (or reject if queue is full).\n *\n * When total reservations exceed the limit (over-subscribed), the\n * proportional split in effectiveReservations() may reduce each\n * intention's reservation below a single permit's weight. In that\n * case, fall back to a pure capacity check: any intention may acquire\n * if active + weight ≤ limit. Reservations only gate borrowing when\n * they are not over-subscribed. */\n canGrant(intention: string, weight: number): boolean {\n const effRes = this.effectiveReservations();\n const reserved = effRes[intention] ?? 0;\n if (this.active + weight > this.limit) return false;\n if ((this.activeByIntention[intention] ?? 0) + weight <= reserved) {\n return true;\n }\n // When reservations are over-subscribed (proportional split reduced\n // them below raw values), allow pure capacity-based granting.\n const overSubscribed = Object.keys(this.reservations).some(\n (k) => (this.reservations[k] ?? 0) > (effRes[k] ?? 0),\n );\n if (overSubscribed) {\n return this.active + weight <= this.limit;\n }\n return this.active + weight <= this.capacity(intention, effRes);\n }\n\n grant(\n resolve: (p: Permit) => void,\n onAcquire: (() => void) | undefined,\n weight: number,\n intention: string,\n ): void {\n this.active += weight;\n this.activeByIntention[intention] = (this.activeByIntention[intention] ?? 0) + weight;\n onAcquire?.();\n this.assertInvariants();\n this.onEmitStats();\n let released = false;\n resolve({\n release: () => {\n if (released) return;\n released = true;\n this.releasePermit(weight, intention);\n },\n });\n }\n\n /** Enqueue a waiter. Returns true if enqueued, false if queue is full. */\n enqueue(waiter: Waiter): boolean {\n if (this.waiters.length >= this.maxQueueDepth) return false;\n this.waiters.push(waiter);\n this.queuedByIntention[waiter.intention] = (this.queuedByIntention[waiter.intention] ?? 0) + 1;\n return true;\n }\n\n removeWaiter(w: Waiter): void {\n const i = this.waiters.indexOf(w);\n if (i >= 0) {\n this.waiters.splice(i, 1);\n this.queuedByIntention[w.intention] = Math.max(\n 0,\n (this.queuedByIntention[w.intention] ?? 0) - 1,\n );\n this.assertInvariants();\n this.onEmitStats();\n }\n }\n\n drainWaiters(): void {\n if (this.waiters.length === 0) return;\n const remaining: Waiter[] = [];\n for (const next of this.waiters) {\n if (!this.canGrant(next.intention, next.weight)) {\n remaining.push(next);\n continue;\n }\n this.queuedByIntention[next.intention] = Math.max(\n 0,\n (this.queuedByIntention[next.intention] ?? 0) - 1,\n );\n clearTimeout(next.timeout);\n this.grant(next.resolve, next.onAcquire, next.weight, next.intention);\n }\n this.waiters = remaining;\n this.assertInvariants();\n }\n\n getQueueTimeoutMs(): number {\n return this.queueTimeoutMs;\n }\n\n shutdown(): void {\n for (const t of this.cooldownTimers) clearTimeout(t);\n this.cooldownTimers.clear();\n this.releaseTimer = null;\n this.pendingReleases = [];\n for (const w of this.waiters) {\n clearTimeout(w.timeout);\n this.queuedByIntention[w.intention] = Math.max(\n 0,\n (this.queuedByIntention[w.intention] ?? 0) - 1,\n );\n w.reject(new GateError(\"shutdown\", \"Shutting down\"));\n }\n this.waiters = [];\n this.assertInvariants();\n }\n\n private releasePermit(weight: number, intention: string): void {\n this.pendingReleases.push({ weight, intention });\n if (this.releaseTimer === null) {\n this.releaseTimer = setTimeout(() => {\n this.releaseTimer = null;\n const batch = this.pendingReleases;\n this.pendingReleases = [];\n for (const rel of batch) {\n this.active = Math.max(0, this.active - rel.weight);\n this.activeByIntention[rel.intention] = Math.max(\n 0,\n (this.activeByIntention[rel.intention] ?? 0) - rel.weight,\n );\n }\n this.drainWaiters();\n this.assertInvariants();\n this.onEmitStats();\n }, this.releaseCooldownMs);\n this.cooldownTimers.add(this.releaseTimer);\n }\n }\n\n /** Effective reservations clamped so sum(effRes) <= limit. Each is at least 1\n * when limit allows, but if limit < intention count, some may be 0. */\n private effectiveReservations(): Record<string, number> {\n const keys = Object.keys(this.reservations);\n const sum = keys.reduce((s, k) => s + (this.reservations[k] ?? 0), 0);\n if (sum <= this.limit) {\n const out: Record<string, number> = {};\n for (const k of keys) out[k] = this.reservations[k] ?? 0;\n return out;\n }\n const out: Record<string, number> = {};\n let remaining = this.limit;\n for (const k of keys) {\n const scaled = Math.floor(((this.reservations[k] ?? 0) / sum) * this.limit);\n const v = Math.max(0, Math.min(scaled, remaining));\n out[k] = v;\n remaining -= v;\n }\n return out;\n }\n\n /** Capacity available to intention I: limit minus reserved slots of other\n * intentions that have not yet been filled by active permits. */\n private capacity(intention: string, effRes: Record<string, number>): number {\n let reserved = 0;\n for (const k of Object.keys(effRes)) {\n if (k === intention) continue;\n const active = this.activeByIntention[k] ?? 0;\n const res = effRes[k] ?? 0;\n reserved += Math.max(0, res - active);\n }\n return this.limit - reserved;\n }\n\n private assertInvariants(): void {\n if (!(SCALE <= this.limit && this.limit <= this.hardCap)) {\n log.warn(\n `[gate.ts] invariant violation: SCALE(${SCALE}) <= limit(${this.limit}) <= hardCap(${this.hardCap})`,\n );\n }\n if (!Number.isInteger(this.active)) {\n log.warn(`[gate.ts] invariant warning: active(${this.active}) is not an integer`);\n }\n if (!Number.isInteger(this.limit)) {\n log.warn(`[gate.ts] invariant warning: limit(${this.limit}) is not an integer`);\n }\n if (!Number.isInteger(this.hardCap)) {\n log.warn(`[gate.ts] invariant warning: hardCap(${this.hardCap}) is not an integer`);\n }\n if (this.active > this.hardCap) {\n const excess = (this.active - this.hardCap) / SCALE;\n log.warn(\n `[gate.ts] invariant warning: active(${this.active}) > hardCap(${this.hardCap}); transient over-cap by ${excess} (in-flight permits will drain)`,\n );\n }\n if (this.active < 0) {\n log.warn(`[gate.ts] invariant violation: active(${this.active}) < 0`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// ConcurrencyGate — public API, composes Semaphore + CircuitBreaker.\n// ---------------------------------------------------------------------------\n\nexport class ConcurrencyGate {\n private readonly semaphore: Semaphore;\n private readonly breaker: CircuitBreaker;\n private onStatsCb: (() => void) | null = null;\n\n constructor(opts: ConcurrencyGateOptions) {\n this.breaker = new CircuitBreaker(\n opts.breakerThreshold,\n opts.breakerWindowMs,\n opts.breakerCooldownMs,\n );\n this.semaphore = new Semaphore(opts, () => this.emitStats());\n }\n\n /** Hot-reload reconfigurable parameters. Does NOT affect the soft limit\n * (which is driven by usage.onChange). Updates breaker/queue/cooldown. */\n reconfigure(opts: Partial<ConcurrencyGateOptions>): void {\n this.semaphore.reconfigure(opts);\n this.breaker.reconfigure({\n threshold: opts.breakerThreshold,\n windowMs: opts.breakerWindowMs,\n cooldownMs: opts.breakerCooldownMs,\n });\n this.emitStats();\n }\n\n onStatsChange(cb: () => void): void {\n this.onStatsCb = cb;\n }\n\n getLimit(): number {\n return this.semaphore.getLimit() / SCALE;\n }\n\n getIntentionActive(intention: string): number {\n return this.semaphore.getIntentionActive(intention);\n }\n\n getIntentionQueued(intention: string): number {\n return this.semaphore.getIntentionQueued(intention);\n }\n\n resize(newLimit: number): void {\n if (this.semaphore.resize(newLimit)) {\n this.emitStats();\n }\n }\n\n /** Update the hard cap (e.g. from /v1/usage reconciliation).\n * Clamps the soft limit down if it now exceeds the new hard cap.\n *\n * Policy: `hard_cap` is a hard grant boundary, NOT a hard real-time ceiling.\n * Lowering the cap does NOT evict active permits. Transient over-cap states\n * are expected until in-flight permits complete naturally. */\n setHardCap(newHardCap: number): void {\n if (this.semaphore.setHardCap(newHardCap)) {\n this.emitStats();\n }\n }\n\n /** Update the persisted soft limit (from /v1/usage). Does NOT change the\n * effective limit — that's driven by usage.onChange with priorityLow adjustment. */\n setSoftLimit(newSoftLimit: number): void {\n if (this.semaphore.setSoftLimit(newSoftLimit)) {\n this.emitStats();\n }\n }\n\n acquire(\n opts: {\n weight?: number;\n signal?: AbortSignal;\n onAcquire?: () => void;\n intention?: string;\n } = {},\n ): Promise<Permit> {\n const weight = Math.round((opts.weight ?? 1) * SCALE);\n if (weight <= 0) {\n throw new GateError(\"invalid_weight\", \"weight must be positive\");\n }\n const intention = opts.intention ?? \"main\";\n return new Promise((resolve, reject) => {\n // Circuit breaker check\n if (this.breaker.maybeHalfOpen() === \"open\") {\n reject(new GateError(\"circuit_open\", \"Circuit breaker open\"));\n return;\n }\n\n if (this.semaphore.canGrant(intention, weight)) {\n this.semaphore.grant(resolve, opts.onAcquire, weight, intention);\n return;\n }\n\n // Enqueue\n const queueTimeoutMs = this.semaphore.getQueueTimeoutMs();\n const timeout = setTimeout(() => {\n this.semaphore.removeWaiter(waiter);\n reject(new GateError(\"timeout\", \"Queue timeout exceeded\"));\n }, queueTimeoutMs);\n\n const waiter: Waiter = {\n resolve,\n reject,\n enqueuedAt: Date.now(),\n weight,\n intention,\n signal: opts.signal,\n onAcquire: opts.onAcquire,\n timeout,\n };\n\n if (!this.semaphore.enqueue(waiter)) {\n clearTimeout(timeout);\n reject(new GateError(\"queue_full\", \"Concurrency queue full\"));\n return;\n }\n\n this.emitStats();\n\n opts.signal?.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timeout);\n this.semaphore.removeWaiter(waiter);\n reject(new GateError(\"aborted\", \"Client disconnected while enqueued\"));\n },\n { once: true },\n );\n });\n }\n\n record429(type: \"concurrency\" | \"rate_limit\" | \"gateway\"): void {\n this.breaker.record429(type);\n if (this.breaker.getState() === \"open\") {\n this.emitStats();\n }\n }\n\n recordSuccess(): void {\n const wasHalfOpen = this.breaker.getState() === \"half_open\";\n this.breaker.recordSuccess();\n if (wasHalfOpen) {\n this.emitStats();\n }\n }\n\n getStats(snapshot: UsageSnapshot): GateStats {\n const now = Date.now();\n const boxed = snapshot.boxedUntil !== null && snapshot.boxedUntil > now;\n const activeByIntention: Record<string, number> = {};\n const semActive = this.semaphore.getActiveByIntention();\n for (const k of Object.keys(semActive)) {\n activeByIntention[k] = semActive[k] / SCALE;\n }\n const reservations: Record<string, number> = {};\n const semRes = this.semaphore.getReservations();\n for (const k of Object.keys(semRes)) {\n reservations[k] = semRes[k] / SCALE;\n }\n return {\n active: this.semaphore.getActive() / SCALE,\n queued: this.semaphore.getWaiterCount(),\n softLimit: this.semaphore.getSoftLimit() / SCALE,\n hardCap: this.semaphore.getHardCap() / SCALE,\n tier: snapshot.plan,\n breaker: this.breaker.getState(),\n boxed,\n boxedReason: snapshot.boxedReason,\n boxedUntil: snapshot.boxedUntil,\n priorityLow: snapshot.priorityLow,\n unitsDemoted: snapshot.unitsDemoted,\n demotedUntil: snapshot.demotedUntil,\n requestsRemaining: snapshot.requestsRemaining,\n requestsInWindow: snapshot.requestsInWindow,\n requestsLimit: snapshot.requestsLimit,\n windowSeconds: snapshot.requestsWindowSeconds,\n usageOk: snapshot.ok,\n lastUsageFetch: snapshot.fetchedAt || null,\n activeByIntention,\n queuedByIntention: { ...this.semaphore.getQueuedByIntention() },\n reservations,\n };\n }\n\n shutdown(): void {\n this.semaphore.shutdown();\n }\n\n private emitStats(): void {\n this.onStatsCb?.();\n }\n}\n","// Zero-dependency in-memory metrics registry.\n// Exposes counters and gauges in Prometheus text exposition format.\n// Designed for single-process Bun — no external collector required.\n\nexport type MetricType = \"counter\" | \"gauge\";\n\ninterface MetricEntry {\n type: MetricType;\n help: string;\n value: number;\n labels?: Record<string, string>;\n}\n\n/** In-memory metrics registry with Prometheus text format export. */\nexport class MetricsRegistry {\n private metrics = new Map<string, MetricEntry>();\n\n /** Increment a counter by n (default 1). Creates the metric if missing. */\n inc(name: string, n = 1, help?: string): void {\n const existing = this.metrics.get(name);\n if (existing) {\n existing.value += n;\n } else {\n this.metrics.set(name, {\n type: \"counter\",\n help: help ?? name,\n value: n,\n });\n }\n }\n\n /** Set a gauge to a specific value. Creates the metric if missing. */\n set(name: string, value: number, help?: string): void {\n const existing = this.metrics.get(name);\n if (existing) {\n existing.value = value;\n } else {\n this.metrics.set(name, {\n type: \"gauge\",\n help: help ?? name,\n value,\n });\n }\n }\n\n /** Get the current value of a metric. */\n get(name: string): number | undefined {\n return this.metrics.get(name)?.value;\n }\n\n /** Serialize all metrics to Prometheus text exposition format. */\n format(): string {\n const lines: string[] = [];\n const sorted = [...this.metrics.entries()].sort((a, b) => a[0].localeCompare(b[0]));\n for (const [name, entry] of sorted) {\n lines.push(`# HELP ${name} ${entry.help}`);\n lines.push(`# TYPE ${name} ${entry.type}`);\n lines.push(`${name} ${entry.value}`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n }\n\n /** Reset all counters (not gauges). Used for testing. */\n resetCounters(): void {\n for (const entry of this.metrics.values()) {\n if (entry.type === \"counter\") entry.value = 0;\n }\n }\n}\n\n/** Singleton registry for the process. */\nexport const metrics = new MetricsRegistry();\n","// Shared parser for the /v1/models/info upstream response.\n//\n// Both src/models.ts (rich ModelInfo) and src/vision/catalog.ts (subset\n// ModelInfo) consume the same JSON shape — a top-level object whose keys\n// are model ids and whose values carry capabilities/base_model metadata.\n// This module does the type-guarded extraction once into a normalized\n// ParsedModelInfo; each caller projects to its own interface.\n\n/** Tristate vision support as encoded by the upstream API. */\nexport type VisionSupport = boolean | \"via-handoff\";\n\n/**\n * Faithfully typed fields extracted from a single /v1/models/info entry.\n * Every field is populated; callers pick the subset they need.\n */\nexport interface ParsedModelInfo {\n name: string;\n display_name: string;\n description: string;\n base_model: {\n name: string;\n provider: string | undefined;\n family: string | undefined;\n oss_base: string | undefined;\n };\n capabilities: {\n max_completion_tokens: number;\n recommended_max_tokens: number;\n context_window: number;\n supports_vision: VisionSupport;\n supports_tools: boolean;\n reasoning: {\n supported: boolean;\n can_disable: boolean;\n levels: string[];\n default_level: string | null;\n };\n };\n benchmarks: Record<string, unknown>;\n weights: {\n precision: string | undefined;\n hf_url: string | undefined;\n };\n stage: string | undefined;\n lifecycle: { playground_start_date: string | undefined } | undefined;\n}\n\n/** Untyped shape of a single entry as received from upstream. */\ninterface RawModelInfo {\n name?: unknown;\n display_name?: unknown;\n description?: unknown;\n base_model?: {\n name?: unknown;\n provider?: unknown;\n family?: unknown;\n oss_base?: unknown;\n };\n capabilities?: {\n max_completion_tokens?: unknown;\n recommended_max_tokens?: unknown;\n context_window?: unknown;\n supports_vision?: unknown;\n supports_tools?: unknown;\n reasoning?: {\n supported?: unknown;\n can_disable?: unknown;\n levels?: unknown;\n default_level?: unknown;\n };\n };\n benchmarks?: Record<string, unknown>;\n weights?: {\n precision?: unknown;\n hf_url?: unknown;\n };\n stage?: unknown;\n lifecycle?: { playground_start_date?: unknown };\n}\n\n/**\n * Parse a raw /v1/models/info JSON body into a map keyed by model id.\n *\n * Entries whose value is not an object are skipped (matching prior behavior\n * in both src/models.ts and src/vision/catalog.ts). Every field is\n * type-guarded; missing or wrong-typed fields fall back to defaults\n * identical to the inline code this replaces.\n */\nexport function parseModelInfoResponse(body: unknown): Map<string, ParsedModelInfo> {\n const out = new Map<string, ParsedModelInfo>();\n if (typeof body !== \"object\" || body === null) return out;\n\n for (const [key, rawVal] of Object.entries(body as Record<string, unknown>)) {\n if (typeof rawVal !== \"object\" || rawVal === null) continue;\n const v = rawVal as RawModelInfo;\n const caps = v.capabilities ?? {};\n const bm = v.base_model ?? {};\n const w = v.weights ?? {};\n const sv = caps.supports_vision;\n const supportsVision: VisionSupport =\n sv === true ? true : sv === \"via-handoff\" ? \"via-handoff\" : false;\n const reasoning = caps.reasoning ?? {};\n const levels = Array.isArray(reasoning.levels)\n ? reasoning.levels.filter((l) => typeof l === \"string\")\n : [];\n out.set(key, {\n name: typeof v.name === \"string\" ? v.name : key,\n display_name: typeof v.display_name === \"string\" ? v.display_name : \"\",\n description: typeof v.description === \"string\" ? v.description : \"\",\n base_model: {\n name: typeof bm.name === \"string\" ? bm.name : \"\",\n provider: typeof bm.provider === \"string\" ? bm.provider : undefined,\n family: typeof bm.family === \"string\" ? bm.family : undefined,\n oss_base: typeof bm.oss_base === \"string\" ? bm.oss_base : undefined,\n },\n capabilities: {\n max_completion_tokens:\n typeof caps.max_completion_tokens === \"number\" ? caps.max_completion_tokens : 0,\n recommended_max_tokens:\n typeof caps.recommended_max_tokens === \"number\" ? caps.recommended_max_tokens : 0,\n context_window: typeof caps.context_window === \"number\" ? caps.context_window : 0,\n supports_vision: supportsVision,\n supports_tools: caps.supports_tools === true,\n reasoning: {\n supported: reasoning.supported === true,\n can_disable: reasoning.can_disable === true,\n levels,\n default_level:\n typeof reasoning.default_level === \"string\" ? reasoning.default_level : null,\n },\n },\n benchmarks: typeof v.benchmarks === \"object\" && v.benchmarks !== null ? v.benchmarks : {},\n weights: {\n precision: typeof w.precision === \"string\" ? w.precision : undefined,\n hf_url: typeof w.hf_url === \"string\" ? w.hf_url : undefined,\n },\n stage: typeof v.stage === \"string\" ? v.stage : undefined,\n lifecycle: v.lifecycle\n ? {\n playground_start_date:\n typeof v.lifecycle.playground_start_date === \"string\"\n ? v.lifecycle.playground_start_date\n : undefined,\n }\n : undefined,\n });\n }\n return out;\n}\n","// Shared model-info fetch utility.\n//\n// Both src/models.ts (ModelsClient) and src/vision/catalog.ts\n// (ModelInfoClient) fetch the /v1/models/info endpoint with the same\n// pattern: add a Bearer Authorization header, GET the JSON body, and\n// parse it via parseModelInfoResponse into a map keyed by model id.\n// This module extracts that shared fetch+parse pipeline so both\n// consumers share one implementation.\n\nimport { parseModelInfoResponse } from \"../model-info-parser.js\";\n\nexport type { ParsedModelInfo } from \"../model-info-parser.js\";\nimport type { ParsedModelInfo } from \"../model-info-parser.js\";\n\n/** Timeout for the upstream fetch (ms). */\nconst FETCH_TIMEOUT_MS = 15000;\n\n/**\n * Fetch /v1/models/info from `target` and return a map keyed by model id.\n *\n * Adds a `Bearer <apiKey>` Authorization header when `apiKey` is non-empty.\n * Throws on any failure — non-ok HTTP status, network error, or parse\n * error — so callers can preserve their own error-handling semantics.\n */\nexport async function fetchModelsInfo(\n target: string,\n apiKey: string | undefined,\n signal?: AbortSignal,\n): Promise<Map<string, ParsedModelInfo>> {\n const headers: Record<string, string> = {};\n if (apiKey) headers.authorization = `Bearer ${apiKey}`;\n\n const resp = await fetch(target, {\n method: \"GET\",\n headers,\n signal: signal ?? AbortSignal.timeout(FETCH_TIMEOUT_MS),\n });\n if (!resp.ok) {\n throw new Error(`HTTP ${resp.status} from ${target}`);\n }\n const parsed = await resp.json();\n return parseModelInfoResponse(parsed);\n}\n","// Upstream /v1/models client — fetches the model list and derives per-model\n// concurrency weights. Models with output pricing below the cheap-threshold\n// (default 2.0) get a reduced weight (default 0.5); all others default to 1.0.\n\nimport { createLogger } from \"./logger.js\";\nimport type { ParsedModelInfo, VisionSupport } from \"./model-info-parser.js\";\nimport { fetchModelsInfo } from \"./models/fetch-info.js\";\nimport type { VisionLookup, VisionTristate } from \"./vision/detect.js\";\n\nconst log = createLogger(\"models\");\n\nconst DEFAULT_MODELS_PATH = \"/v1/models\";\nconst DEFAULT_MODELS_INFO_PATH = \"/v1/models/info\";\n/** Pricing output threshold below which a model is considered \"cheap\". */\nconst CHEAP_OUTPUT_THRESHOLD = 2;\n/** Weight assigned to cheap models (output pricing < threshold). */\nconst CHEAP_MODEL_WEIGHT = 0.5;\n/** Default weight for models without cheap pricing. */\nconst DEFAULT_MODEL_WEIGHT = 1;\n\n/** Rich model info from /v1/models/info — faithfully typed from the upstream API. */\ninterface ModelInfo {\n name: string;\n display_name: string;\n description: string;\n base_model: {\n name: string;\n provider?: string;\n family?: string;\n oss_base?: string;\n };\n capabilities: {\n max_completion_tokens: number;\n recommended_max_tokens: number;\n context_window: number;\n supports_vision: VisionSupport;\n supports_tools: boolean;\n reasoning: {\n supported: boolean;\n can_disable: boolean;\n levels: string[];\n default_level: string | null;\n };\n };\n benchmarks: Record<string, unknown>;\n weights: {\n precision: string | undefined;\n hf_url: string | undefined;\n };\n stage?: string;\n lifecycle?: {\n playground_start_date?: string;\n };\n}\n\nexport interface ModelEntry {\n id: string;\n context_length: number;\n pricing: { input: number; output: number } | null;\n weight: number;\n info: ModelInfo | null;\n}\n\ninterface RawModel {\n id?: unknown;\n context_length?: unknown;\n pricing?: { input?: unknown; output?: unknown } | null;\n}\n\ninterface RawModelsResponse {\n data?: RawModel[];\n}\n\nexport interface ModelsClientOptions {\n target: string;\n apiKey?: string | null;\n refreshMs: number;\n /** Path appended to target for the model list. */\n path?: string;\n /** Path appended to target for the model info endpoint. */\n infoPath?: string;\n}\n\n/**\n * Fetches /v1/models on a timer, derives weights, and exposes synchronous\n * lookups. Failures are best-effort: last-known-good is served with ok=false.\n */\nexport class ModelsClient implements VisionLookup {\n private entries: Map<string, ModelEntry> = new Map();\n private fetchedAt = 0;\n private ok = false;\n private timer: ReturnType<typeof setInterval> | null = null;\n private fetching: Promise<boolean> | null = null;\n private onChangeCb: (() => void) | null = null;\n private readonly target: string;\n private readonly apiKey: string | null;\n private readonly refreshMs: number;\n private readonly path: string;\n private readonly infoPath: string;\n\n constructor(opts: ModelsClientOptions) {\n this.target = opts.target.replace(/\\/+$/, \"\");\n this.apiKey = opts.apiKey ?? null;\n this.refreshMs = opts.refreshMs;\n this.path = opts.path ?? DEFAULT_MODELS_PATH;\n this.infoPath = opts.infoPath ?? DEFAULT_MODELS_INFO_PATH;\n }\n\n start(): void {\n if (this.timer) return;\n void this.refresh().catch(() => {});\n this.timer = setInterval(() => {\n void this.refresh().catch(() => {});\n }, this.refreshMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Subscribe to catalog updates (fired after each successful or failed refresh). */\n onChange(cb: () => void): void {\n this.onChangeCb = cb;\n }\n\n /** True if the catalog has been successfully fetched at least once. */\n isReady(): boolean {\n return this.fetchedAt > 0;\n }\n\n /** Timestamp (ms) of the last successful fetch, or 0 if never. */\n lastFetchedAt(): number {\n return this.fetchedAt;\n }\n\n /** Current fetch health. */\n healthy(): boolean {\n return this.ok;\n }\n\n /**\n * Look up the derived weight for a model id.\n * Returns DEFAULT_MODEL_WEIGHT if the model is unknown or the catalog\n * has not been fetched yet.\n */\n getWeight(modelId: string): number {\n const entry = this.entries.get(modelId);\n return entry?.weight ?? DEFAULT_MODEL_WEIGHT;\n }\n\n getVisionSupport(modelName: string): VisionTristate | null {\n const entry = this.entries.get(modelName);\n if (!entry?.info) return null;\n return entry.info.capabilities.supports_vision;\n }\n\n /** Get a model entry, or null if unknown. */\n get(modelId: string): ModelEntry | null {\n return this.entries.get(modelId) ?? null;\n }\n\n /** Snapshot of all known models (sorted by id). */\n list(): ModelEntry[] {\n return [...this.entries.values()].sort((a, b) => a.id.localeCompare(b.id));\n }\n\n /**\n * Fetch the model list from the upstream API.\n * Deduplicates concurrent calls. Returns true on success, false on failure.\n */\n async refresh(): Promise<boolean> {\n if (this.fetching) return this.fetching;\n this.fetching = this.doFetch();\n try {\n return await this.fetching;\n } finally {\n this.fetching = null;\n }\n }\n\n private async doFetch(): Promise<boolean> {\n const url = `${this.target}${this.path}`;\n const infoUrl = `${this.target}${this.infoPath}`;\n const headers: Record<string, string> = {};\n if (this.apiKey) headers.authorization = `Bearer ${this.apiKey}`;\n const infoPromise = fetchModelsInfo(infoUrl, this.apiKey ?? undefined).then(\n (m) => m,\n (e: unknown) => {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.startsWith(\"HTTP \")) {\n log.warn(`info fetch failed: ${msg} from ${infoUrl}`);\n } else {\n log.warn(`info fetch parse failed: ${msg}`);\n }\n return null;\n },\n );\n try {\n const resp = await fetch(url, { method: \"GET\", headers, signal: AbortSignal.timeout(15000) });\n if (!resp.ok) {\n this.ok = false;\n log.error(`fetch failed: HTTP ${resp.status} from ${url}`);\n this.onChangeCb?.();\n return false;\n }\n const parsed = (await resp.json()) as RawModelsResponse;\n const data = parsed.data;\n if (!Array.isArray(data)) {\n this.ok = false;\n log.error(\"fetch failed: response has no data[] array\");\n this.onChangeCb?.();\n return false;\n }\n\n let infoMap: Map<string, ParsedModelInfo> | null = null;\n const infoResult = await infoPromise;\n if (infoResult) {\n infoMap = infoResult;\n }\n\n const next = new Map<string, ModelEntry>();\n for (const raw of data) {\n const id = typeof raw.id === \"string\" ? raw.id : null;\n if (!id) continue;\n const pricing =\n raw.pricing && typeof raw.pricing === \"object\"\n ? {\n input: typeof raw.pricing.input === \"number\" ? raw.pricing.input : 0,\n output: typeof raw.pricing.output === \"number\" ? raw.pricing.output : 0,\n }\n : null;\n const weight =\n pricing && pricing.output < CHEAP_OUTPUT_THRESHOLD\n ? CHEAP_MODEL_WEIGHT\n : DEFAULT_MODEL_WEIGHT;\n next.set(id, {\n id,\n context_length: typeof raw.context_length === \"number\" ? raw.context_length : 0,\n pricing,\n weight,\n info: infoMap?.get(id) ?? null,\n });\n }\n\n this.entries = next;\n this.fetchedAt = Date.now();\n this.ok = true;\n const cheap = [...next.values()].filter((m) => m.weight < DEFAULT_MODEL_WEIGHT).length;\n const withInfo = [...next.values()].filter((m) => m.info !== null).length;\n log.info(\n `fetched ${next.size} models from ${url} — cheap (weight<1): ${cheap}, with info: ${withInfo}`,\n );\n this.onChangeCb?.();\n return true;\n } catch (err) {\n this.ok = false;\n log.error(`fetch error: ${err instanceof Error ? err.message : String(err)}`);\n this.onChangeCb?.();\n return false;\n }\n }\n}\n","// Proxy handler — captures request/response, stamps TTL, forwards upstream.\n// Tee's response stream so the client gets data immediately while capture\n// accumulates in the background.\n\nimport type { CaptureDB } from \"./db.js\";\nimport {\n HOP,\n classify429,\n computeRequestWeight,\n decodeText,\n headersToObject,\n newSummary,\n redactHeaders,\n textDecoder,\n textEncoder,\n} from \"./helpers.js\";\nimport type { GateError } from \"./limiter/index.js\";\nimport type { ConcurrencyGate } from \"./limiter/index.js\";\nimport { createLogger } from \"./logger.js\";\n\nimport { STAMP_ANTHROPIC_BETA_HEADER } from \"./config.js\";\nimport { extractModelName } from \"./models/name.js\";\nimport type { WriteQueue } from \"./queue.js\";\nimport type { SlidingWindowRateLimiter } from \"./rate.js\";\nimport {\n CacheTtlStep,\n STAMP_PIPELINE,\n type StampContext,\n parseJsonBody,\n} from \"./stamp-pipeline.js\";\nimport type {\n CaptureConfig,\n GateConfig,\n ProtocolConfig,\n RequestMeta,\n ResponseMeta,\n StampConfig,\n} from \"./types.js\";\nimport type { UsageMetrics } from \"./usage-extract.js\";\n\nconst log = createLogger(\"proxy\");\nimport type { ModelsClient } from \"./models.js\";\nimport { extractUsage } from \"./usage-extract.js\";\nimport type { VisionHandoff } from \"./vision/handoff.js\";\nimport type { WsBroadcaster } from \"./ws.js\";\n\n// ─── Stamp pipeline (table-driven dispatch) ────────────────────────────────\n\n/**\n * Re-stamp cache_control TTL on the post-vision body.\n * Only CacheTtlStep runs here — thinking/maxTokens/outputConfig/reasoning\n * are NOT re-applied after vision injection.\n */\nfunction stampPostVision(\n body: unknown,\n ctx: StampContext,\n reqBuf: Uint8Array,\n): { reqBuf: Uint8Array; changed: boolean } {\n if (!CacheTtlStep.applies(ctx) || body === null || typeof body !== \"object\") {\n return { reqBuf, changed: false };\n }\n const changed = CacheTtlStep.apply(body, ctx);\n if (!changed) return { reqBuf, changed: false };\n return { reqBuf: textEncoder.encode(JSON.stringify(body)), changed: true };\n}\n\n/** Create the proxy request handler. */\nexport type RateLimiterRef = { current: SlidingWindowRateLimiter | null };\n\nexport function createProxyHandler(\n db: CaptureDB,\n ws: WsBroadcaster,\n queue: WriteQueue,\n config: StampConfig & CaptureConfig & GateConfig & ProtocolConfig,\n gate: ConcurrencyGate,\n rateRef: RateLimiterRef,\n vision: VisionHandoff | null,\n models: ModelsClient,\n onTraffic?: () => void,\n) {\n async function handleProxy(req: Request, url: URL): Promise<Response> {\n onTraffic?.();\n const startedAt = Date.now();\n const path = url.pathname + url.search;\n const targetUrl = config.target + path;\n\n const reqHeadersRaw = headersToObject(req.headers);\n const isOpenAi = url.pathname.includes(config.openaiPath);\n\n // Claude Code stamp: ensure ?beta=true on /v1/messages requests.\n const stampBeta = config.stampClaudeCode && !isOpenAi && url.pathname === \"/v1/messages\";\n const targetUrlObj = new URL(targetUrl);\n const finalTargetUrl =\n stampBeta && targetUrlObj.searchParams.get(\"beta\") !== \"true\"\n ? `${targetUrl}${targetUrl.includes(\"?\") ? \"&\" : \"?\"}beta=true`\n : targetUrl;\n\n // --- Request body capture ---\n let reqBuf: Uint8Array | null = null;\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n reqBuf = new Uint8Array(await req.arrayBuffer());\n }\n\n // Early exit if client already disconnected after sending the body.\n if (req.signal.aborted) {\n return new Response(null, { status: 499 });\n }\n\n // --- Stamp pipeline (TTL, AnthropicBody, OpenAiReasoning, TopK) ---\n // Non-critical: stamping is optimization, not correctness. If it fails,\n // forward the original body unchanged.\n let body: unknown = null;\n if (reqBuf && reqBuf.byteLength > 0) {\n try {\n const parsed = parseJsonBody(reqBuf, reqHeadersRaw);\n body = parsed.body;\n } catch (err) {\n log.warn(\"stamp pipeline failed, forwarding original body\", {\n error: (err as Error).message,\n path: url.pathname,\n });\n }\n }\n\n const reqModelName = body ? (extractModelName(body) ?? null) : null;\n\n if (body && typeof body === \"object\" && body !== null) {\n try {\n const stampCtx: StampContext = {\n config,\n isOpenAi,\n headers: reqHeadersRaw,\n url,\n method: req.method,\n modelName: reqModelName ?? undefined,\n };\n let stampChanged = false;\n for (const step of STAMP_PIPELINE) {\n if (!step.applies(stampCtx)) continue;\n if (step.apply(body, stampCtx)) stampChanged = true;\n }\n if (stampChanged) {\n reqBuf = textEncoder.encode(JSON.stringify(body));\n }\n } catch (err) {\n log.warn(\"stamp pipeline failed, forwarding original body\", {\n error: (err as Error).message,\n path: url.pathname,\n });\n }\n }\n\n // --- Insert capture row (early, so vision calls can link to it) ---\n let reqBodyText = reqBuf ? decodeText(reqBuf) : \"\";\n const reqMeta: RequestMeta = {\n method: req.method,\n path,\n request_size: reqBuf ? reqBuf.byteLength : 0,\n started_at: startedAt,\n };\n const capId = db.startCapture({\n $method: req.method,\n $path: path,\n $url: finalTargetUrl,\n $rh: JSON.stringify(redactHeaders(reqHeadersRaw)),\n $rb: reqBodyText,\n $rs: reqBuf ? reqBuf.byteLength : 0,\n $st: startedAt,\n $state: \"enqueued\",\n $inp: config.incomingProtocol,\n $outp: config.upstreamProtocol,\n });\n ws.broadcast({\n type: \"new\",\n capture: newSummary(capId, reqMeta, config, \"enqueued\", reqModelName),\n });\n\n // --- Vision handoff (image → text description) ---\n // Non-critical: vision is an optional feature. If it fails, forward\n // the original body unchanged.\n if (reqBuf && reqBuf.byteLength > 0 && vision) {\n try {\n if (!body) {\n try {\n const ct = reqHeadersRaw[\"content-type\"] ?? \"\";\n if (ct.includes(\"json\") || reqBuf[0] === 0x7b) {\n body = JSON.parse(textDecoder.decode(reqBuf));\n }\n } catch {\n body = null;\n }\n }\n if (body) {\n const apiKind = isOpenAi ? \"openai\" : \"anthropic\";\n const modelName = reqModelName ?? undefined;\n const result = config.backgroundVision\n ? await vision.processBodyCacheOnly(body, apiKind, modelName, capId, req.signal)\n : await vision.processBody(body, apiKind, modelName, capId, req.signal);\n if (result.changed) {\n reqBuf = textEncoder.encode(JSON.stringify(result.body));\n const postVisionCtx: StampContext = {\n config,\n isOpenAi,\n headers: reqHeadersRaw,\n url,\n method: req.method,\n modelName: extractModelName(result.body),\n };\n const stamped = stampPostVision(result.body, postVisionCtx, reqBuf);\n if (stamped.changed) {\n reqBuf = stamped.reqBuf;\n log.info(`post-handoff stamped (claude_code=${config.stampClaudeCode})`, {\n method: req.method,\n path: url.pathname,\n });\n }\n reqBodyText = decodeText(reqBuf);\n db.updateRequestBody(capId, reqBodyText, reqBuf.byteLength);\n reqMeta.request_size = reqBuf.byteLength;\n ws.broadcast({\n type: \"update\",\n capture: newSummary(capId, reqMeta, config, \"enqueued\", reqModelName),\n });\n log.info(\n `vision handoff: ${result.stats.handoffCount} images, ${result.stats.cacheHits} hits, ${result.stats.visionCalls} calls, active=${vision.visionActive} queued=${vision.visionQueued}`,\n {\n captureId: capId,\n method: req.method,\n path: url.pathname,\n },\n );\n }\n }\n } catch (err) {\n log.warn(\"vision handoff failed, forwarding original body\", {\n error: (err as Error).message,\n captureId: capId,\n });\n }\n }\n\n const reqSize = reqBuf ? reqBuf.byteLength : 0;\n reqMeta.request_size = reqSize;\n\n // --- Forwarded request headers: strip hop-by-hop + host ---\n const fwdHeaders: Record<string, string> = {};\n for (const [k, v] of Object.entries(reqHeadersRaw)) {\n if (HOP.has(k)) continue;\n fwdHeaders[k] = v;\n }\n\n // The proxy strips Content-Encoding from the upstream response and forwards\n // decoded bodies, so it must not advertise compression. Force identity on\n // every upstream request to stay responsible for the encoding contract.\n fwdHeaders[\"accept-encoding\"] = \"identity\";\n\n if (stampBeta) {\n fwdHeaders[\"anthropic-beta\"] = STAMP_ANTHROPIC_BETA_HEADER;\n }\n\n // --- Weighted rate limit check (pro tier only; rate is null when unlimited) ---\n const modelName = reqModelName ?? undefined;\n const weight = computeRequestWeight(modelName, models);\n const rate = rateRef.current;\n if (rate) {\n const rc = rate.check(weight);\n if (!rc.allowed) {\n queue.queueUpdate(capId, reqMeta, {\n $status: 429,\n $rh: JSON.stringify({ error: \"rate_limit_exceeded\" }),\n $rb: JSON.stringify({ error: \"rate_limit_exceeded\", retry_after: rc.retryAfterSeconds }),\n $rs: 0,\n $ct: \"application/json\",\n $sse: 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"gate\",\n $gate_reason: `Rate limit exceeded — retry after ${rc.retryAfterSeconds}s`,\n });\n return new Response(\n JSON.stringify({\n error: \"rate_limit_exceeded\",\n retry_after: rc.retryAfterSeconds,\n }),\n {\n status: 429,\n headers: {\n \"content-type\": \"application/json\",\n \"retry-after\": String(rc.retryAfterSeconds),\n },\n },\n );\n }\n }\n\n // --- Concurrency gate: acquire a permit (blocks if at cap) ---\n let permit: { release: () => void } | null = null;\n try {\n permit = await gate.acquire({\n weight,\n signal: req.signal,\n onAcquire: () => {\n db.setState(capId, \"streaming\");\n ws.broadcast({ type: \"state\", captureId: capId, state: \"streaming\" });\n },\n });\n } catch (e) {\n const err = e as GateError;\n const aborted = err.code === \"aborted\";\n const status = aborted\n ? 499\n : err.code === \"circuit_open\" || err.code === \"queue_full\" || err.code === \"timeout\"\n ? 503\n : 502;\n queue.queueUpdate(capId, reqMeta, {\n $status: status,\n $rh: JSON.stringify({ error: err.code }),\n $rb: aborted ? \"\" : err.message,\n $rs: 0,\n $ct: \"application/json\",\n $sse: 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"gate\",\n $gate_reason: aborted\n ? \"Client disconnected while enqueued\"\n : err.code === \"circuit_open\"\n ? \"Circuit breaker open — upstream concurrency 429s exceeded threshold\"\n : err.code === \"queue_full\"\n ? \"Concurrency queue full — too many requests waiting for a slot\"\n : err.code === \"timeout\"\n ? \"Queue timeout — request waited too long for a concurrency slot\"\n : err.code === \"invalid_weight\"\n ? \"Invalid weight — model weight must be positive\"\n : err.message,\n });\n if (aborted) return new Response(null, { status: 499 });\n return new Response(JSON.stringify({ error: err.code, message: err.message }), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n\n // --- Forward upstream ---\n let upstream: Response;\n try {\n const upstreamSignal = AbortSignal.any([\n req.signal,\n AbortSignal.timeout(config.upstreamTimeoutMs),\n ]);\n upstream = await fetch(finalTargetUrl, {\n method: req.method,\n headers: fwdHeaders,\n body: reqBuf && reqBuf.byteLength > 0 ? (reqBuf as BodyInit) : undefined,\n protocol: config.upstreamProtocol as unknown as never,\n signal: upstreamSignal,\n });\n } catch (e) {\n const err = e as Error;\n const clientAborted = err.name === \"AbortError\" && req.signal.aborted;\n const upstreamTimedOut =\n err.name === \"TimeoutError\" || (err.name === \"AbortError\" && !req.signal.aborted);\n const status = clientAborted ? 499 : upstreamTimedOut ? 504 : 502;\n queue.queueUpdate(capId, reqMeta, {\n $status: status,\n $rh: JSON.stringify({\n error: clientAborted\n ? \"client_disconnected\"\n : upstreamTimedOut\n ? \"upstream_timeout\"\n : String(err),\n }),\n $rb: clientAborted ? \"\" : `Upstream error: ${err.message}`,\n $rs: 0,\n $ct: \"text/plain\",\n $sse: 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"gate\",\n $gate_reason: clientAborted\n ? \"Client disconnected during upstream request\"\n : upstreamTimedOut\n ? \"Upstream inactivity timeout (300s)\"\n : `Upstream unreachable — ${err.message}`,\n });\n permit?.release();\n if (clientAborted) return new Response(null, { status: 499 });\n if (upstreamTimedOut) return new Response(`Gateway Timeout: ${err.message}`, { status: 504 });\n return new Response(`Bad Gateway: ${err.message}`, { status: 502 });\n }\n\n // --- Classify 429: only concurrency-429s trip the breaker ---\n try {\n if (upstream.status === 429) {\n gate.record429(classify429(upstream));\n } else if (upstream.status < 400) {\n gate.recordSuccess();\n }\n\n const resHeadersRaw = headersToObject(upstream.headers);\n const resHeadersJson = JSON.stringify(resHeadersRaw);\n const contentType = upstream.headers.get(\"content-type\") ?? \"\";\n const isSSE = contentType.includes(\"text/event-stream\");\n\n // Forwarded response headers: strip content-encoding/length + hop-by-hop.\n const outHeaders: Record<string, string> = {};\n for (const [k, v] of Object.entries(resHeadersRaw)) {\n if (HOP.has(k) || k === \"content-encoding\") continue;\n outHeaders[k] = v;\n }\n\n const doneRes = (): Omit<ResponseMeta, \"$rb\" | \"$rs\"> => ({\n $status: upstream.status,\n $rh: resHeadersJson,\n $ct: contentType,\n $sse: isSSE ? 1 : 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"upstream\",\n $gate_reason: null,\n });\n\n if (!upstream.body) {\n queue.queueUpdate(capId, reqMeta, { ...doneRes(), $rb: \"\", $rs: 0 });\n permit?.release();\n return new Response(null, { status: upstream.status, headers: outHeaders });\n }\n\n // Stream response to client while incrementally decoding for capture.\n // Decodes each chunk with stream:true to handle multi-byte sequences\n // spanning chunk boundaries, avoiding the combine() double-copy.\n // `captureBodyMaxBytes` caps in-memory buffer growth (0 = unlimited);\n // the stream to the client is never truncated.\n const cap = config.captureBodyMaxBytes;\n const parts: string[] = [];\n const timedChunks: { text: string; time: number }[] = [];\n let totalSize = 0;\n let flushed = false;\n let firstChunkSent = false;\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n const flushCapture = (): void => {\n if (flushed) return;\n flushed = true;\n if (cap === 0 || totalSize <= cap) {\n try {\n const tail = decoder.decode();\n if (tail) parts.push(tail);\n } catch {\n // Tail had invalid bytes — ignore, already captured above.\n }\n }\n const fullBody = parts.join(\"\");\n const isStream = isSSE;\n let usage: UsageMetrics | null = null;\n let model: string | null = null;\n try {\n const result = extractUsage({\n provider: isOpenAi ? \"openai\" : \"anthropic\",\n streaming: isStream,\n requestBody: reqBodyText,\n responseBody: fullBody,\n durationMs: Date.now() - startedAt,\n requestStartedAt: startedAt,\n chunks: isStream ? timedChunks : undefined,\n });\n model = result.model;\n usage = result.metrics;\n } catch {\n usage = null;\n model = null;\n }\n try {\n queue.queueUpdate(capId, reqMeta, {\n ...doneRes(),\n $rb: fullBody,\n $rs: totalSize,\n $usage: usage,\n $model: model,\n });\n } catch {\n // Non-critical: capture persistence failure must not block permit release\n }\n permit?.release();\n };\n const capture = new TransformStream({\n transform(chunk: Uint8Array, controller) {\n totalSize += chunk.byteLength;\n const capturing = cap === 0 || totalSize <= cap;\n const now = Date.now();\n if (capturing) {\n let decoded = \"\";\n try {\n decoded = decoder.decode(chunk, { stream: true });\n parts.push(decoded);\n } catch {\n parts.push(Buffer.from(chunk).toString(\"base64\"));\n }\n if (decoded) timedChunks.push({ text: decoded, time: now });\n }\n if (!firstChunkSent) {\n firstChunkSent = true;\n try {\n ws.broadcast({\n type: \"update\",\n capture: {\n ...newSummary(capId, reqMeta, config, \"streaming\", reqModelName),\n ttft_ms: now - startedAt,\n },\n });\n } catch {\n // Non-critical: dashboard update failure must not error the stream\n }\n }\n controller.enqueue(chunk);\n },\n flush() {\n flushCapture();\n },\n });\n\n // Client disconnected mid-stream — flush capture so it doesn't stay \"streaming\".\n if (req.signal) {\n if (req.signal.aborted) {\n flushCapture();\n } else {\n req.signal.addEventListener(\n \"abort\",\n () => {\n flushCapture();\n },\n { once: true },\n );\n }\n }\n\n const stream = upstream.body.pipeThrough(capture);\n return new Response(stream, {\n status: upstream.status,\n statusText: upstream.statusText,\n headers: outHeaders,\n });\n } catch (err) {\n log.error(\"post-fetch processing failed\", {\n error: (err as Error).message,\n captureId: capId,\n });\n queue.queueUpdate(capId, reqMeta, {\n $status: 500,\n $rh: JSON.stringify({ error: \"internal_error\" }),\n $rb: `Internal error: ${(err as Error).message}`,\n $rs: 0,\n $ct: \"application/json\",\n $sse: 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"gate\",\n $gate_reason: `Internal proxy error — ${(err as Error).message}`,\n });\n permit?.release();\n return new Response(\n JSON.stringify({ error: \"internal_error\", message: (err as Error).message }),\n { status: 500, headers: { \"content-type\": \"application/json\" } },\n );\n }\n }\n\n return { handleProxy };\n}\n","// Stamp pipeline — table-driven dispatch for request body mutation steps.\n// Extracted from proxy.ts to keep the proxy handler focused on transport\n// (capture, streaming, forwarding) while the pipeline is independently\n// testable and extensible.\n//\n// Dependency direction: stamp-pipeline → stamp* / types / config / helpers.\n// Never imports proxy.ts (no cycles).\n\nimport {\n STAMP_CACHE_TTL_VALUE,\n STAMP_CONTEXT_MANAGEMENT_VALUE,\n STAMP_OUTPUT_CONFIG_VALUE,\n STAMP_TEMPERATURE_VALUE,\n STAMP_THINKING_VALUE,\n STAMP_TOP_K_VALUE,\n} from \"./config.js\";\nimport { textDecoder } from \"./helpers.js\";\nimport { createLogger } from \"./logger.js\";\nimport { isGlmModel, resolveEffortForModel } from \"./model-policy.js\";\nimport { stampReasoning } from \"./stamp-reasoning.js\";\nimport { stampTemperature } from \"./stamp-temperature.js\";\nimport { stampThinking } from \"./stamp-thinking.js\";\nimport { stampTopK } from \"./stamp-topk.js\";\nimport { stampCacheTtl } from \"./stamp.js\";\nimport type {\n AnthropicBody,\n CaptureConfig,\n GateConfig,\n OpenAiBody,\n ProtocolConfig,\n StampConfig,\n} from \"./types.js\";\n\nconst log = createLogger(\"stamp-pipeline\");\n\n// ─── Pipeline types ───────────────────────────────────────────────────────\n\n/** Resolved config subset available to every stamp step. */\ntype StampPipelineConfig = StampConfig & CaptureConfig & GateConfig & ProtocolConfig;\n\n/** Everything a stamp step needs to decide applicability and mutate the body. */\nexport interface StampContext {\n config: StampPipelineConfig;\n isOpenAi: boolean;\n headers: Record<string, string>;\n url: URL;\n method: string;\n modelName: string | undefined;\n}\n\n/** A single mutation step in the stamp pipeline. */\nexport interface StampStep {\n readonly label: string;\n applies(ctx: StampContext): boolean;\n apply(body: unknown, ctx: StampContext): boolean;\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\n/** Parse a request buffer as JSON using the content-type / first-byte heuristic. */\nexport function parseJsonBody(\n reqBuf: Uint8Array,\n headers: Record<string, string>,\n): { body: unknown; ok: boolean } {\n const ct = headers[\"content-type\"] ?? \"\";\n if (!ct.includes(\"json\") && reqBuf[0] !== 0x7b) return { body: null, ok: false };\n try {\n return { body: JSON.parse(textDecoder.decode(reqBuf)), ok: true };\n } catch {\n return { body: null, ok: false };\n }\n}\n\n// ─── Concrete steps ───────────────────────────────────────────────────────\n\nexport const CacheTtlStep: StampStep = {\n label: \"ttl\",\n applies(ctx) {\n return ctx.config.stampClaudeCode && !ctx.isOpenAi;\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const n = stampCacheTtl(body as AnthropicBody, STAMP_CACHE_TTL_VALUE);\n if (n > 0) {\n log.info(`stamped ttl=\"${STAMP_CACHE_TTL_VALUE}\" on ${n} block(s)`, {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\nexport const AnthropicBodyStep: StampStep = {\n label: \"anthropic-body\",\n applies(ctx) {\n return ctx.config.stampClaudeCode && !ctx.isOpenAi;\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const effort = resolveEffortForModel(ctx.modelName, true);\n const outputConfigValue = effort !== undefined ? { effort } : STAMP_OUTPUT_CONFIG_VALUE;\n const changed = stampThinking(body as AnthropicBody, {\n maxTokens: true,\n thinking: STAMP_THINKING_VALUE,\n outputConfig: outputConfigValue,\n });\n if (changed) {\n log.info(\"stamped anthropic body fields\", {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\nexport const ContextManagementStep: StampStep = {\n label: \"context-management\",\n applies(ctx) {\n return (\n ctx.config.stampClaudeCode &&\n !ctx.isOpenAi &&\n ctx.headers[\"anthropic-version\"] === \"2023-06-01\"\n );\n },\n apply(body) {\n if (body === null || typeof body !== \"object\") return false;\n const b = body as AnthropicBody;\n b.context_management = {\n edits: STAMP_CONTEXT_MANAGEMENT_VALUE.edits.map((e) => ({ ...e })),\n };\n log.info(\"stamped context_management\");\n return true;\n },\n};\n\nexport const OpenAiReasoningStep: StampStep = {\n label: \"openai-reasoning\",\n applies(ctx) {\n return ctx.isOpenAi && ctx.config.stampReasoningEffort !== null;\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const reasoningEffort =\n resolveEffortForModel(ctx.modelName, ctx.config.stampReasoningEffort !== null) ??\n (ctx.config.stampReasoningEffort as \"high\" | \"max\");\n const changed = stampReasoning(body as OpenAiBody, { reasoningEffort });\n if (changed) {\n log.info(\"stamped openai body reasoning_effort\", {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\nexport const TopKStep: StampStep = {\n label: \"top-k\",\n applies(ctx) {\n return ctx.config.stampClaudeCode && isGlmModel(ctx.modelName);\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const changed = stampTopK(body, STAMP_TOP_K_VALUE);\n if (changed) {\n log.info(`stamped top_k=${STAMP_TOP_K_VALUE}`, {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\nexport const TemperatureStep: StampStep = {\n label: \"temperature\",\n applies(ctx) {\n return ctx.config.stampClaudeCode;\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const changed = stampTemperature(body, STAMP_TEMPERATURE_VALUE);\n if (changed) {\n log.info(`stamped temperature=${STAMP_TEMPERATURE_VALUE}`, {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\n// ─── Pipeline ─────────────────────────────────────────────────────────────\n\n/** Ordered stamp steps applied to every request body before forwarding. */\nexport const STAMP_PIPELINE: StampStep[] = [\n CacheTtlStep,\n AnthropicBodyStep,\n ContextManagementStep,\n OpenAiReasoningStep,\n TopKStep,\n TemperatureStep,\n];\n","import { STAMP_REASONING_EFFORT_GLM_VALUE, STAMP_REASONING_EFFORT_VALUE } from \"./config.js\";\n\n/** Returns true if the model name belongs to the umans-glm family. */\nexport function isGlmModel(modelName: string | undefined): boolean {\n return typeof modelName === \"string\" && modelName.startsWith(\"umans-glm\");\n}\n\n/**\n * Resolve the effort level for a model.\n *\n * GLM models get \"max\"; all others get \"high\". When `enabled` is false,\n * returns undefined (stamping disabled). This is the single source of truth\n * for all GLM effort-policy decisions across the stamp pipeline.\n */\nexport function resolveEffortForModel(modelName: string | undefined, enabled: true): \"high\" | \"max\";\nexport function resolveEffortForModel(modelName: string | undefined, enabled: false): undefined;\nexport function resolveEffortForModel(\n modelName: string | undefined,\n enabled: boolean,\n): \"high\" | \"max\" | undefined;\nexport function resolveEffortForModel(\n modelName: string | undefined,\n enabled: boolean,\n): \"high\" | \"max\" | undefined {\n if (!enabled) return undefined;\n return isGlmModel(modelName) ? STAMP_REASONING_EFFORT_GLM_VALUE : STAMP_REASONING_EFFORT_VALUE;\n}\n","import type { OpenAiBody } from \"./types.js\";\n\nexport interface StampReasoningOptions {\n reasoningEffort: \"high\" | \"max\" | null | undefined;\n}\n\n/**\n * Stamp OpenAI-compatible request body with reasoning_effort and strip any\n * conflicting max_tokens/thinking fields. Mutates the body in place.\n * Returns true if the body was changed.\n *\n * When reasoning_effort is disabled (null/undefined), the key is removed from\n * the body rather than left as an explicit undefined value.\n */\nexport function stampReasoning(body: OpenAiBody, options: StampReasoningOptions): boolean {\n let changed = false;\n\n if (body.max_tokens !== undefined) {\n body.max_tokens = undefined;\n changed = true;\n }\n if (body.thinking !== undefined) {\n body.thinking = undefined;\n changed = true;\n }\n\n if (options.reasoningEffort === null || options.reasoningEffort === undefined) {\n if (Object.prototype.hasOwnProperty.call(body, \"reasoning_effort\")) {\n Reflect.deleteProperty(body, \"reasoning_effort\");\n changed = true;\n }\n } else if (body.reasoning_effort !== options.reasoningEffort) {\n body.reasoning_effort = options.reasoningEffort;\n changed = true;\n }\n\n return changed;\n}\n","// Force `temperature` onto LLM request bodies before forwarding upstream.\n// Applies to both OpenAI-compatible and Anthropic routes.\n// Overwrites any existing `temperature` value already set by the caller.\n\ninterface RequestBody {\n temperature?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Force `temperature` onto a parsed request body, overwriting any existing value.\n * Mutates the body in place. Returns true if the body was changed.\n */\nexport function stampTemperature(body: unknown, temperature: number): boolean {\n if (typeof body !== \"object\" || body === null || Array.isArray(body)) return false;\n const b = body as RequestBody;\n if (b.temperature === temperature) return false;\n b.temperature = temperature;\n return true;\n}\n","import {\n STAMP_MAX_TOKENS_GLM_VALUE,\n STAMP_MAX_TOKENS_VALUE,\n STAMP_OUTPUT_CONFIG_GLM_VALUE,\n STAMP_OUTPUT_CONFIG_VALUE,\n} from \"./config.js\";\nimport { isGlmModel } from \"./model-policy.js\";\nimport { extractModelName } from \"./models/name.js\";\nimport type { AnthropicBody, OutputConfig, ThinkingConfig } from \"./types.js\";\n\nconst DEFAULT_THINKING: ThinkingConfig = {\n type: \"adaptive\",\n};\n\nexport interface StampOptions {\n /** Inject `max_tokens` resolved from model (131071 for GLM, 32767 for others) when true. */\n maxTokens?: boolean;\n /** Inject `thinking` block when true. */\n thinking?: ThinkingConfig | boolean;\n /** Inject `output_config` block when true. */\n outputConfig?: OutputConfig | boolean;\n}\n\nfunction modelMatchesThinkingPattern(model: unknown): boolean {\n if (typeof model !== \"string\") return false;\n if (model === \"umans-coder\" || model === \"umans-flash\") return true;\n return model.startsWith(\"umans-kimi\") || model.startsWith(\"umans-qwen\");\n}\n\nfunction resolveOutputConfig(model: unknown, outputConfig: OutputConfig | boolean): OutputConfig {\n if (typeof outputConfig === \"object\" && outputConfig !== null) return outputConfig;\n if (typeof model === \"string\" && isGlmModel(model)) return STAMP_OUTPUT_CONFIG_GLM_VALUE;\n return STAMP_OUTPUT_CONFIG_VALUE;\n}\n\nfunction resolveMaxTokens(model: unknown): number {\n if (typeof model === \"string\" && isGlmModel(model)) return STAMP_MAX_TOKENS_GLM_VALUE;\n return STAMP_MAX_TOKENS_VALUE;\n}\n\n/**\n * Stamp Anthropic request body fields based on enabled toggles. Overwrites any\n * existing values. Mutates the body in place. Returns true if the body was changed.\n */\nexport function stampThinking(body: AnthropicBody, options: StampOptions): boolean {\n const model = extractModelName(body);\n let changed = false;\n\n if (options.maxTokens) {\n body.max_tokens = resolveMaxTokens(model);\n changed = true;\n }\n\n if (options.thinking) {\n if (modelMatchesThinkingPattern(model)) {\n body.thinking = typeof options.thinking === \"object\" ? options.thinking : DEFAULT_THINKING;\n changed = true;\n }\n }\n\n if (options.outputConfig) {\n body.output_config = resolveOutputConfig(model, options.outputConfig);\n changed = true;\n }\n\n return changed;\n}\n","// Inject `top_k` into LLM request bodies before forwarding upstream.\n// Applies to both OpenAI-compatible and Anthropic routes.\n// Places `top_k` immediately after `model` for consistent wire ordering.\n// Preserves any existing `top_k` value already set by the caller.\n\nimport { isGlmModel } from \"./model-policy.js\";\nimport { extractModelName } from \"./models/name.js\";\n\n/**\n * Body shape we mutate — has a `model` and optional `top_k` at top level.\n * The rest is opaque (OpenAI/Anthropic bodies differ in structure).\n */\ninterface RequestBody {\n model?: unknown;\n top_k?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Inject `top_k` into a parsed request body, placing it right after `model`.\n * If `top_k` already exists on the body, it is preserved (not overwritten).\n * Mutates the body in place. Returns true if the body was changed.\n *\n * Key ordering: JSON.stringify emits string keys in insertion order. To place\n * `top_k` immediately after `model`, we rebuild the object as a new record\n * with model first, then top_k, then the remaining keys — guaranteeing wire\n * ordering matches the UMANS API convention and the benchmark harness.\n */\nexport function stampTopK(body: unknown, topK: number): boolean {\n if (typeof body !== \"object\" || body === null || Array.isArray(body)) return false;\n const model = extractModelName(body);\n if (model === undefined) return false;\n if (!isGlmModel(model)) return false; // defense in depth — guard also in TopKStep.applies()\n const b = body as RequestBody;\n if (b.top_k !== undefined) return false; // caller already set — respect it\n\n // Rebuild with desired key order: model, top_k, then the rest.\n const rebuilt: Record<string, unknown> = {};\n rebuilt.model = model;\n rebuilt.top_k = topK;\n for (const k of Object.keys(b)) {\n if (k !== \"model\" && k !== \"top_k\") rebuilt[k] = b[k];\n }\n // Mutate original in place: clear then copy back.\n for (const k of Object.keys(b)) Reflect.deleteProperty(b, k);\n Object.assign(b, rebuilt);\n return true;\n}\n","// Anthropic cache_control TTL stamping.\n// Walks the request body's system + messages content arrays, stamping\n// `ttl` onto any `cache_control: {type:\"ephemeral\"}` block that lacks one.\n\nimport type { AnthropicBody, ContentBlock } from \"./types.js\";\n\n/**\n * Stamp `ttl` onto Anthropic-style cache_control ephemeral blocks that lack one.\n * Mutates the body in place. Returns the count of blocks stamped (caller\n * re-serializes only when > 0). Non-array system (e.g. a plain string) is\n * skipped safely.\n */\nexport function stampCacheTtl(body: AnthropicBody, ttl: string): number {\n let n = 0;\n\n const stamp = (blocks: unknown) => {\n if (!Array.isArray(blocks)) return;\n for (const b of blocks as ContentBlock[]) {\n const cc = b?.cache_control;\n if (cc?.type === \"ephemeral\" && !cc.ttl) {\n cc.ttl = ttl;\n n++;\n }\n }\n };\n\n stamp(body?.system);\n for (const m of body?.messages ?? []) {\n stamp(m.content);\n }\n\n return n;\n}\n","// Write-behind queue for capture updates.\n// Batches database writes to reduce SQLite contention during streaming.\n\nimport type { UpdateParams } from \"./db.js\";\nimport { buildSummary } from \"./helpers.js\";\nimport { createLogger } from \"./logger.js\";\nimport type { ProtocolConfig, QueueConfig, RequestMeta, ResponseMeta, WsMessage } from \"./types.js\";\n\nconst logger = createLogger(\"queue\");\n\ninterface QueuedUpdate {\n id: number;\n reqMeta: RequestMeta;\n res: ResponseMeta;\n}\n\n/** Abstraction over the capture persistence layer used by WriteQueue. */\nexport interface CaptureStore {\n updateCapture(params: UpdateParams): void;\n batchUpdate(items: Array<{ id: number; res: Omit<UpdateParams, \"$id\"> }>): Promise<void>;\n}\n\n/**\n * Batches response metadata updates and flushes them to the database\n * in a single transaction. Broadcasts WebSocket updates after each flush\n * via the optional onFlush callback.\n */\nexport class WriteQueue {\n private queue: QueuedUpdate[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly flushIntervalMs: number;\n private readonly flushBatch: number;\n private readonly queueMaxDepth: number;\n private store: CaptureStore;\n private onFlush?: (messages: WsMessage[]) => void;\n private config: QueueConfig & ProtocolConfig;\n droppedCount = 0;\n\n constructor(\n store: CaptureStore,\n config: QueueConfig & ProtocolConfig,\n onFlush?: (messages: WsMessage[]) => void,\n ) {\n this.store = store;\n this.config = config;\n this.onFlush = onFlush;\n this.flushIntervalMs = config.flushIntervalMs;\n this.flushBatch = config.flushBatch;\n this.queueMaxDepth = config.queueMaxDepth;\n }\n\n /** Queue a response metadata update for a capture. */\n queueUpdate(id: number, reqMeta: RequestMeta, res: ResponseMeta): void {\n this.queue.push({ id, reqMeta, res });\n if (this.queue.length >= this.flushBatch) {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n void this.flushNow().catch((err) => {\n logger.error(\"WriteQueue flush failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n } else if (!this.flushTimer) {\n this.flushTimer = setTimeout(() => {\n void this.flushNow().catch((err) => {\n logger.error(\"WriteQueue flush failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }, this.flushIntervalMs);\n }\n if (this.queue.length >= this.queueMaxDepth) {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n void this.flushNow().catch((err) => {\n logger.error(\"WriteQueue flush failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n if (this.queue.length >= this.queueMaxDepth) {\n const dropped = this.queue.shift();\n this.droppedCount++;\n logger.warn(\"WriteQueue overflow: dropped oldest entry\", {\n captureId: dropped?.id,\n depth: this.queue.length,\n maxDepth: this.queueMaxDepth,\n totalDropped: this.droppedCount,\n });\n }\n }\n }\n\n get length(): number {\n return this.queue.length;\n }\n\n get hasTimer(): boolean {\n return this.flushTimer !== null;\n }\n\n /** Flush all queued updates to the database immediately.\n * On batchUpdate failure, re-queues the batch at the front so items are\n * not permanently lost. The drop path in queueUpdate() will activate\n * if the queue overflows after a failed flush. */\n async flushNow(): Promise<void> {\n this.flushTimer = null;\n if (this.queue.length === 0) return;\n const batch = this.queue.splice(0, this.queue.length);\n try {\n await this.store.batchUpdate(batch.map((it) => ({ id: it.id, res: it.res })));\n } catch (err) {\n this.queue.unshift(...batch);\n logger.error(\"WriteQueue flush failed, re-queued batch\", {\n batchSize: batch.length,\n depth: this.queue.length,\n error: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n if (this.onFlush) {\n const messages: WsMessage[] = batch.map((it) => ({\n type: \"update\" as const,\n capture: buildSummary(it.id, it.reqMeta, it.res, this.config),\n }));\n this.onFlush(messages);\n }\n }\n}\n","// Sliding-window rate limiter for the pro-tier request limit.\n// Tracks weighted entries: each request consumes `weight` units of the budget.\n// Binary-search pruning; check() records, peek() does not.\n\nexport interface RateLimitConfig {\n limit: number;\n windowSeconds: number;\n}\n\nexport interface RateCheckResult {\n allowed: boolean;\n remaining: number;\n retryAfterSeconds: number | null;\n}\n\ninterface Entry {\n time: number;\n weight: number;\n}\n\nexport class SlidingWindowRateLimiter {\n private entries: Entry[] = [];\n private runningSum = 0;\n private readonly limit;\n private readonly windowMs;\n\n constructor(config: RateLimitConfig) {\n this.limit = config.limit;\n this.windowMs = config.windowSeconds * 1000;\n }\n\n /** Record a request with the given weight. Returns whether it was allowed. */\n check(weight = 1, now: number = Date.now()): RateCheckResult {\n this.prune(now);\n const currentWeight = this.runningSum;\n if (currentWeight + weight > this.limit) {\n const oldest = this.entries[0];\n const retryAfter = oldest ? Math.ceil((oldest.time + this.windowMs - now) / 1000) : 1;\n return {\n allowed: false,\n remaining: Math.max(0, this.limit - currentWeight),\n retryAfterSeconds: Math.max(1, retryAfter),\n };\n }\n this.entries.push({ time: now, weight });\n this.runningSum += weight;\n return {\n allowed: true,\n remaining: Math.max(0, this.limit - currentWeight - weight),\n retryAfterSeconds: null,\n };\n }\n\n /** Check whether a request with the given weight would be allowed, without recording. */\n peek(weight = 1, now: number = Date.now()): RateCheckResult {\n this.prune(now);\n const currentWeight = this.runningSum;\n if (currentWeight + weight > this.limit) {\n const oldest = this.entries[0];\n const retryAfter = oldest ? Math.ceil((oldest.time + this.windowMs - now) / 1000) : 1;\n return {\n allowed: false,\n remaining: Math.max(0, this.limit - currentWeight),\n retryAfterSeconds: Math.max(1, retryAfter),\n };\n }\n return {\n allowed: true,\n remaining: Math.max(0, this.limit - currentWeight - weight),\n retryAfterSeconds: null,\n };\n }\n\n private prune(now: number): void {\n const cutoff = now - this.windowMs;\n let lo = 0;\n let hi = this.entries.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (this.entries[mid].time < cutoff) lo = mid + 1;\n else hi = mid;\n }\n if (lo > 0) {\n for (let i = 0; i < lo; i++) this.runningSum -= this.entries[i].weight;\n this.entries.splice(0, lo);\n }\n }\n\n /** Total weight of entries currently in the window. */\n count(now: number = Date.now()): number {\n this.prune(now);\n return this.runningSum;\n }\n}\n","// Shared /v1/usage fetch helper.\n// Consolidates duplicated fetch+parse+error handling between reconciler and aggregator.\n\nimport type { RawUsage } from \"./parser.js\";\n\nconst USAGE_PATH = \"/v1/usage\";\n\nexport type { RawUsage };\n\nexport type FetchUsageResult = { ok: true; data: RawUsage } | { ok: false; error: string };\n\n/** Shared raw fetch of the upstream /v1/usage endpoint.\n * Preserves the exact request shape used by callers: GET, no query params,\n * and an `authorization: Bearer <apiKey>` header when an API key is provided. */\nexport async function fetchUsageRaw(\n target: string,\n apiKey: string | null,\n signal?: AbortSignal,\n): Promise<FetchUsageResult> {\n if (!apiKey) return { ok: false, error: \"umans_api_key not set\" };\n try {\n const res = await fetch(`${target}${USAGE_PATH}`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${apiKey}` },\n signal,\n });\n if (!res.ok) return { ok: false, error: `HTTP ${res.status}` };\n const data = (await res.json()) as RawUsage;\n return { ok: true, data };\n } catch (e) {\n return { ok: false, error: e instanceof Error ? e.message : String(e) };\n }\n}\n","// Raw upstream /v1/usage response parsing + snapshot construction.\n// Pure functions: no I/O, no mutation. Reusable by aggregator + reconciler.\n\nimport type { UsageSnapshot } from \"../types.js\";\n\n/** Raw shape of the upstream /v1/usage response. */\nexport interface RawUsage {\n plan?: { display_name?: string };\n limits?: {\n requests?: { limit?: number; hard_cap?: number; burst_pct?: number; window_seconds?: number };\n concurrency?: { limit?: number; hard_cap?: number; burst_pct?: number };\n };\n usage?: {\n requests_in_window?: number;\n remaining_requests?: number;\n concurrent_sessions?: number;\n tokens_in?: number;\n tokens_out?: number;\n priority?: {\n low?: boolean;\n boxed_until?: number | null;\n reason?: string | null;\n units_demoted?: boolean;\n demoted_until?: number | null;\n };\n };\n}\n\n/** Classify a plan display name into one of the known tiers.\n * Uses substring matching so variants like \"Code Max (Founding Seat)\"\n * still resolve to \"Code Max\". */\nfunction classifyPlan(name: string | undefined): \"Code Pro\" | \"Code Max\" | \"unknown\" {\n if (!name) return \"unknown\";\n const lower = name.toLowerCase();\n if (lower.includes(\"code max\")) return \"Code Max\";\n if (lower.includes(\"code pro\")) return \"Code Pro\";\n return \"unknown\";\n}\n\n/** Build a UsageSnapshot from a parsed raw response.\n * Carries forward last-known concurrency limits when the upstream omits them. */\nexport function buildSnapshot(\n raw: RawUsage,\n ok: boolean,\n lastConcurrencyHardCap: number,\n lastConcurrencySoftLimit: number,\n): UsageSnapshot {\n const planName = raw.plan?.display_name;\n const plan = classifyPlan(planName);\n return {\n ok,\n fetchedAt: Date.now(),\n plan,\n requestsLimit: raw.limits?.requests?.limit ?? null,\n requestsHardCap: raw.limits?.requests?.hard_cap ?? null,\n requestsWindowSeconds: raw.limits?.requests?.window_seconds ?? null,\n concurrencySoftLimit: raw.limits?.concurrency?.limit ?? lastConcurrencySoftLimit,\n concurrencyHardCap: raw.limits?.concurrency?.hard_cap ?? lastConcurrencyHardCap,\n requestsInWindow: raw.usage?.requests_in_window ?? 0,\n requestsRemaining: raw.usage?.remaining_requests ?? null,\n concurrentSessions: raw.usage?.concurrent_sessions ?? 0,\n priorityLow: raw.usage?.priority?.low ?? false,\n boxedUntil: raw.usage?.priority?.boxed_until ?? null,\n boxedReason: raw.usage?.priority?.reason ?? null,\n unitsDemoted: raw.usage?.priority?.units_demoted ?? false,\n demotedUntil: raw.usage?.priority?.demoted_until ?? null,\n };\n}\n\n/** Fail-safe worst-case snapshot used when no data is available.\n * Assumes at hard cap, priority low — the most conservative posture. */\nexport function failSafeSnapshot(): UsageSnapshot {\n return {\n ok: false,\n fetchedAt: 0,\n plan: \"unknown\",\n requestsLimit: null,\n requestsHardCap: null,\n requestsWindowSeconds: null,\n concurrencySoftLimit: 1,\n concurrencyHardCap: 1,\n requestsInWindow: 0,\n requestsRemaining: null,\n concurrentSessions: 0,\n priorityLow: true,\n boxedUntil: null,\n boxedReason: null,\n unitsDemoted: false,\n demotedUntil: null,\n };\n}\n","// One-shot /v1/usage fetches for limit reconciliation.\n// Used by proxy startup + config validation to compare source-of-truth limits\n// against cached/captured values. Does NOT touch live snapshot state.\n\nimport { fetchUsageRaw } from \"./fetch-usage.js\";\n\nexport type ConcurrencyLimitResult =\n | { ok: true; hardCap: number; softLimit: number }\n | { ok: false; error: string };\n\nexport type RequestsLimitResult =\n | { ok: true; limit: number | null; hardCap: number | null; windowSeconds: number | null }\n | { ok: false; error: string };\n\n/** One-shot fetch of /v1/usage to extract concurrency hard_cap + soft_limit.\n * Does NOT update live snapshot. */\nexport async function fetchConcurrencyLimits(\n target: string,\n apiKey: string | null,\n): Promise<ConcurrencyLimitResult> {\n const result = await fetchUsageRaw(target, apiKey);\n if (!result.ok) return { ok: false, error: result.error };\n const hardCap = Math.max(1, Math.floor(Number(result.data.limits?.concurrency?.hard_cap ?? 1)));\n const softLimit = Math.max(1, Math.floor(Number(result.data.limits?.concurrency?.limit ?? 1)));\n return { ok: true, hardCap, softLimit };\n}\n\n/** One-shot fetch of /v1/usage to extract requests limits for rate_limit_requests validation.\n * Returns {limit, hardCap, windowSeconds} or null if not set (unlimited). */\nexport async function fetchRequestsLimits(\n target: string,\n apiKey: string | null,\n): Promise<RequestsLimitResult> {\n const result = await fetchUsageRaw(target, apiKey);\n if (!result.ok) return { ok: false, error: result.error };\n return {\n ok: true,\n limit: result.data.limits?.requests?.limit ?? null,\n hardCap: result.data.limits?.requests?.hard_cap ?? null,\n windowSeconds: result.data.limits?.requests?.window_seconds ?? null,\n };\n}\n","// UmansUsageClient — accumulates /v1/usage snapshots with periodic refresh.\n// Owns mutable state (snapshot, timer, fetching flag) and the onChange callback.\n// Delegates parsing to ./parser.js and one-shot limit fetches to ./reconciler.js.\n\nimport { createLogger } from \"../logger.js\";\nimport type { ProxyConfig, UsageSnapshot } from \"../types.js\";\nimport { type RawUsage, fetchUsageRaw } from \"./fetch-usage.js\";\nimport { buildSnapshot, failSafeSnapshot } from \"./parser.js\";\nimport { fetchConcurrencyLimits, fetchRequestsLimits } from \"./reconciler.js\";\n\nconst log = createLogger(\"usage\");\n\nexport class UmansUsageClient {\n private snapshot: UsageSnapshot | null = null;\n private timer: ReturnType<typeof setInterval> | null = null;\n private fetching = false;\n private readonly target: string;\n private readonly apiKey: string | null;\n private readonly refreshMs: number;\n private onChangeCb: ((s: UsageSnapshot) => void) | null = null;\n\n constructor(config: Pick<ProxyConfig, \"target\" | \"umansApiKey\" | \"usageRefreshMs\">) {\n this.target = config.target.replace(/\\/+$/, \"\");\n this.apiKey = config.umansApiKey;\n this.refreshMs = config.usageRefreshMs;\n }\n\n onChange(cb: (s: UsageSnapshot) => void): void {\n this.onChangeCb = cb;\n }\n\n start(): void {\n if (!this.apiKey || this.timer) return;\n void this.refresh();\n this.timer = setInterval(() => void this.refresh(), this.refreshMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n getSnapshot(): UsageSnapshot {\n if (this.snapshot) return this.snapshot;\n return failSafeSnapshot();\n }\n\n async refresh(): Promise<void> {\n if (this.fetching || !this.apiKey) return;\n this.fetching = true;\n try {\n const result = await fetchUsageRaw(this.target, this.apiKey);\n if (!result.ok) {\n this.applyFailedSnapshot(result.error);\n return;\n }\n this.applySnapshot(result.data, true);\n } finally {\n this.fetching = false;\n }\n }\n\n /** One-shot fetch of /v1/usage to extract concurrency hard_cap + soft_limit.\n * Does NOT update live snapshot. */\n async fetchLimitsFromSource(): Promise<\n { ok: true; hardCap: number; softLimit: number } | { ok: false; error: string }\n > {\n return fetchConcurrencyLimits(this.target, this.apiKey);\n }\n\n /** One-shot fetch of /v1/usage to extract requests limits for rate_limit_requests validation.\n * Returns {limit, hardCap, windowSeconds} or null if not set (unlimited). */\n async fetchRequestsLimit(): Promise<\n | { ok: true; limit: number | null; hardCap: number | null; windowSeconds: number | null }\n | { ok: false; error: string }\n > {\n return fetchRequestsLimits(this.target, this.apiKey);\n }\n\n private applySnapshot(raw: RawUsage, ok: boolean): void {\n const lastHardCap = this.snapshot?.concurrencyHardCap ?? 1;\n const lastSoftLimit = this.snapshot?.concurrencySoftLimit ?? 1;\n const snap = buildSnapshot(raw, ok, lastHardCap, lastSoftLimit);\n this.snapshot = snap;\n this.onChangeCb?.(snap);\n }\n\n private applyFailedSnapshot(reason: string): void {\n if (this.snapshot) {\n this.snapshot = { ...this.snapshot, ok: false, fetchedAt: Date.now() };\n } else {\n this.snapshot = this.getSnapshot();\n this.snapshot.ok = false;\n }\n this.onChangeCb?.(this.snapshot);\n log.error(`fetch failed: ${reason}`);\n }\n}\n","// Viewer router — serves the inspector dashboard and REST API.\n// All routes are under the /dashboard prefix.\n//\n// Asset resolution order:\n// 1. dashboard/dist/ (production build from Vite)\n// 2. 404\n\nimport {\n type ReloadResult,\n readConfigFile,\n resetConfig,\n saveConfig,\n validateConfig,\n} from \"./config.js\";\nimport type { RawConfigInput } from \"./config.js\";\nimport type { CaptureDB } from \"./db.js\";\nimport {\n getAvailableMonths,\n getDailyUsage,\n getMonthSummary,\n getPricingTable,\n} from \"./economics.js\";\nimport { summary } from \"./helpers.js\";\nimport type { ConcurrencyGate } from \"./limiter/index.js\";\nimport type { ModelsClient } from \"./models.js\";\nimport type { ProxyConfig } from \"./types.js\";\nimport type { UmansUsageClient } from \"./usage.js\";\nimport type { VisionHandoff } from \"./vision/handoff.js\";\nimport type { WsBroadcaster } from \"./ws.js\";\n\n// Embedded assets: in compiled executables, Bun.embeddedFiles exposes imported\n// `with { type: \"file\" }` assets as Blobs. In dev mode, it's an empty array.\n// This allows the dashboard to work inside standalone compiled executables.\nimport { embeddedFiles } from \"bun\";\nimport { EMBEDDED_ASSET_PATHS } from \"./embedded-assets.js\";\n\ninterface NamedBlob extends Blob {\n name: string;\n}\n\n// Prevent tree-shaking of the embedded asset imports.\n// EMBEDDED_ASSET_PATHS is referenced here so the bundler keeps the side-effect imports.\nvoid EMBEDDED_ASSET_PATHS;\n\n// Module-level: populated only in compiled executables.\n// In dev mode, Bun.embeddedFiles is [] (empty array), so this stays null.\nconst EMBEDDED_ASSETS: Map<string, Blob> | null = (() => {\n try {\n if (!embeddedFiles || embeddedFiles.length === 0) return null;\n const map = new Map<string, Blob>();\n for (const blob of embeddedFiles as NamedBlob[]) {\n // blob.name is an internal Bun virtual FS path like /$bunfs/root/dashboard/dist/index.html\n // Strip the prefix to get the relative path within dashboard/dist/\n const name = blob.name.replace(/^.*\\/dashboard\\/dist\\//, \"\");\n if (name) {\n // Also store a de-hashed version (Bun adds hash suffix: index-a1b2c3d4.html → index.html)\n const dehashed = name.replace(/-[a-f0-9]{6,}(\\.[^.]+)$/, \"$1\");\n map.set(name, blob);\n if (dehashed !== name) map.set(dehashed, blob);\n }\n }\n return map.size > 0 ? map : null;\n } catch {\n return null;\n }\n})();\n\nexport interface CreateViewerRouterOptions {\n db: CaptureDB;\n ws: WsBroadcaster;\n config: ProxyConfig;\n gate: ConcurrencyGate;\n usage: UmansUsageClient;\n vision: VisionHandoff | null;\n models: ModelsClient | null;\n reloadConfig?: () => ReloadResult;\n refreshLimits?: () => Promise<\n | {\n ok: true;\n hardCap: number;\n softLimit: number;\n requestsLimit: number | null;\n requestsHardCap: number | null;\n requestsWindowSeconds: number | null;\n }\n | { ok: false; error: string }\n >;\n restart?: () => void;\n}\n\n/** Content type map for common static file extensions. */\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"application/javascript; charset=utf-8\",\n \".mjs\": \"application/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\n/** Resolve content type from file path. */\nfunction contentTypeFor(path: string): string {\n for (const [ext, ct] of Object.entries(CONTENT_TYPES)) {\n if (path.endsWith(ext)) return ct;\n }\n return \"application/octet-stream\";\n}\n\n/**\n * Try to resolve a static file from multiple candidate directories.\n * Returns the Bun file if found, null otherwise.\n *\n * Security: after resolving the relative path against each candidate base,\n * verify the resolved URL remains inside the base directory. This is an\n * explicit containment check that does not rely on URL-parser normalization\n * or Bun.file's own file-URL validation to prevent path traversal.\n */\nasync function resolveStaticFile(\n relativePath: string,\n candidates: URL[],\n): Promise<Response | null> {\n if (EMBEDDED_ASSETS) {\n const dehashed = relativePath.replace(/-[a-f0-9]{6,}(\\.[^.]+)$/, \"$1\");\n const blob = EMBEDDED_ASSETS.get(relativePath) ?? EMBEDDED_ASSETS.get(dehashed);\n if (blob) {\n return new Response(blob, {\n headers: { \"content-type\": contentTypeFor(relativePath) },\n });\n }\n }\n for (const base of candidates) {\n const fileUrl = new URL(relativePath, base);\n // Reject any resolved path that escapes the base directory.\n // base.href always ends with \"/\" (constructed with trailing slash),\n // so a contained fileUrl must start with base.href.\n if (!fileUrl.href.startsWith(base.href)) continue;\n try {\n const file = Bun.file(fileUrl);\n if (await file.exists()) {\n return new Response(file, {\n headers: { \"content-type\": contentTypeFor(relativePath) },\n });\n }\n } catch {\n // Bun.file rejects malformed file URLs (e.g. encoded path\n // separators like %2f). Treat as \"not found\" and continue.\n }\n }\n return null;\n}\n\n/** Result of matching a RegExp route pattern; `match[1]` is the capture id. */\ntype RegExpMatch = RegExpMatchArray;\n\n/** All dependencies a route handler needs from the viewer router closure. */\ninterface ViewerRouteContext {\n url: URL;\n req: Request;\n pathname: string;\n /** Present only when the matched route's pattern is a RegExp. */\n match: RegExpMatch | null;\n db: CaptureDB;\n ws: WsBroadcaster;\n config: ProxyConfig;\n gate: ConcurrencyGate;\n usage: UmansUsageClient;\n vision: VisionHandoff | null;\n models: ModelsClient | null;\n reloadConfig: (() => ReloadResult) | null;\n refreshLimits:\n | (() => Promise<\n | {\n ok: true;\n hardCap: number;\n softLimit: number;\n requestsLimit: number | null;\n requestsHardCap: number | null;\n requestsWindowSeconds: number | null;\n }\n | { ok: false; error: string }\n >)\n | null;\n restart: (() => void) | null;\n}\n\n/** A single route in the viewer dispatch table. */\ninterface ViewerRoute {\n method: string;\n pattern: string | RegExp;\n handler: (ctx: ViewerRouteContext) => Response | Promise<Response>;\n}\n\n/**\n * Create the viewer router that handles static asset serving and the REST API.\n * Returns null if the request is not a viewer route (caller should proxy it).\n */\nexport function createViewerRouter(options: CreateViewerRouterOptions) {\n const { db, ws, config, gate, usage, vision, models, reloadConfig, refreshLimits, restart } =\n options;\n const VIEWER = config.viewerPrefix;\n const DETAIL_RE = new RegExp(`^${VIEWER}/api/captures/(\\\\d+)$`);\n\n // Candidate directories for static assets, in priority order.\n // When running from src/ (dev): dashboard/dist/ is in project root.\n // When running from dist/ (built): dashboard/dist/ is a sibling to dist/.\n const assetBases: URL[] = [\n // dashboard/dist/ relative to src/ (dev mode)\n new URL(\"../../dashboard/dist/\", import.meta.url),\n // dashboard/dist/ relative to dist/ (built mode)\n new URL(\"../dashboard/dist/\", import.meta.url),\n ];\n\n // Route table — exact priority order of the former if-chain.\n // API routes first, then the DETAIL_RE regex route, then static-file/SPA\n // fallback is handled after the loop (see handleViewer).\n const ROUTES: ViewerRoute[] = [\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/captures`,\n handler: (ctx) => {\n const limit = Math.min(Number(ctx.url.searchParams.get(\"limit\") ?? 200), 1000);\n const rows = ctx.db.list(limit);\n return Response.json(rows.map(summary));\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/clear`,\n handler: (ctx) => {\n ctx.db.clear();\n ctx.vision?.clearRecords();\n ctx.ws.broadcast({ type: \"clear\" });\n return Response.json({ ok: true });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/gate`,\n handler: (ctx) => Response.json(ctx.gate.getStats(ctx.usage.getSnapshot())),\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/usage`,\n handler: (ctx) => Response.json(ctx.usage.getSnapshot()),\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/models`,\n handler: (ctx) => {\n if (!ctx.models) return Response.json({ models: [], fetched_at: 0, ok: false });\n return Response.json({\n models: ctx.models.list(),\n fetched_at: ctx.models.lastFetchedAt(),\n ok: ctx.models.healthy(),\n });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/performance`,\n handler: (ctx) => Response.json(ctx.db.getPerformanceStats()),\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/vision-calls`,\n handler: (ctx) => {\n const limit = Math.min(Number(ctx.url.searchParams.get(\"limit\") ?? 100), 500);\n return Response.json(ctx.db.getVisionCallRecords(limit));\n },\n },\n {\n method: \"DELETE\",\n pattern: `${VIEWER}/api/vision-calls`,\n handler: (ctx) => {\n ctx.vision?.clearRecords();\n ctx.ws.broadcast({ type: \"vision-clear\" });\n return Response.json({ ok: true });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/vision-cache-stats`,\n handler: (ctx) => Response.json(ctx.vision?.getCacheStats() ?? null),\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/config`,\n handler: (ctx) => {\n const raw = readConfigFile();\n const { umans_api_key: _omitted, ...safe } = raw;\n // Use the resolved config's umansApiKey (env > file) so has_api_key\n // is true even when the key is set via UMANS_API_KEY env var only.\n return Response.json({ ...safe, has_api_key: Boolean(ctx.config.umansApiKey) });\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/config`,\n handler: async (ctx) => {\n try {\n const body = (await ctx.req.json()) as RawConfigInput;\n const result = saveConfig(body);\n return Response.json(result, { status: result.ok ? 200 : 400 });\n } catch (e) {\n return Response.json(\n {\n ok: false,\n errors: [`Invalid JSON body: ${e instanceof Error ? e.message : String(e)}`],\n warnings: [],\n written: null,\n },\n { status: 400 },\n );\n }\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/config/reset`,\n handler: async (ctx) => {\n const result = resetConfig();\n if (!result.ok) {\n return Response.json(result, { status: 500 });\n }\n if (ctx.refreshLimits) {\n const limits = await ctx.refreshLimits();\n return Response.json({\n ...result,\n limits: limits.ok\n ? {\n hardCap: limits.hardCap,\n softLimit: limits.softLimit,\n requestsLimit: limits.requestsLimit ?? null,\n requestsHardCap: limits.requestsHardCap ?? null,\n requestsWindowSeconds: limits.requestsWindowSeconds ?? null,\n }\n : null,\n });\n }\n return Response.json(result);\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/config/validate`,\n handler: async (ctx) => {\n try {\n const body = (await ctx.req.json()) as RawConfigInput;\n const result = validateConfig(body);\n return Response.json({ ok: result.ok, errors: result.errors, warnings: result.warnings });\n } catch (e) {\n return Response.json(\n {\n ok: false,\n errors: [`Invalid JSON body: ${e instanceof Error ? e.message : String(e)}`],\n warnings: [],\n },\n { status: 400 },\n );\n }\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/config/reload`,\n handler: (ctx) => {\n if (!ctx.reloadConfig) {\n return Response.json(\n {\n ok: false,\n errors: [\"Reload not available\"],\n warnings: [],\n applied: [],\n restartRequired: [],\n configPath: \"\",\n },\n { status: 501 },\n );\n }\n const result = ctx.reloadConfig();\n return Response.json(result);\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/usage/refresh-source`,\n handler: async (ctx) => {\n if (!ctx.refreshLimits) {\n return Response.json({ ok: false, error: \"Refresh not available\" }, { status: 501 });\n }\n const result = await ctx.refreshLimits();\n return Response.json(result);\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/restart`,\n handler: (ctx) => {\n const restart = ctx.restart;\n if (!restart) {\n return Response.json({ ok: false, error: \"Restart not available\" }, { status: 501 });\n }\n setTimeout(() => restart(), 100);\n return Response.json({\n ok: true,\n message:\n \"Server is restarting. Requires a process manager (bun --watch, systemd, pm2) to auto-restart — otherwise the server exits and stays down.\",\n });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/economics/summary`,\n handler: (ctx) => {\n const year = Number(ctx.url.searchParams.get(\"year\") ?? new Date().getFullYear());\n const month = Number(ctx.url.searchParams.get(\"month\") ?? new Date().getMonth() + 1);\n const summaryData = getMonthSummary(ctx.db.rawDb, year, month);\n const months = getAvailableMonths(ctx.db.rawDb);\n const pricing = getPricingTable(ctx.db.rawDb);\n return Response.json({ summary: summaryData, months, pricing });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/economics/daily`,\n handler: (ctx) => {\n const limit = Math.min(Number(ctx.url.searchParams.get(\"limit\") ?? 90), 365);\n const rows = getDailyUsage(ctx.db.rawDb, limit);\n return Response.json(rows);\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/economics/pricing`,\n handler: (ctx) => Response.json(getPricingTable(ctx.db.rawDb)),\n },\n // DETAIL_RE regex route — must come after all exact-match API routes and\n // before static file fallback. Uses the capture id from match[1].\n {\n method: \"GET\",\n pattern: DETAIL_RE,\n handler: (ctx) => {\n const m = ctx.match;\n if (!m) return new Response(\"not found\", { status: 404 });\n const row = ctx.db.get(Number(m[1]));\n if (!row) return new Response(\"not found\", { status: 404 });\n return Response.json(row);\n },\n },\n ];\n\n /**\n * Handle a viewer request. Returns null for non-viewer paths (caller proxies).\n */\n async function handleViewer(url: URL, req: Request): Promise<Response | null> {\n const p = url.pathname;\n\n // WebSocket upgrade is handled by the server, not here.\n if (p === `${VIEWER}/ws`) return null;\n\n // Build context once for all routes.\n const ctx: ViewerRouteContext = {\n url,\n req,\n pathname: p,\n match: null,\n db,\n ws,\n config,\n gate,\n usage,\n vision,\n models,\n reloadConfig: reloadConfig ?? null,\n refreshLimits: refreshLimits ?? null,\n restart: restart ?? null,\n };\n\n // Dispatch: first matching route (method + pattern) wins.\n for (const route of ROUTES) {\n if (route.method !== req.method) continue;\n\n if (route.pattern instanceof RegExp) {\n const m = p.match(route.pattern);\n if (m) {\n ctx.match = m;\n return route.handler(ctx);\n }\n } else if (p === route.pattern) {\n return route.handler(ctx);\n }\n }\n\n // Static assets: serve from dashboard/dist/\n // Map /dashboard/ → index.html, /dashboard/assets/* → assets/*\n if (p === VIEWER || p === `${VIEWER}/`) {\n const resp = await resolveStaticFile(\"index.html\", assetBases);\n if (resp) {\n resp.headers.set(\"cache-control\", \"no-cache, no-store, must-revalidate\");\n return resp;\n }\n return new Response(\"dashboard not built. Run: cd dashboard && bun run build\", {\n status: 404,\n });\n }\n\n // Other static asset paths under /dashboard/\n if (p.startsWith(`${VIEWER}/`)) {\n const relativePath = p.slice(`${VIEWER}/`.length);\n\n // Try to serve from asset bases\n const resp = await resolveStaticFile(relativePath, assetBases);\n if (resp) return resp;\n\n // SPA fallback: if no asset matches, serve index.html (for client-side routing)\n // But only for non-API, non-asset paths\n if (!relativePath.startsWith(\"api/\") && !relativePath.includes(\".\")) {\n const spaResp = await resolveStaticFile(\"index.html\", assetBases);\n if (spaResp) {\n spaResp.headers.set(\"cache-control\", \"no-cache, no-store, must-revalidate\");\n return spaResp;\n }\n }\n\n return new Response(\"not found\", { status: 404 });\n }\n\n return new Response(\"not found\", { status: 404 });\n }\n\n return { handleViewer, VIEWER };\n}\n","// Description LRU cache + SHA-256 key generation for vision handoff.\n// In-memory only; entries are lost on restart. Backed by lru-cache v11.\n\nimport { LRUCache } from \"lru-cache\";\nimport type { PersistentDescriptionStore } from \"./persistent-cache.js\";\n\nexport interface CompressionRecipe {\n format: string;\n quality: number;\n max_dimension: number;\n}\n\n/**\n * Stable image-hash key over (original_bytes || canonical_settings || encoder_version).\n * Uses Bun's built-in CryptoHasher — faster than node:crypto.\n */\nexport function imageCacheKey(\n bytes: Buffer | Uint8Array,\n recipe: CompressionRecipe,\n encoderVersion: string,\n): string {\n const h = new Bun.CryptoHasher(\"sha256\");\n h.update(bytes);\n h.update(JSON.stringify(recipe));\n h.update(encoderVersion);\n return h.digest(\"hex\");\n}\n\n/**\n * Description-cache key over (image_hash, prompt_version, model_id).\n * Description text is a VALUE, not part of the KEY.\n */\nexport function descriptionCacheKey(\n bytes: Buffer | Uint8Array,\n recipe: CompressionRecipe,\n encoderVersion: string,\n modelId: string,\n promptVersion: number,\n): string {\n const base = imageCacheKey(bytes, recipe, encoderVersion);\n const h = new Bun.CryptoHasher(\"sha256\");\n h.update(base);\n h.update(`|pv=${promptVersion}|model=${modelId}`);\n return h.digest(\"hex\");\n}\n\n/** Stats surfaced by {@link DescriptionCache}. */\nexport interface DescriptionCacheStats {\n hits: number;\n misses: number;\n evictions: number;\n size: number;\n}\n\n/**\n * Synchronous LRU description cache. Lets the VisionHandoff orchestrator\n * check for a hit before issuing an async vision-model call.\n *\n * `getOrCompute` is intentionally SYNCHRONOUS: JS event loop is single\n * threaded, so no locking is needed; the orchestrator performs async work\n * OUTSIDE the cache and stores the result via a second call on hit-less\n * paths (or by supplying a `compute` that has already resolved).\n */\nexport class DescriptionCache {\n private readonly cache: LRUCache<string, string>;\n private readonly persistent: PersistentDescriptionStore | null;\n private hits = 0;\n private misses = 0;\n private evictions = 0;\n private persistentHits = 0;\n private persistentWrites = 0;\n\n constructor(maxSize: number, ttlMs: number, persistent?: PersistentDescriptionStore | null) {\n this.persistent = persistent ?? null;\n this.cache = new LRUCache<string, string>({\n max: maxSize,\n ttl: ttlMs,\n dispose: (_value, _key, reason) => {\n if (reason === \"evict\" || reason === \"expire\") this.evictions++;\n },\n });\n }\n\n get size(): number {\n return this.cache.size;\n }\n\n get stats(): DescriptionCacheStats {\n return {\n hits: this.hits,\n misses: this.misses,\n evictions: this.evictions,\n size: this.cache.size,\n };\n }\n\n get persistentStats(): { hits: number; writes: number } {\n return { hits: this.persistentHits, writes: this.persistentWrites };\n }\n\n warm(key: string, description: string): void {\n this.cache.set(key, description);\n }\n\n getOrCompute(\n bytes: Buffer | Uint8Array,\n recipe: CompressionRecipe,\n encoderVersion: string,\n modelId: string,\n promptVersion: number,\n compute: () => string,\n ): string {\n const key = descriptionCacheKey(bytes, recipe, encoderVersion, modelId, promptVersion);\n if (this.cache.has(key)) {\n const v = this.cache.get(key);\n if (v !== undefined) {\n this.hits++;\n return v;\n }\n }\n\n if (this.persistent) {\n const persisted = this.persistent.get(key);\n if (persisted !== null) {\n this.persistentHits++;\n this.cache.set(key, persisted);\n this.hits++;\n return persisted;\n }\n }\n\n const value = compute();\n this.cache.set(key, value);\n this.misses++;\n\n if (this.persistent) {\n const imageHash = imageCacheKey(bytes, recipe, encoderVersion);\n this.persistent.set({\n key,\n description: value,\n imageHash,\n model: modelId,\n promptVersion,\n });\n this.persistentWrites++;\n }\n return value;\n }\n}\n","// Image block detection + byte scan for LLM request bodies.\n// Walks OpenAI image_url parts and Anthropic image blocks, plus a cheap\n// regex fast-path that avoids full JSON deserialization when no image is present.\n\n/** Detected image part, normalized across OpenAI and Anthropic shapes. */\nexport interface ImagePart {\n mediaType: string | null;\n encoding: \"base64\" | \"url\";\n data: string;\n}\n\n/** Tristate capability: true = supports vision, false = does not, \"via-handoff\" = only via handoff. */\nexport type VisionTristate = true | false | \"via-handoff\";\n\n/** Lookup interface for vision-capability queries. Implemented by ModelsClient + ModelInfoClient. */\nexport interface VisionLookup {\n getVisionSupport(modelName: string): VisionTristate | null;\n}\n\n/** Which API shape the body follows. */\nexport type ApiKind = \"openai\" | \"anthropic\";\n\n/** Rewrite strategy from config. */\nexport type RewriteStrategy = \"never\" | \"catalog\" | \"always\";\n\n/** OpenAI content[] walker: collects every image_url part. */\nexport function findOpenAIImageParts(body: unknown): ImagePart[] {\n const out: ImagePart[] = [];\n if (typeof body !== \"object\" || body === null) return out;\n const messages = (body as { messages?: unknown }).messages;\n if (!Array.isArray(messages)) return out;\n for (const msg of messages) {\n if (typeof msg !== \"object\" || msg === null) continue;\n const content = (msg as { content?: unknown }).content;\n if (!Array.isArray(content)) continue;\n for (const part of content) {\n if (typeof part !== \"object\" || part === null) continue;\n if ((part as { type?: unknown }).type !== \"image_url\") continue;\n const imageUrl = (part as { image_url?: { url?: unknown } }).image_url;\n const url = typeof imageUrl?.url === \"string\" ? imageUrl.url : \"\";\n if (!url) continue;\n const m = /^data:([^;]+);base64,(.+)$/.exec(url);\n if (m) {\n out.push({ mediaType: m[1], encoding: \"base64\", data: m[2] });\n } else {\n out.push({ mediaType: inferExtFromUrl(url), encoding: \"url\", data: url });\n }\n }\n }\n return out;\n}\n\n/** Anthropic content[] walker: recurses into messages[].content[] and tool_result.content[]. */\nexport function findAnthropicImageParts(body: unknown): ImagePart[] {\n const out: ImagePart[] = [];\n if (typeof body !== \"object\" || body === null) return out;\n const messages = (body as { messages?: unknown }).messages;\n if (!Array.isArray(messages)) return out;\n\n const walk = (arr: unknown[]) => {\n for (const part of arr) {\n if (typeof part !== \"object\" || part === null) continue;\n const p = part as { type?: unknown; source?: unknown; content?: unknown };\n if (p.type === \"image\" && p.source && typeof p.source === \"object\") {\n const src = p.source as {\n type?: unknown;\n media_type?: unknown;\n data?: unknown;\n url?: unknown;\n };\n if (src.type === \"base64\") {\n out.push({\n mediaType: typeof src.media_type === \"string\" ? src.media_type : null,\n encoding: \"base64\",\n data: typeof src.data === \"string\" ? src.data : \"\",\n });\n } else if (src.type === \"url\") {\n const url = typeof src.url === \"string\" ? src.url : \"\";\n out.push({\n mediaType: inferExtFromUrl(url),\n encoding: \"url\",\n data: url,\n });\n }\n } else if (p.type === \"tool_result\" && Array.isArray(p.content)) {\n walk(p.content);\n }\n }\n };\n\n for (const msg of messages) {\n if (typeof msg !== \"object\" || msg === null) continue;\n const content = (msg as { content?: unknown }).content;\n if (Array.isArray(content)) walk(content);\n }\n return out;\n}\n\n/**\n * Cheap signal: does the stringified body contain an image block?\n * Mirrors a memchr scan for `\"type\":\"image_url\"` or `\"type\":\"image\"` without\n * fully deserialising. The Rust equivalent would use `memchr::memmem::find`.\n */\nexport function cheapImageSignal(bodyStr: string): boolean {\n return (\n /\"type\"\\s*:\\s*\"image_url\"/.test(bodyStr) || /\"type\"\\s*:\\s*\"image\"\\s*,\\s*\"source\"/.test(bodyStr)\n );\n}\n\n/** Infer a media type from a URL's extension; null if unknown. */\nexport function inferExtFromUrl(url: string): string | null {\n const m = /\\.(png|jpe?g|webp|gif|bmp|tiff?|heic|heif|svg)$/i.exec(url);\n if (!m) return null;\n const ext = m[1].toLowerCase();\n if (ext === \"jpg\") return \"image/jpeg\";\n if (ext === \"tif\" || ext === \"tiff\") return \"image/tiff\";\n return `image/${ext}`;\n}\n\n/**\n * Tristate capability resolution. Decides whether the proxy should rewrite\n * an image-bearing request into a vision handoff.\n *\n * - `never` → never rewrite\n * - `always` → always rewrite\n * - `catalog` → consult the model's vision capability:\n * - supports_vision: false → rewrite (model can't see images)\n * - supports_vision: \"via-handoff\" → rewrite OpenAI only (server handles Anthropic)\n * - supports_vision: true → pass through, UNLESS forceInterceptCapable is set\n * - unknown (null) → pass through (fail-safe: don't intercept if uncertain)\n */\nexport function shouldRewrite(\n strategy: RewriteStrategy,\n supports: VisionTristate | null,\n apiKind: ApiKind,\n forceInterceptCapable = false,\n): boolean {\n if (strategy === \"never\") return false;\n if (strategy === \"always\") return true;\n // catalog\n if (supports === null) return false;\n if (supports === false) return true;\n if (supports === \"via-handoff\") return apiKind === \"openai\";\n // supports === true\n return forceInterceptCapable;\n}\n","// Image transcode: decode → resize → JPEG re-encode via Bun.Image.\n// Returns bytes + SHA-256 hash + output dimensions for cache keying.\n\n/** Options for {@link transcodeImage}. */\nexport interface TranscodeOptions {\n /** Longest side after resize; aspect ratio is preserved (fit: \"inside\"). */\n maxDimension: number;\n /** JPEG quality 1–100 (used only when format is \"jpeg\"). */\n quality: number;\n /** Output format: \"jpeg\" (lossy) or \"png\" (lossless). Default \"jpeg\". */\n format?: \"jpeg\" | \"png\";\n}\n\n/** Result of a successful transcode. */\nexport interface TranscodeResult {\n bytes: Uint8Array;\n /** SHA-256 hex of the encoded JPEG bytes (not the source). */\n hash: string;\n width: number;\n height: number;\n format: string;\n}\n\n/** Error codes callers can branch on for fail-open behaviour. */\nexport type TranscodeErrorCode = \"unsupported\" | \"decode_failed\" | \"encode_failed\" | \"too_large\";\n\n/**\n * Typed error thrown by {@link transcodeImage}. Callers should catch this\n * and treat the image as un-transcodable (fail-open) rather than crashing.\n */\nexport class TranscodeError extends Error {\n readonly code: TranscodeErrorCode;\n constructor(\n code: TranscodeErrorCode,\n message: string,\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"TranscodeError\";\n this.code = code;\n }\n}\n\n/**\n * Map a `Bun.Image` rejection's `error.code` to a {@link TranscodeErrorCode}.\n * Returns `null` for codes we don't classify (caller rethrows).\n */\nfunction classifyBunImageError(err: unknown): TranscodeErrorCode | null {\n if (typeof err !== \"object\" || err === null) return null;\n const code = (err as { code?: unknown }).code;\n switch (code) {\n case \"ERR_IMAGE_FORMAT_UNSUPPORTED\":\n case \"ERR_IMAGE_UNKNOWN_FORMAT\":\n return \"unsupported\";\n case \"ERR_IMAGE_DECODE_FAILED\":\n return \"decode_failed\";\n case \"ERR_IMAGE_ENCODE_FAILED\":\n return \"encode_failed\";\n case \"ERR_IMAGE_TOO_MANY_PIXELS\":\n return \"too_large\";\n default:\n return null;\n }\n}\n\n/**\n * Decode image bytes, resize to fit within `maxDimension` (preserving aspect\n * ratio), and re-encode as progressive JPEG quality `quality`. Returns the\n * encoded bytes, their SHA-256 hash, and the output dimensions.\n *\n * Errors from `Bun.Image` terminals are converted to {@link TranscodeError}\n * so the caller can fail-open without a `try/catch` wrapping every call.\n */\nexport async function transcodeImage(\n imageBytes: Uint8Array,\n opts: TranscodeOptions = { maxDimension: 1024, quality: 85 },\n): Promise<TranscodeResult> {\n const img = new Bun.Image(imageBytes, { maxPixels: 50_000_000 });\n try {\n await img.metadata();\n } catch (err) {\n const code = classifyBunImageError(err);\n if (code) {\n throw new TranscodeError(code, `image decode failed: ${code}`, err);\n }\n throw err;\n }\n\n img.resize(opts.maxDimension, opts.maxDimension, { fit: \"inside\", filter: \"lanczos3\" });\n\n const format = opts.format ?? \"jpeg\";\n let encoded: Uint8Array;\n try {\n if (format === \"png\") {\n encoded = await img.png().bytes();\n } else {\n encoded = await img.jpeg({ quality: opts.quality, progressive: true }).bytes();\n }\n } catch (err) {\n const code = classifyBunImageError(err);\n if (code) {\n throw new TranscodeError(code, `image encode failed: ${code}`, err);\n }\n throw err;\n }\n\n const hash = new Bun.CryptoHasher(\"sha256\").update(encoded).digest(\"hex\");\n return { bytes: encoded, hash, width: img.width, height: img.height, format };\n}\n","// Deterministic replacement-text wrapper + failure placeholders + format policies.\n// Mirrors umans-dash's label format so upstream KV-cache prefixes remain stable.\n// All output text is byte-identical for identical inputs — no dynamic metadata.\n\nimport type { ImagePart } from \"./detect.js\";\n\n/** Reason a vision analysis failed. Constrained to safe, non-leaky tokens. */\nexport type FailureReason = \"timeout\" | \"http_status\" | \"parse\" | \"empty\" | \"generic\" | \"aborted\";\n\n/** What to do with a given image format. */\nexport type FormatPolicy = \"pass\" | \"transcode\" | \"reject\";\n\n/**\n * Deterministic wrapper. Mirrors umans-dash's label format.\n *\n * The label is byte-identical for identical `desc` inputs — it never includes\n * timestamps, request IDs, image position, or any runtime metadata. The\n * active-model caveat is fixed text so upstream KV-cache prefixes stay stable.\n */\nexport function wrapDescription(desc: string): string {\n return `[Image content — analyzed by vision module, shown as text because the active model cannot see images:]\\n${desc}`;\n}\n\n/**\n * Failure placeholder. Always starts with `[Image analysis failed:` and ends\n * with a fixed model-caveat suffix. `detail` is interpolated only for the\n * safe-reason tokens (`timeout`, `http_status`, `generic`) — it must be a\n * sanitized scalar, never an upstream body or stack frame.\n */\nexport function failurePlaceholder(reason: FailureReason, detail: string): string {\n const reasonText: Record<FailureReason, string> = {\n timeout: `vision model timed out after ${detail}`,\n http_status: `vision model returned HTTP ${detail}`,\n parse: \"vision model returned an unparseable response\",\n empty: \"vision model returned an empty description\",\n generic: detail,\n aborted: \"client disconnected before vision model responded\",\n };\n return `[Image analysis failed: ${reasonText[reason]}. The active model cannot see this image.]`;\n}\n\n/**\n * Format policy: pass-through natively supported formats, transcode\n * bitmaps/heic/svg, reject anything else. Extension is lower-cased; mediaType\n * is accepted but the extension wins (mirrors PoC at L668-675).\n */\nexport function formatPolicy(ext: string, _mediaType: string): FormatPolicy {\n const supported = new Set([\"png\", \"jpg\", \"jpeg\", \"webp\", \"gif\"]);\n const transcodable = new Set([\"bmp\", \"tiff\", \"tif\", \"heic\", \"heif\", \"svg\"]);\n const e = ext.toLowerCase();\n if (supported.has(e)) return \"pass\";\n if (transcodable.has(e)) return \"transcode\";\n return \"reject\";\n}\n\n/**\n * Apply the max-images overflow policy.\n *\n * Image parts beyond `maxImages` are dropped from the `kept` set and each is\n * replaced with a deterministic placeholder in `overflow`. The placeholder\n * tells the model not to describe or reference the omitted image, preventing\n * hallucination about content the model never received.\n */\nexport function applyMaxImagesPolicy(\n parts: ImagePart[],\n maxImages: number,\n): { kept: ImagePart[]; overflow: string[] } {\n const kept = parts.slice(0, maxImages);\n const overflow = parts\n .slice(maxImages)\n .map(\n (_, i) =>\n `[Image omitted: not delivered to model — do not describe or reference it. (overflow ${i + 1})]`,\n );\n return { kept, overflow };\n}\n","// Vision handoff orchestrator.\n// Drives the full pipeline: detect images → check cache → transcode → call\n// vision model → wrap descriptions → replace image blocks in body.\n//\n// The orchestrator never holds the vision permit and the main proxy permit\n// at the same time: the caller (proxy.ts) acquires the vision permit, invokes\n// `processBody`, and only after it returns acquires the main upstream permit.\n// All I/O here is async; the synchronous `DescriptionCache` is consulted\n// before any network call so cache hits never touch the network.\n\nimport { flattenUsage } from \"../db.js\";\nimport type { CaptureDB } from \"../db.js\";\nimport { headersToObject, redactHeaders } from \"../helpers.js\";\nimport { ConcurrencyGate } from \"../limiter/index.js\";\nimport { createLogger } from \"../logger.js\";\nimport type { CaptureState, ProtocolConfig } from \"../types.js\";\nimport { extractOpenAiNonStreaming } from \"../usage/extract.js\";\nimport type { UsageMetrics } from \"../usage/types.js\";\nimport type { CompressionRecipe, DescriptionCache } from \"./cache.js\";\nimport { descriptionCacheKey } from \"./cache.js\";\nimport type { ApiKind, ImagePart, VisionLookup, VisionTristate } from \"./detect.js\";\nimport {\n cheapImageSignal,\n findAnthropicImageParts,\n findOpenAIImageParts,\n shouldRewrite,\n} from \"./detect.js\";\nimport type { VisionHttpExchange, VisionRecordSink } from \"./sink.js\";\nimport { TranscodeError, transcodeImage } from \"./transcode.js\";\nimport { applyMaxImagesPolicy, failurePlaceholder, wrapDescription } from \"./wrapper.js\";\n\nconst log = createLogger(\"vision\");\n\n/** Strategy for when to rewrite image-bearing requests. */\ntype VisionStrategy = \"never\" | \"catalog\" | \"always\";\n\nconst DEFAULT_VISION_BREAKER_THRESHOLD = 100;\nconst DEFAULT_VISION_BREAKER_WINDOW_MS = 5000;\nconst DEFAULT_VISION_BREAKER_COOLDOWN_MS = 1000;\nconst DEFAULT_VISION_MAX_QUEUE_DEPTH = 256;\nconst DEFAULT_VISION_QUEUE_TIMEOUT_MS = 30000;\n\n/**\n * Vision-handoff configuration extracted from {@link ProxyConfig} fields.\n * `target` / `model` / `apiKey` are nullable so the caller can disable\n * handoff by leaving them unset without constructing an invalid config.\n */\nexport interface VisionConfig {\n strategy: VisionStrategy;\n target: string | null;\n model: string | null;\n prompt: string;\n promptVersion: number;\n maxImages: number;\n maxDescriptionTokens: number;\n reasoningEffort: \"none\" | \"low\" | \"medium\" | \"high\" | null;\n timeoutMs: number;\n cacheSize: number;\n cacheTtlMs: number;\n cacheMaxRows: number;\n persistentCache: boolean;\n apiKey: string | null;\n forceInterceptCapable: boolean;\n concurrency: number;\n maxDimension: number;\n jpegQuality: number;\n imageFormat: \"jpeg\" | \"png\";\n imageDetail: \"auto\" | \"low\" | \"high\";\n /** Weight used when acquiring a vision permit from the shared concurrency gate. */\n visionWeight: number;\n /** Circuit-breaker failure threshold for the fallback concurrency gate. */\n breakerThreshold?: number;\n /** Window over which circuit-breaker failures are counted. */\n breakerWindowMs?: number;\n /** Cooldown before the circuit breaker allows traffic again. */\n breakerCooldownMs?: number;\n /** Maximum queued permits for the fallback concurrency gate. */\n maxQueueDepth?: number;\n /** Timeout for queued permits waiting for a free slot. */\n queueTimeoutMs?: number;\n /** When true, cache misses forward the original body immediately and process vision in the background. */\n backgroundVision: boolean;\n}\n\n/** Per-call statistics surfaced back to the caller. */\ninterface VisionStats {\n handoffCount: number;\n cacheHits: number;\n cacheMisses: number;\n visionCalls: number;\n latencyMs: number[];\n}\n\n/** A recorded vision call for debugging and dashboard visibility. */\nexport interface VisionCallRecord {\n id: number;\n timestamp: number;\n captureId: number | null;\n model: string;\n target: string;\n imageSize: number;\n imageHash: string | null;\n status:\n | \"ok\"\n | \"cache_hit\"\n | \"http_error\"\n | \"timeout\"\n | \"fetch_error\"\n | \"parse_error\"\n | \"empty\"\n | \"skipped\"\n | \"aborted\";\n httpStatus: number | null;\n latencyMs: number;\n description: string;\n error: string | null;\n incomingProtocol: string;\n upstreamProtocol: string;\n state: CaptureState;\n}\n\n/** Result of {@link processBody}. */\nexport interface ProcessBodyResult {\n body: unknown;\n changed: boolean;\n stats: VisionStats;\n}\n\n/** Per-image result from processImage — assembled by processBody into stats. */\ninterface ImageProcessResult {\n description: string;\n cacheHit: boolean;\n cacheMiss: boolean;\n visionCall: boolean;\n latencyMs: number;\n}\n\n/** Encoder version tag mixed into the cache key. Bump when transcode output bytes change. */\nconst ENCODER_VERSION = \"bun-image-v2\";\n\n/**\n * Sentinel value returned (via throw) from {@link DescriptionCache.getOrCompute}\n * when a vision description is not found in either the LRU or persistent cache.\n *\n * Callers should treat this as a cache miss and proceed to invoke the upstream\n * vision model. The value is always empty (`\"\"`) after the miss is caught.\n *\n * This is a unique `Symbol` rather than `undefined` or `null` so it can never\n * collide with a legitimate cached string (including empty descriptions) and\n * remains detectable across the `getOrCompute` callback boundary.\n */\nconst CACHE_MISS = Symbol(\"cache-miss\");\n\nfunction recipeFromConfig(cfg: VisionConfig): CompressionRecipe {\n return {\n format: cfg.imageFormat,\n quality: cfg.jpegQuality,\n max_dimension: cfg.maxDimension,\n };\n}\n\n/**\n * Orchestrates the vision handoff pipeline for a single request body.\n *\n * One instance is cheap to construct and holds no per-call state beyond the\n * constructor-supplied config and cache, so it can be reused across requests\n * or created per-request. The cache is shared by reference.\n */\nexport class VisionHandoff {\n private records: VisionCallRecord[] = [];\n private nextRecordId = 1;\n private readonly maxRecords: number;\n private readonly gate: ConcurrencyGate;\n private readonly inflight = new Map<string, Promise<string>>();\n\n constructor(\n private readonly config: VisionConfig,\n private readonly cache: DescriptionCache,\n private readonly catalog: VisionLookup | null = null,\n gate?: ConcurrencyGate,\n private readonly db?: CaptureDB,\n private readonly sink?: VisionRecordSink,\n private readonly protocolConfig: ProtocolConfig = {\n incomingProtocol: \"http1.1\",\n upstreamProtocol: \"http1.1\",\n upstreamTimeoutMs: 300000,\n },\n ) {\n this.maxRecords = Math.max(config.cacheSize, 200);\n this.gate =\n gate ??\n new ConcurrencyGate({\n hardCap: Math.max(1, config.concurrency),\n softLimit: Math.max(1, config.concurrency),\n releaseCooldownMs: 0,\n breakerThreshold: config.breakerThreshold ?? DEFAULT_VISION_BREAKER_THRESHOLD,\n breakerWindowMs: config.breakerWindowMs ?? DEFAULT_VISION_BREAKER_WINDOW_MS,\n breakerCooldownMs: config.breakerCooldownMs ?? DEFAULT_VISION_BREAKER_COOLDOWN_MS,\n maxQueueDepth: config.maxQueueDepth ?? DEFAULT_VISION_MAX_QUEUE_DEPTH,\n queueTimeoutMs: config.queueTimeoutMs ?? DEFAULT_VISION_QUEUE_TIMEOUT_MS,\n intentions: { vision: config.concurrency },\n });\n }\n\n get visionActive(): number {\n return this.gate.getIntentionActive(\"vision\");\n }\n\n get visionQueued(): number {\n return this.gate.getIntentionQueued(\"vision\");\n }\n\n getCacheStats(): {\n lru: { hits: number; misses: number; evictions: number; size: number };\n persistent: { hits: number; writes: number };\n } {\n return {\n lru: this.cache.stats,\n persistent: this.cache.persistentStats,\n };\n }\n\n /** Return recent vision call records (newest first). */\n getRecords(limit = 100): VisionCallRecord[] {\n return this.records.slice(-limit).reverse();\n }\n\n /** Clear all stored vision call records. */\n clearRecords(): void {\n this.records = [];\n this.nextRecordId = 1;\n this.db?.clearVisionCaptures();\n }\n\n private addRecord(\n rec: Omit<\n VisionCallRecord,\n \"id\" | \"timestamp\" | \"incomingProtocol\" | \"upstreamProtocol\" | \"state\"\n >,\n httpExchange?: VisionHttpExchange,\n usage: UsageMetrics | null = null,\n dbId?: number,\n ): void {\n const now = Date.now();\n const full: VisionCallRecord = {\n ...rec,\n id: this.nextRecordId++,\n timestamp: now,\n incomingProtocol: this.protocolConfig.incomingProtocol,\n upstreamProtocol: this.protocolConfig.upstreamProtocol,\n state: \"done\",\n };\n this.records.push(full);\n if (this.records.length > this.maxRecords) {\n this.records.splice(0, this.records.length - this.maxRecords);\n }\n\n this.sink?.record({ rec: full, httpExchange, usage, dbId });\n }\n\n /**\n * Process a request body: detect images, transcode + describe each via the\n * vision model (or serve from cache), and replace image blocks in the body\n * with text blocks containing the wrapped descriptions.\n *\n * The original `body` is never mutated — a deep clone is produced first.\n * Fail-open: on any per-image error (transcode, timeout, HTTP, parse), the\n * image is replaced with a deterministic failure placeholder rather than\n * aborting the whole request.\n *\n * If a model name and catalog are provided, the catalog strategy consults\n * the model's vision capability to decide whether to intercept.\n */\n async processBody(\n body: unknown,\n apiKind: ApiKind,\n modelName?: string,\n captureId?: number,\n signal?: AbortSignal,\n ): Promise<ProcessBodyResult> {\n const stats: VisionStats = {\n handoffCount: 0,\n cacheHits: 0,\n cacheMisses: 0,\n visionCalls: 0,\n latencyMs: [],\n };\n\n // 0. Catalog-aware gate: if we know the model and it supports vision,\n // decide whether to intercept based on strategy + forceInterceptCapable.\n if (this.config.strategy === \"catalog\" && modelName && this.catalog) {\n const supports: VisionTristate | null = this.catalog.getVisionSupport(modelName);\n if (\n !shouldRewrite(this.config.strategy, supports, apiKind, this.config.forceInterceptCapable)\n ) {\n return { body, changed: false, stats };\n }\n }\n\n // 1. Cheap signal: skip full parse when no image block can be present.\n const bodyStr = typeof body === \"string\" ? body : JSON.stringify(body);\n if (!cheapImageSignal(bodyStr)) {\n return { body, changed: false, stats };\n }\n\n // 2. Extract image parts per API shape.\n const parts =\n apiKind === \"anthropic\" ? findAnthropicImageParts(body) : findOpenAIImageParts(body);\n if (parts.length === 0) {\n return { body, changed: false, stats };\n }\n\n // 3. Cap image count; overflow becomes deterministic placeholders.\n const { kept, overflow } = applyMaxImagesPolicy(parts, this.config.maxImages);\n stats.handoffCount = kept.length;\n\n // 4. Clone the body so the original is never mutated.\n const mutated = cloneBody(body);\n\n // 5. For each kept image: transcode → cache lookup → vision call → wrap.\n // Processed in parallel; the concurrency gate serializes vision calls.\n const wrappedDescriptions: string[] = [];\n const results = await Promise.allSettled(\n kept.map((part) => this.processImage(part, captureId, signal)),\n );\n for (const result of results) {\n const r: ImageProcessResult =\n result.status === \"fulfilled\"\n ? result.value\n : {\n description: failurePlaceholder(\"generic\", \"unexpected error\"),\n cacheHit: false,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n wrappedDescriptions.push(r.description);\n if (r.cacheHit) stats.cacheHits++;\n if (r.cacheMiss) stats.cacheMisses++;\n if (r.visionCall) {\n stats.visionCalls++;\n stats.latencyMs.push(r.latencyMs);\n }\n }\n\n // 6. Replace image blocks in the cloned body with text blocks.\n replaceImageBlocks(mutated, apiKind, wrappedDescriptions, overflow);\n\n return {\n body: mutated,\n changed: true,\n stats,\n };\n }\n\n /**\n * Cache-only version of {@link processBody}. Checks the LRU + persistent\n * cache for every image part. On any cache miss, the original body is\n * forwarded unchanged and a background `processBody` call is enqueued to\n * populate the cache for future requests.\n *\n * Returns `{ changed: true }` only when every image was a cache hit.\n */\n async processBodyCacheOnly(\n body: unknown,\n apiKind: ApiKind,\n modelName?: string,\n captureId?: number,\n signal?: AbortSignal,\n ): Promise<ProcessBodyResult> {\n const stats: VisionStats = {\n handoffCount: 0,\n cacheHits: 0,\n cacheMisses: 0,\n visionCalls: 0,\n latencyMs: [],\n };\n\n if (this.config.strategy === \"catalog\" && modelName && this.catalog) {\n const supports: VisionTristate | null = this.catalog.getVisionSupport(modelName);\n if (\n !shouldRewrite(this.config.strategy, supports, apiKind, this.config.forceInterceptCapable)\n ) {\n return { body, changed: false, stats };\n }\n }\n\n const bodyStr = typeof body === \"string\" ? body : JSON.stringify(body);\n if (!cheapImageSignal(bodyStr)) {\n return { body, changed: false, stats };\n }\n\n const parts =\n apiKind === \"anthropic\" ? findAnthropicImageParts(body) : findOpenAIImageParts(body);\n if (parts.length === 0) {\n return { body, changed: false, stats };\n }\n\n const { kept } = applyMaxImagesPolicy(parts, this.config.maxImages);\n stats.handoffCount = kept.length;\n\n const recipe = recipeFromConfig(this.config);\n const descriptions: string[] = [];\n let allHit = true;\n\n for (const part of kept) {\n if (part.encoding === \"url\") {\n allHit = false;\n break;\n }\n const decoded = decodeBase64(part.data);\n if (decoded === null) {\n allHit = false;\n break;\n }\n let cacheBytes: Uint8Array;\n try {\n const result = await transcodeImage(decoded, {\n maxDimension: recipe.max_dimension,\n quality: recipe.quality,\n format: this.config.imageFormat,\n });\n cacheBytes = result.bytes;\n } catch {\n allHit = false;\n break;\n }\n let cached = \"\";\n try {\n cached = this.cache.getOrCompute(\n cacheBytes,\n recipe,\n ENCODER_VERSION,\n this.config.model ?? \"\",\n this.config.promptVersion,\n () => {\n throw CACHE_MISS;\n },\n );\n } catch (err) {\n if (err !== CACHE_MISS) throw err;\n }\n if (cached !== \"\") {\n descriptions.push(wrapDescription(cached));\n stats.cacheHits++;\n } else {\n allHit = false;\n break;\n }\n }\n\n if (!allHit) {\n this.enqueueBackgroundVision(body, apiKind, modelName, captureId, signal);\n return { body, changed: false, stats };\n }\n\n const mutated = cloneBody(body);\n replaceImageBlocks(mutated, apiKind, descriptions, []);\n return { body: mutated, changed: true, stats };\n }\n\n private enqueueBackgroundVision(\n body: unknown,\n apiKind: ApiKind,\n modelName: string | undefined,\n captureId: number | undefined,\n signal: AbortSignal | undefined,\n ): void {\n const bgSignal = signal ? AbortSignal.any([signal]) : undefined;\n this.processBody(body, apiKind, modelName, captureId, bgSignal).catch((err) => {\n log.warn(\"background vision processing failed\", {\n error: (err as Error).message,\n captureId,\n });\n });\n }\n\n /**\n * Process a single image part: decode → transcode → cache check → vision call → record.\n * Returns a result object that the caller assembles into stats + wrappedDescriptions.\n * Never rejects — errors produce a failure placeholder.\n */\n private async processImage(\n part: ImagePart,\n captureId: number | undefined,\n signal: AbortSignal | undefined,\n ): Promise<ImageProcessResult> {\n if (part.encoding === \"url\") {\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: 0,\n imageHash: null,\n status: \"skipped\",\n httpStatus: null,\n latencyMs: 0,\n description: \"\",\n error: \"url images unsupported in v1\",\n });\n return {\n description: failurePlaceholder(\"generic\", \"url images unsupported in v1\"),\n cacheHit: false,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const decoded = decodeBase64(part.data);\n if (decoded === null) {\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: 0,\n imageHash: null,\n status: \"parse_error\",\n httpStatus: null,\n latencyMs: 0,\n description: \"\",\n error: \"invalid base64 image data\",\n });\n return {\n description: failurePlaceholder(\"parse\", \"invalid base64 image data\"),\n cacheHit: false,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const recipe = recipeFromConfig(this.config);\n let cacheBytes: Uint8Array;\n try {\n const result = await transcodeImage(decoded, {\n maxDimension: recipe.max_dimension,\n quality: recipe.quality,\n format: this.config.imageFormat,\n });\n cacheBytes = result.bytes;\n } catch (err) {\n const errMsg = err instanceof TranscodeError ? `transcode ${err.code}` : \"transcode failed\";\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: decoded.byteLength,\n imageHash: null,\n status: \"fetch_error\",\n httpStatus: null,\n latencyMs: 0,\n description: \"\",\n error: errMsg,\n });\n return {\n description: failurePlaceholder(\"generic\", errMsg),\n cacheHit: false,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const imageHash = simpleHash(cacheBytes);\n\n let cached: string;\n try {\n cached = this.cache.getOrCompute(\n cacheBytes,\n recipe,\n ENCODER_VERSION,\n this.config.model ?? \"\",\n this.config.promptVersion,\n () => {\n throw CACHE_MISS;\n },\n );\n } catch (err) {\n if (err !== CACHE_MISS) throw err;\n cached = \"\";\n }\n if (cached !== \"\") {\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: cacheBytes.byteLength,\n imageHash,\n status: \"cache_hit\",\n httpStatus: null,\n latencyMs: 0,\n description: cached,\n error: null,\n });\n return {\n description: wrapDescription(cached),\n cacheHit: true,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const cacheKey = descriptionCacheKey(\n cacheBytes,\n recipe,\n ENCODER_VERSION,\n this.config.model ?? \"\",\n this.config.promptVersion,\n );\n\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n const desc = await existing;\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: cacheBytes.byteLength,\n imageHash,\n status: \"cache_hit\",\n httpStatus: null,\n latencyMs: 0,\n description: desc,\n error: null,\n });\n return {\n description: wrapDescription(desc),\n cacheHit: true,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const startedAt = Date.now();\n const visionDbId = this.db?.insertVisionCapture({\n $method: \"POST\",\n $path: \"/v1/chat/completions\",\n $url: this.config.target ?? \"\",\n $rh: \"{}\",\n $rb: \"{}\",\n $rs: 0,\n $status: null,\n $rh2: \"{}\",\n $rb2: \"\",\n $rs2: 0,\n $ct: \"application/json\",\n $dur: 0,\n $state: \"enqueued\",\n $started_at: startedAt,\n $finished_at: 0,\n $inp: this.protocolConfig.incomingProtocol,\n $outp: this.protocolConfig.upstreamProtocol,\n $model: this.config.model ?? \"\",\n $parent_capture_id: captureId ?? null,\n $vision_meta: null,\n ...flattenUsage(null),\n });\n if (visionDbId) this.db?.setState(visionDbId, \"streaming\");\n\n const start = Date.now();\n const visionPromise = (async () => {\n const result = await this.callVisionRecorded(cacheBytes, signal);\n const stored = this.cache.getOrCompute(\n cacheBytes,\n recipe,\n ENCODER_VERSION,\n this.config.model ?? \"\",\n this.config.promptVersion,\n () => result.description,\n );\n return { result, stored };\n })();\n this.inflight.set(\n cacheKey,\n visionPromise.then((r) => r.stored),\n );\n\n let visionResult: Awaited<ReturnType<typeof this.callVisionRecorded>>;\n let stored: string;\n try {\n const r = await visionPromise;\n visionResult = r.result;\n stored = r.stored;\n } finally {\n this.inflight.delete(cacheKey);\n }\n const elapsed = Date.now() - start;\n\n if (visionDbId) {\n const finishedAt = Date.now();\n const metaJson = JSON.stringify({\n status: visionResult.status,\n httpStatus: visionResult.httpStatus,\n latencyMs: elapsed,\n description: visionResult.description,\n error: visionResult.error,\n imageHash,\n imageSize: cacheBytes.byteLength,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n });\n this.db?.updateVisionCapture({\n $id: visionDbId,\n $status:\n visionResult.status === \"ok\" || visionResult.status === \"cache_hit\"\n ? 200\n : (visionResult.httpStatus ?? null),\n $rh: visionResult.responseHeaders,\n $rb: visionResult.responseBody,\n $rs: Buffer.byteLength(visionResult.responseBody),\n $ct: \"application/json\",\n $sse: 0,\n $dur: elapsed,\n $fin: finishedAt,\n $status_source: \"upstream\",\n $gate_reason: null,\n $vision_meta: metaJson,\n $model: this.config.model ?? null,\n ...flattenUsage(visionResult.usage),\n });\n }\n\n this.addRecord(\n {\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: cacheBytes.byteLength,\n imageHash,\n status: visionResult.status,\n httpStatus: visionResult.httpStatus,\n latencyMs: elapsed,\n description: visionResult.description,\n error: visionResult.error,\n },\n {\n requestBody: visionResult.requestBody,\n requestHeaders: visionResult.requestHeaders,\n responseBody: visionResult.responseBody,\n responseHeaders: visionResult.responseHeaders,\n },\n visionResult.usage,\n visionDbId,\n );\n\n return {\n description: wrapDescription(stored),\n cacheHit: false,\n cacheMiss: true,\n visionCall: true,\n latencyMs: elapsed,\n };\n }\n\n /**\n * Call the vision model with a single JPEG image. Returns the description\n * text or a deterministic failure placeholder on any error (fail-open).\n *\n * Request shape follows the OpenAI chat-completions format so a single\n * vision endpoint can describe images regardless of the upstream API kind.\n */\n private async callVisionRecorded(\n imageBytes: Uint8Array,\n signal?: AbortSignal,\n ): Promise<{\n description: string;\n status: VisionCallRecord[\"status\"];\n httpStatus: number | null;\n error: string | null;\n requestBody: string;\n requestHeaders: string;\n responseBody: string;\n responseHeaders: string;\n usage: UsageMetrics | null;\n }> {\n const { target, model, prompt, reasoningEffort, imageFormat, imageDetail, apiKey } =\n this.config;\n if (!target || !model) {\n log.warn(\"callVision skipped: target or model not configured\");\n return {\n description: failurePlaceholder(\"generic\", \"vision target or model not configured\"),\n status: \"skipped\",\n httpStatus: null,\n error: \"target or model not configured\",\n requestBody: \"\",\n requestHeaders: \"{}\",\n responseBody: \"\",\n responseHeaders: \"{}\",\n usage: null,\n };\n }\n\n const authHeader = apiKey ? `Bearer ${apiKey}` : \"\";\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (apiKey) headers.Authorization = authHeader;\n const requestHeaders = JSON.stringify(redactHeaders(headers));\n\n const base64 = encodeBase64(imageBytes);\n const mediaType = imageFormat === \"png\" ? \"image/png\" : \"image/jpeg\";\n\n const buildRequestBody = (): string => {\n const reqBody: Record<string, unknown> = {\n model,\n messages: [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: prompt },\n {\n type: \"image_url\",\n image_url: {\n url: `data:${mediaType};base64,${base64}`,\n detail: imageDetail,\n },\n },\n ],\n },\n ],\n };\n if (reasoningEffort !== null) {\n reqBody.reasoning_effort = reasoningEffort;\n }\n return JSON.stringify(reqBody);\n };\n\n const requestBody = buildRequestBody();\n const visionStart = Date.now();\n\n let permit: { release: () => void } | null = null;\n let response: Response;\n try {\n permit = await this.gate.acquire({\n intention: \"vision\",\n weight: this.config.visionWeight,\n signal,\n });\n response = await fetch(target, {\n method: \"POST\",\n headers,\n body: requestBody,\n signal,\n });\n } catch (err) {\n if (permit) permit.release();\n const elapsed = Date.now() - visionStart;\n if (err instanceof DOMException && err.name === \"AbortError\") {\n log.warn(`callVision ABORTED after ${elapsed}ms (client disconnect)`, {\n model,\n target,\n imgSize: imageBytes.byteLength,\n });\n return {\n description: failurePlaceholder(\"aborted\", \"client disconnected\"),\n status: \"aborted\",\n httpStatus: null,\n error: \"client disconnected\",\n requestBody,\n requestHeaders,\n responseBody: \"\",\n responseHeaders: \"{}\",\n usage: null,\n };\n }\n const errMsg = err instanceof Error ? err.message : String(err);\n log.error(`callVision FETCH ERROR after ${elapsed}ms: ${errMsg}`, { model, target });\n return {\n description: failurePlaceholder(\"generic\", \"vision fetch failed\"),\n status: \"fetch_error\",\n httpStatus: null,\n error: errMsg,\n requestBody,\n requestHeaders,\n responseBody: \"\",\n responseHeaders: \"{}\",\n usage: null,\n };\n }\n permit.release();\n\n const elapsed = Date.now() - visionStart;\n const resHeadersJson = JSON.stringify(headersToObject(response.headers));\n const rawBody = await response.text().catch(() => \"\");\n if (!response.ok) {\n log.error(`callVision HTTP ${response.status} after ${elapsed}ms`, {\n model,\n target,\n imgSize: imageBytes.byteLength,\n body: rawBody.slice(0, 300),\n });\n return {\n description: failurePlaceholder(\"http_status\", String(response.status)),\n status: \"http_error\",\n httpStatus: response.status,\n error: rawBody.slice(0, 500) || `HTTP ${response.status}`,\n requestBody,\n requestHeaders,\n responseBody: rawBody,\n responseHeaders: resHeadersJson,\n usage: null,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawBody);\n } catch {\n log.error(`callVision PARSE ERROR after ${elapsed}ms — response not valid JSON`, { model });\n return {\n description: failurePlaceholder(\"parse\", \"\"),\n status: \"parse_error\",\n httpStatus: response.status,\n error: \"response not valid JSON\",\n requestBody,\n requestHeaders,\n responseBody: rawBody,\n responseHeaders: resHeadersJson,\n usage: null,\n };\n }\n\n const text = extractOpenAIContent(parsed);\n if (!text) {\n log.error(\n `callVision EMPTY response after ${elapsed}ms — choices[0].message.content missing or empty`,\n { model },\n );\n return {\n description: failurePlaceholder(\"empty\", \"\"),\n status: \"empty\",\n httpStatus: response.status,\n error: \"choices[0].message.content missing or empty\",\n requestBody,\n requestHeaders,\n responseBody: rawBody,\n responseHeaders: resHeadersJson,\n usage: null,\n };\n }\n log.info(`callVision OK in ${elapsed}ms`, {\n model,\n imgSize: imageBytes.byteLength,\n descLen: text.length,\n });\n const usage = extractOpenAiNonStreaming(parsed, elapsed);\n return {\n description: text,\n status: \"ok\",\n httpStatus: response.status,\n error: null,\n requestBody,\n requestHeaders,\n responseBody: rawBody,\n responseHeaders: resHeadersJson,\n usage,\n };\n }\n}\n\n/**\n * Replace image blocks in `body` (already cloned) with text blocks carrying\n * the wrapped descriptions. OpenAI image_url parts and Anthropic image blocks\n * are both replaced in-place; overflow placeholders are appended after the\n * last replaced block of the relevant message so the model still sees them\n * in conversation order.\n */\nfunction replaceImageBlocks(\n body: unknown,\n apiKind: ApiKind,\n descriptions: string[],\n overflow: string[],\n): void {\n if (typeof body !== \"object\" || body === null) return;\n const messages = (body as { messages?: unknown }).messages;\n if (!Array.isArray(messages)) return;\n\n const cursor = { descIdx: 0, overflowIdx: 0 };\n const overflowText = overflow.length > 0 ? overflow.join(\"\\n\") : \"\";\n\n for (const msg of messages) {\n if (typeof msg !== \"object\" || msg === null) continue;\n const content = (msg as { content?: unknown }).content;\n if (!Array.isArray(content)) continue;\n replaceInContentArray(content, apiKind, descriptions, overflow, cursor);\n\n if (\n overflowText &&\n cursor.descIdx >= descriptions.length &&\n cursor.overflowIdx < overflow.length\n ) {\n content.push({ type: \"text\", text: overflowText });\n cursor.overflowIdx = overflow.length;\n }\n }\n}\n\nfunction replaceInContentArray(\n content: unknown[],\n apiKind: ApiKind,\n descriptions: string[],\n overflow: string[],\n cursor: { descIdx: number; overflowIdx: number },\n): void {\n for (let i = 0; i < content.length; i++) {\n const part = content[i];\n if (typeof part !== \"object\" || part === null) continue;\n const p = part as { type?: unknown; content?: unknown };\n\n if (apiKind === \"openai\" && p.type === \"image_url\") {\n if (cursor.descIdx < descriptions.length) {\n content[i] = { type: \"text\", text: descriptions[cursor.descIdx] };\n cursor.descIdx++;\n }\n } else if (apiKind === \"anthropic\" && p.type === \"image\") {\n if (cursor.descIdx < descriptions.length) {\n content[i] = {\n type: \"text\",\n text: descriptions[cursor.descIdx],\n cache_control: { type: \"ephemeral\" },\n };\n cursor.descIdx++;\n }\n } else if (apiKind === \"anthropic\" && p.type === \"tool_result\" && Array.isArray(p.content)) {\n replaceInContentArray(p.content, apiKind, descriptions, overflow, cursor);\n }\n }\n}\n\n/** Deep-clone a body via JSON round-trip. Falls back to the original on failure. */\nfunction cloneBody(body: unknown): unknown {\n try {\n return JSON.parse(JSON.stringify(body));\n } catch {\n return body;\n }\n}\n\n/**\n * Decode a base64 string to bytes. Returns null on invalid input rather than\n * throwing — callers fail-open with a placeholder.\n */\nfunction decodeBase64(data: string): Uint8Array | null {\n try {\n return new Uint8Array(Buffer.from(data, \"base64\"));\n } catch {\n return null;\n }\n}\n\n/** Encode bytes to a base64 string. */\nfunction encodeBase64(bytes: Uint8Array): string {\n return Buffer.from(bytes).toString(\"base64\");\n}\n\n/** Fast non-cryptographic hash for dedup identification in vision call records. */\nfunction simpleHash(bytes: Uint8Array): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < bytes.length; i++) {\n h ^= bytes[i];\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Extract the assistant message text from an OpenAI chat-completions response.\n * Returns the empty string when the shape is unexpected.\n */\nfunction extractOpenAIContent(parsed: unknown): string {\n if (typeof parsed !== \"object\" || parsed === null) return \"\";\n const choices = (parsed as { choices?: unknown }).choices;\n if (!Array.isArray(choices) || choices.length === 0) return \"\";\n const first = choices[0];\n if (typeof first !== \"object\" || first === null) return \"\";\n const message = (first as { message?: { content?: unknown } }).message;\n if (!message) return \"\";\n const content = message.content;\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n for (const part of content) {\n if (typeof part !== \"object\" || part === null) continue;\n const t = (part as { type?: unknown; text?: unknown }).type;\n const txt = (part as { text?: unknown }).text;\n if (t === \"text\" && typeof txt === \"string\") return txt;\n }\n }\n // Never fall back to reasoning_content — it is the model's internal chain-of-thought,\n // not the actual image description. An empty return triggers a failure placeholder.\n return \"\";\n}\n","import type { CaptureDB } from \"../db.js\";\n\nexport interface PersistentDescriptionEntry {\n key: string;\n description: string;\n imageHash: string;\n model: string;\n promptVersion: number;\n}\n\ninterface PendingWrite {\n entry: PersistentDescriptionEntry;\n now: number;\n}\n\n/**\n * SQLite-backed description store with write-behind batching.\n *\n * Writes are buffered and flushed in batches on a timer (same pattern as\n * `queue.ts` WriteQueue) to avoid blocking the request path with synchronous\n * SQLite writes. Reads check the pending buffer first, then SQLite.\n *\n * WAL mode + single-threaded JS event loop means no lock contention between\n * reads and batched writes — the write-behind queue exists to keep the\n * request path fast, not to avoid lock contention (WAL already handles that).\n */\nexport class PersistentDescriptionStore {\n private readonly db: CaptureDB;\n private readonly ttlMs: number;\n private readonly maxRows: number;\n private lastEvictionAt = 0;\n private static readonly EVICTION_INTERVAL_MS = 60_000;\n\n private readonly pendingWrites: PendingWrite[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly flushIntervalMs: number;\n private readonly flushBatch: number;\n private closed = false;\n\n constructor(db: CaptureDB, ttlMs: number, maxRows: number, flushBatch = 50) {\n this.db = db;\n this.ttlMs = ttlMs;\n this.maxRows = maxRows;\n this.flushIntervalMs = 500;\n this.flushBatch = flushBatch;\n }\n\n get(key: string): string | null {\n for (let i = this.pendingWrites.length - 1; i >= 0; i--) {\n if (this.pendingWrites[i].entry.key === key) {\n const pw = this.pendingWrites[i];\n if (Date.now() - pw.now > this.ttlMs) return null;\n return pw.entry.description;\n }\n }\n const row = this.db.getVisionDescription(key);\n if (!row) return null;\n const now = Date.now();\n if (now - row.created_at > this.ttlMs) {\n this.db.deleteVisionDescription(key);\n return null;\n }\n return row.description;\n }\n\n set(entry: PersistentDescriptionEntry): void {\n const now = Date.now();\n this.pendingWrites.push({ entry, now });\n if (this.pendingWrites.length >= this.flushBatch) {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n this.flushNow();\n } else if (!this.flushTimer) {\n this.flushTimer = setTimeout(() => {\n try {\n this.flushNow();\n } catch {\n // DB may be closed during shutdown; pending writes are lost\n }\n }, this.flushIntervalMs);\n }\n this.maybeEvict(now);\n }\n\n flushNow(): void {\n this.flushTimer = null;\n if (this.closed || this.pendingWrites.length === 0) return;\n const batch = this.pendingWrites.splice(0, this.pendingWrites.length);\n for (const pw of batch) {\n this.db.upsertVisionDescription({\n $key: pw.entry.key,\n $image_hash: pw.entry.imageHash,\n $model: pw.entry.model,\n $prompt_version: pw.entry.promptVersion,\n $description: pw.entry.description,\n $now: pw.now,\n });\n }\n }\n\n close(): void {\n this.closed = true;\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n this.flushNow();\n }\n\n warmIntoCache(onEntry: (key: string, description: string) => void, limit: number): number {\n this.flushNow();\n const cutoff = Date.now() - this.ttlMs;\n const rows = this.db.listVisionDescriptionsForWarming(limit, cutoff);\n let count = 0;\n for (const row of rows) {\n onEntry(row.key, row.description);\n count++;\n }\n return count;\n }\n\n private maybeEvict(now: number): void {\n if (now - this.lastEvictionAt < PersistentDescriptionStore.EVICTION_INTERVAL_MS) return;\n this.lastEvictionAt = now;\n const cutoff = now - this.ttlMs;\n this.db.evictVisionDescriptions(cutoff, this.maxRows);\n }\n}\n","// Vision record sinks.\n//\n// Decouples VisionHandoff.addRecord() from the concrete persistence (CaptureDB)\n// and broadcast (WsBroadcaster) side effects. A sink receives a fully-built\n// VisionRecord and performs its side effect; error isolation is the\n// CompositeVisionSink's responsibility so a DB failure never prevents a WS\n// broadcast (and vice versa).\n\nimport { type CaptureDB, flattenUsage } from \"../db.js\";\nimport type { CaptureSummary, ProtocolConfig, WsMessage } from \"../types.js\";\nimport type { UsageMetrics } from \"../usage/types.js\";\nimport type { WsBroadcaster } from \"../ws.js\";\nimport type { VisionCallRecord } from \"./handoff.js\";\n\ninterface SinkRecordParts {\n metaJson: string;\n reqBody: string;\n resBody: string;\n reqHeaders: string;\n resHeaders: string;\n path: string;\n url: string;\n status: number | null;\n startedAt: number;\n}\n\nfunction buildSinkRecord(record: VisionRecord, opts?: { includeUrl?: boolean }): SinkRecordParts {\n const { rec, httpExchange } = record;\n const metaJson = JSON.stringify({\n status: rec.status,\n httpStatus: rec.httpStatus,\n latencyMs: rec.latencyMs,\n description: rec.description,\n error: rec.error,\n imageHash: rec.imageHash,\n imageSize: rec.imageSize,\n model: rec.model,\n target: rec.target,\n });\n const reqBody =\n httpExchange?.requestBody ??\n JSON.stringify({\n model: rec.model,\n target: rec.target,\n imageSize: rec.imageSize,\n imageHash: rec.imageHash,\n });\n const resBody = httpExchange?.responseBody ?? metaJson;\n const reqHeaders = httpExchange?.requestHeaders ?? \"{}\";\n const resHeaders = httpExchange?.responseHeaders ?? \"{}\";\n\n let path = \"/v1/chat/completions\";\n let url = \"\";\n if (rec.target) {\n try {\n const u = new URL(rec.target);\n path = u.pathname + u.search;\n if (opts?.includeUrl) {\n url = rec.target;\n }\n } catch {\n path = rec.target;\n if (opts?.includeUrl) {\n url = rec.target;\n }\n }\n }\n\n const status = rec.status === \"ok\" || rec.status === \"cache_hit\" ? 200 : (rec.httpStatus ?? null);\n const startedAt = rec.timestamp - Math.max(0, rec.latencyMs);\n\n return {\n metaJson,\n reqBody,\n resBody,\n reqHeaders,\n resHeaders,\n path,\n url,\n status,\n startedAt,\n };\n}\n\n/** Raw HTTP exchange captured alongside a vision call (bodies + headers). */\nexport interface VisionHttpExchange {\n requestBody: string;\n requestHeaders: string;\n responseBody: string;\n responseHeaders: string;\n}\n\n/**\n * Everything a sink needs to persist a vision capture and broadcast it.\n * Bundles the call record, the optional HTTP exchange, and parsed usage.\n */\nexport interface VisionRecord {\n /** The assigned vision call record (id + timestamp included). */\n rec: VisionCallRecord;\n /** Raw HTTP exchange if available; sinks fall back to synthesized bodies. */\n httpExchange?: VisionHttpExchange;\n /** Parsed usage metrics for token accounting (null if absent). */\n usage: UsageMetrics | null;\n /** CaptureDB row id when the row has already been inserted (lifecycle path). */\n dbId?: number;\n}\n\n/**\n * Receives a completed vision record for persistence and/or broadcast.\n * Implementations must not throw — wrap errors internally if they cannot\n * propagate cleanly, because {@link CompositeVisionSink} relies on the\n * \"one sink throwing must not break the others\" contract at the composite\n * boundary (it also isolates, but sinks shouldn't depend on that).\n */\nexport interface VisionRecordSink {\n record(record: VisionRecord): void;\n}\n\n/**\n * Fan-out sink: forwards the record to each child sink, isolating errors so\n * a throw in one sink never prevents the remaining sinks from running.\n *\n * This is the default sink wired in src/index.ts: CompositeVisionSink([\n * new DbVisionSink(db),\n * new WsBroadcastVisionSink(ws),\n * ]).\n */\nexport class CompositeVisionSink implements VisionRecordSink {\n private readonly sinks: readonly VisionRecordSink[];\n\n constructor(sinks: readonly VisionRecordSink[]) {\n this.sinks = sinks;\n }\n\n record(record: VisionRecord): void {\n for (const sink of this.sinks) {\n try {\n sink.record(record);\n } catch {\n // Error isolation: one sink failing must not break the others.\n // Intentionally swallowed — each sink owns its own logging if needed.\n }\n }\n }\n}\n\n/**\n * Persists a vision record into the captures table via CaptureDB.\n * Reproduces the exact insert params the fused VisionHandoff.addRecord()\n * previously built inline.\n */\nexport class DbVisionSink implements VisionRecordSink {\n constructor(\n private readonly db: CaptureDB,\n private readonly protocolConfig: ProtocolConfig,\n ) {}\n\n record(record: VisionRecord): void {\n if (record.dbId !== undefined) return;\n const { rec, usage } = record;\n const parts = buildSinkRecord(record, { includeUrl: true });\n\n record.dbId = this.db.insertVisionCapture({\n $method: \"POST\",\n $path: parts.path,\n $url: parts.url,\n $rh: parts.reqHeaders,\n $rb: parts.reqBody,\n $rs: parts.reqBody.length,\n $status: parts.status,\n $rh2: parts.resHeaders,\n $rb2: parts.resBody,\n $rs2: parts.resBody.length,\n $ct: \"application/json\",\n $dur: rec.latencyMs,\n $state: rec.state,\n $started_at: parts.startedAt,\n $finished_at: rec.timestamp,\n $inp: this.protocolConfig.incomingProtocol,\n $outp: this.protocolConfig.upstreamProtocol,\n $model: rec.model,\n $parent_capture_id: rec.captureId ?? null,\n $vision_meta: parts.metaJson,\n ...flattenUsage(usage),\n });\n }\n}\n\n/**\n * Broadcasts a vision record as a \"new capture\" WS message.\n * Reproduces the exact CaptureSummary the fused VisionHandoff.addRecord()\n * previously built inline.\n */\nexport class WsBroadcastVisionSink implements VisionRecordSink {\n constructor(\n private readonly ws: WsBroadcaster,\n private readonly protocolConfig: ProtocolConfig,\n ) {}\n\n record(record: VisionRecord): void {\n const { rec, usage } = record;\n const parts = buildSinkRecord(record);\n const msg: WsMessage = {\n type: record.dbId ? \"update\" : \"new\",\n capture: {\n id: record.dbId ?? rec.id,\n method: \"POST\",\n path: parts.path,\n response_status: parts.status,\n is_sse: false,\n content_type: \"application/json\",\n request_size: parts.reqBody.length,\n response_size: parts.resBody.length,\n duration_ms: rec.latencyMs,\n state: rec.state,\n started_at: parts.startedAt,\n finished_at: rec.timestamp,\n incoming_protocol: this.protocolConfig.incomingProtocol,\n upstream_protocol: this.protocolConfig.upstreamProtocol,\n model: rec.model,\n usage_missing: usage ? usage.usage_missing : null,\n ttft_ms: usage?.ttft_ms ?? null,\n tps: usage?.tps ?? null,\n cache_creation_tokens: usage?.cache_creation_tokens ?? null,\n cache_read_tokens: usage?.cache_read_tokens ?? null,\n total_input_tokens: usage?.total_input_tokens ?? null,\n output_tokens: usage?.output_tokens ?? null,\n total_output_tokens: usage?.total_output_tokens ?? null,\n is_vision: true,\n status_source: \"upstream\",\n gate_reason: null,\n } satisfies CaptureSummary,\n };\n this.ws.broadcast(msg);\n }\n}\n","// Upstream connection warmer — prevents TLS handshake overhead on the first\n// request after startup or extended idle (saves ~750ms cold-start latency).\n// Pings a lightweight upstream endpoint on a fixed interval, but skips\n// the ping whenever the proxy handled real traffic in the last interval.\n\nimport { createLogger } from \"./logger.js\";\nimport type { ProxyConfig, UpstreamProtocol } from \"./types.js\";\n\nconst log = createLogger(\"warmer\");\n\nexport class ConnectionWarmer {\n private readonly target: string;\n private readonly intervalMs: number;\n private readonly path: string;\n private readonly upstreamProtocol: UpstreamProtocol;\n private timer: ReturnType<typeof setInterval> | null = null;\n private lastTrafficAt = 0;\n private warmedOnce = false;\n\n constructor(\n config: Pick<ProxyConfig, \"target\" | \"warmerIntervalMs\" | \"warmerPath\" | \"upstreamProtocol\">,\n ) {\n this.target = config.target;\n this.intervalMs = config.warmerIntervalMs;\n this.path = config.warmerPath;\n this.upstreamProtocol = config.upstreamProtocol;\n }\n\n start(): void {\n if (this.timer || this.intervalMs <= 0) return;\n void this.ping();\n this.timer = setInterval(() => void this.ping(), this.intervalMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Called by the proxy on every upstream request — connection is already warm. */\n notifyTraffic(): void {\n this.lastTrafficAt = Date.now();\n }\n\n private async ping(): Promise<void> {\n if (Date.now() - this.lastTrafficAt < this.intervalMs) return;\n const url = `${this.target}${this.path}`;\n try {\n const res = await fetch(url, {\n method: \"GET\",\n headers: { \"accept-encoding\": \"identity\" },\n protocol: this.upstreamProtocol as unknown as never,\n });\n if (!this.warmedOnce && res.ok) {\n this.warmedOnce = true;\n log.info(`upstream warm: ${url} (${res.status})`);\n }\n } catch {\n // Silent — warmer is best-effort. Next interval will retry.\n }\n }\n}\n","import type { UpdateParams } from \"../db.js\";\nimport { createLogger } from \"../logger.js\";\nimport type { CaptureStore } from \"../queue.js\";\n\nconst logger = createLogger(\"worker-store\");\n\nconst ACK_TIMEOUT_MS = 10_000;\n\ninterface PendingBatch {\n resolve: () => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n itemCount: number;\n}\n\nexport class WorkerCaptureStore implements CaptureStore {\n private worker: Worker;\n private dbPath: string;\n private compressionEnabled: boolean;\n private nextBatchId = 1;\n private pending = new Map<number, PendingBatch>();\n\n constructor(dbPath: string, compressionEnabled: boolean) {\n this.dbPath = dbPath;\n this.compressionEnabled = compressionEnabled;\n this.worker = new Worker(new URL(\"./write-worker.ts\", import.meta.url));\n this.worker.onmessage = (e: MessageEvent) => {\n const msg = e.data as { type: string; batchId?: number; count?: number; error?: string };\n if (!msg.batchId) return;\n const pending = this.pending.get(msg.batchId);\n if (!pending) return;\n clearTimeout(pending.timer);\n this.pending.delete(msg.batchId);\n if (msg.type === \"error\") {\n logger.error(\"worker batch write failed\", { error: msg.error, count: msg.count });\n pending.reject(new Error(msg.error ?? \"unknown worker error\"));\n } else {\n pending.resolve();\n }\n };\n this.worker.onerror = (e: ErrorEvent) => {\n logger.error(\"worker error\", { message: e.message });\n for (const [id, pending] of this.pending) {\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.reject(new Error(`worker crashed: ${e.message}`));\n }\n };\n }\n\n updateCapture(_params: UpdateParams): void {\n throw new Error(\n \"updateCapture should not be called on WorkerCaptureStore — use the main db for single updates\",\n );\n }\n\n batchUpdate(items: Array<{ id: number; res: Omit<UpdateParams, \"$id\"> }>): Promise<void> {\n const batchId = this.nextBatchId++;\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(batchId);\n logger.error(\"worker ack timeout\", { batchId, count: items.length });\n reject(new Error(`worker ack timeout for batch ${batchId}`));\n }, ACK_TIMEOUT_MS);\n this.pending.set(batchId, { resolve, reject, timer, itemCount: items.length });\n this.worker.postMessage({\n type: \"batch\",\n batchId,\n dbPath: this.dbPath,\n items,\n compressionEnabled: this.compressionEnabled,\n });\n });\n }\n\n async close(): Promise<void> {\n this.worker.postMessage({ type: \"close\" });\n const drainDeadline = Date.now() + ACK_TIMEOUT_MS;\n while (this.pending.size > 0 && Date.now() < drainDeadline) {\n await Bun.sleep(50);\n }\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timer);\n pending.reject(new Error(\"worker closed before ack\"));\n }\n this.pending.clear();\n this.worker.terminate();\n }\n}\n","// WebSocket broadcast manager.\n// Tracks connected inspector clients and fans out capture updates.\n\nimport type { WsMessage } from \"./types.js\";\n\nexport type { WsMessage };\n\n/** A Bun WebSocket server instance. */\nexport interface BunServerWebSocket {\n send(data: string): number;\n close(code?: number, reason?: string): void;\n readyState: number;\n}\n\n/**\n * Manages the set of connected WebSocket clients and broadcasts messages.\n * Decoupled from the server lifecycle so tests can stub it.\n */\nexport class WsBroadcaster {\n private clients = new Set<BunServerWebSocket>();\n\n /** Add a client to the broadcast set. */\n add(ws: BunServerWebSocket): void {\n this.clients.add(ws);\n }\n\n /** Remove a client from the broadcast set. */\n remove(ws: BunServerWebSocket): void {\n this.clients.delete(ws);\n }\n\n /** Broadcast a message to all connected clients. Swallows per-client errors. */\n broadcast(msg: WsMessage): void {\n const s = JSON.stringify(msg);\n const dead: BunServerWebSocket[] = [];\n for (const ws of this.clients) {\n try {\n const sent = ws.send(s);\n if (sent === 0) {\n dead.push(ws);\n try {\n ws.close(1013, \"backpressure\");\n } catch {\n // Ignore close failures on already-closed sockets.\n }\n }\n } catch {\n dead.push(ws);\n }\n }\n for (const ws of dead) {\n this.clients.delete(ws);\n }\n }\n\n /** Current number of connected clients. */\n get size(): number {\n return this.clients.size;\n }\n}\n"],"mappings":"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","names":["existsSync","execSync","existsSync","dirname","join","isCompiledExecutable","isNpmGlobal","log","evType","num","num","metrics","log","out","log","log","log","modelName","log","restart","log","stored","elapsed","log","logger","log","fetch","checkForUpdate","performUpdate","uninstall"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/updater.ts","../src/uninstaller.ts","../src/cli.ts","../package.json","../src/banner.ts","../src/index.ts","../src/db.ts","../src/compress.ts","../src/logger.ts","../src/economics.ts","../src/usage/sse-parse.ts","../src/models/name.ts","../src/usage/helpers.ts","../src/usage/extract.ts","../src/usage/ddl.ts","../src/vision-description-store.ts","../src/shared/http-headers.ts","../src/shared/text-codec.ts","../src/shared/capture-summary.ts","../src/shared/classify-429.ts","../src/shared/weight.ts","../src/limiter/types.ts","../src/limiter/circuit-breaker.ts","../src/limiter/gate.ts","../src/metrics.ts","../src/model-info-parser.ts","../src/models/fetch-info.ts","../src/models.ts","../src/proxy.ts","../src/stamp-pipeline.ts","../src/model-policy.ts","../src/stamp-reasoning.ts","../src/stamp-temperature.ts","../src/stamp-thinking.ts","../src/stamp-topk.ts","../src/stamp.ts","../src/queue.ts","../src/rate.ts","../src/usage/fetch-usage.ts","../src/usage/parser.ts","../src/usage/reconciler.ts","../src/usage/aggregator.ts","../src/viewer.ts","../src/vision/cache.ts","../src/vision/detect.ts","../src/vision/transcode.ts","../src/vision/wrapper.ts","../src/vision/handoff.ts","../src/vision/persistent-cache.ts","../src/vision/sink.ts","../src/warmer.ts","../src/workers/worker-store.ts","../src/ws.ts"],"sourcesContent":["// Configuration: JSON config file (single source of truth) + env overrides.\n// Config path resolution (cross-OS):\n// Linux/macOS: $XDG_CONFIG_HOME/umans-gate/config.json or ~/.config/umans-gate/config.json\n// Windows: %APPDATA%/umans-gate/config.json\n// Precedence: env vars > JSON config file > built-in defaults.\n// On first run, a config.json is written to the resolved path.\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type {\n IncomingProtocol,\n OutputConfig,\n ProxyConfig,\n ThinkingConfig,\n UpstreamProtocol,\n} from \"./types.js\";\n\n/** Resolve the config directory path following OS conventions. */\nexport function resolveConfigDir(): string {\n const p = platform();\n if (p === \"win32\") {\n const appData = process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n return join(appData, \"umans-gate\");\n }\n // Linux, macOS, and fallback.\n const xdg = process.env.XDG_CONFIG_HOME;\n const base = xdg && xdg.length > 0 ? xdg : join(homedir(), \".config\");\n return join(base, \"umans-gate\");\n}\n\n/** Resolve the config file path (JSON single source of truth). */\nexport function resolveConfigPath(): string {\n return join(resolveConfigDir(), \"config.json\");\n}\n\n/**\n * Fields removed from user config (hardcoded — app is Umans-specific):\n * target → \"https://api.code.umans.ai\"\n * openai_path → \"chat/completions\"\n * warmer_path → \"/v1/models\"\n * rate_limit_window_seconds → derived from /v1/usage (inherent, not configurable)\n * vision_target → derived from target + \"/v1/chat/completions\"\n * host → hardcoded \"127.0.0.1\" (local-only; use SSH tunnel for remote access)\n */\nexport interface RawConfig {\n port?: number;\n max_captures?: number;\n db_path?: string;\n idle_timeout?: number;\n upstream_protocol?: string;\n /** When true, applies the full Claude Code stamp bundle on Anthropic requests (TTL, top_k, max_tokens, thinking, output_config, context_management). */\n stamp_claude_code_enabled?: boolean;\n /** When true, stamps reasoning_effort onto OpenAI-compatible requests (effort=max for umans-glm* models, effort=high for all others) and removes max_tokens/thinking. */\n stamp_reasoning_effort_enabled?: boolean;\n warmer_enabled?: boolean;\n warmer_interval_ms?: number;\n umans_api_key?: string;\n usage_refresh_ms?: number;\n models_refresh_ms?: number;\n concurrency_hard_cap?: number;\n concurrency_soft_limit?: number;\n /** Pro-tier rolling-window request limit. -1 = unlimited (no limiter), 0 = auto-derive from /v1/usage, >0 = explicit limit. */\n rate_limit_requests?: number;\n queue_timeout_ms?: number;\n max_queue_depth?: number;\n release_cooldown_ms?: number;\n breaker_threshold?: number;\n breaker_window_ms?: number;\n breaker_cooldown_ms?: number;\n vision_strategy?: \"never\" | \"catalog\" | \"always\";\n vision_model?: string;\n vision_prompt?: string;\n vision_prompt_version?: number;\n vision_max_images?: number;\n vision_max_description_tokens?: number;\n vision_reasoning_effort?: \"none\" | \"low\" | \"medium\" | \"high\" | null;\n vision_timeout_ms?: number;\n vision_cache_size?: number;\n vision_cache_ttl_ms?: number;\n vision_cache_max_rows?: number;\n vision_persistent_cache?: boolean;\n vision_concurrency?: number;\n vision_max_dimension?: number;\n vision_jpeg_quality?: number;\n vision_image_format?: \"jpeg\" | \"png\";\n vision_image_detail?: \"auto\" | \"low\" | \"high\";\n concurrency_main_reservation?: number;\n concurrency_vision_reservation?: number;\n /** Max captured request/response body size in bytes. 0 = unlimited. */\n capture_body_max_bytes?: number;\n /** Max depth of the write-behind response queue. Distinct from waiters queue. */\n queue_max_depth?: number;\n /** WebSocket backpressure limit in bytes. 0 = use Bun default. */\n ws_backpressure_limit?: number;\n /** Close WebSocket connections that exceed the backpressure limit. */\n ws_close_on_backpressure_limit?: boolean;\n /** Max pending vision requests to batch together. */\n vision_pending_max_batch?: number;\n /** Compress stored request/response bodies with zstd. Default true (on). */\n compression_enabled?: boolean;\n upstream_timeout_ms?: number;\n}\n\n/**\n * Input shape accepted by validateConfig/saveConfig. Numeric fields accept\n * strings because HTML form inputs and env vars produce strings. Coercion\n * to RawConfig happens inside validateConfig.\n */\nexport type RawConfigInput = {\n [K in keyof RawConfig]?: NonNullable<RawConfig[K]> extends number\n ? RawConfig[K] | string\n : RawConfig[K];\n};\n\n/** Hardcoded constants — app is Umans-specific, not user-configurable. */\nexport const UPSTREAM_TARGET = \"https://api.code.umans.ai\";\nexport const OPENAI_CHAT_PATH = \"chat/completions\";\nexport const WARMER_PATH = \"/v1/models\";\n/** Vision target derived from upstream target. */\nexport const VISION_TARGET_PATH = \"/v1/chat/completions\";\n/** Stamp TTL value used when stamp_claude_code_enabled is true. */\nexport const STAMP_CACHE_TTL_VALUE = \"1h\";\n/** Top-K value used when stamp_claude_code_enabled is true. */\nexport const STAMP_TOP_K_VALUE = 20;\n/** Temperature value forced when stamp_claude_code_enabled is true. */\nexport const STAMP_TEMPERATURE_VALUE = 1.0;\n/** Thinking block injected when stamp_claude_code_enabled is true. */\nexport const STAMP_THINKING_VALUE: ThinkingConfig = {\n type: \"adaptive\",\n};\n\n/** max_tokens injected for umans-glm* models when stamp_claude_code_enabled is true. */\nexport const STAMP_MAX_TOKENS_GLM_VALUE = 131071;\n\n/** max_tokens injected for non-GLM models when stamp_claude_code_enabled is true. */\nexport const STAMP_MAX_TOKENS_VALUE = 32767;\n\n/** output_config injected for non-GLM models when stamp_claude_code_enabled is true. */\nexport const STAMP_OUTPUT_CONFIG_VALUE: OutputConfig = {\n effort: \"high\",\n};\n\n/** output_config injected for umans-glm* models when stamp_claude_code_enabled is true. */\nexport const STAMP_OUTPUT_CONFIG_GLM_VALUE: OutputConfig = {\n effort: \"max\",\n};\n\n/** anthropic-beta header injected when stamp_claude_code_enabled is true (Anthropic requests only). */\nexport const STAMP_ANTHROPIC_BETA_HEADER =\n \"claude-code-20250219,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,thinking-token-count-2026-05-13,context-management-2025-06-27,prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07,advisor-tool-2026-03-01,effort-2025-11-24,extended-cache-ttl-2025-04-11\";\n\n/** context_management block injected when stamp_claude_code_enabled is true and anthropic-version is 2023-06-01. */\nexport const STAMP_CONTEXT_MANAGEMENT_VALUE = {\n edits: [{ type: \"clear_thinking_20251015\", keep: \"all\" }],\n} as const;\n\nexport const STAMP_REASONING_EFFORT_VALUE = \"high\" as const;\nexport const STAMP_REASONING_EFFORT_GLM_VALUE = \"max\" as const;\n\n/** The default config written on first run. */\nconst DEFAULT_CONFIG: RawConfig = {\n port: 1945,\n max_captures: 200,\n db_path: \"./umans-gate.db\",\n idle_timeout: 255,\n upstream_protocol: \"http1.1\",\n stamp_claude_code_enabled: false,\n stamp_reasoning_effort_enabled: false,\n warmer_enabled: true,\n warmer_interval_ms: 20000,\n umans_api_key: \"\",\n usage_refresh_ms: 60000,\n models_refresh_ms: 3600000,\n concurrency_hard_cap: 1,\n concurrency_soft_limit: 1,\n rate_limit_requests: 0,\n queue_timeout_ms: 30000,\n max_queue_depth: 256,\n release_cooldown_ms: 1000,\n breaker_threshold: 5,\n breaker_window_ms: 300000,\n breaker_cooldown_ms: 60000,\n vision_strategy: \"catalog\",\n vision_model: \"umans-flash\",\n vision_prompt:\n \"You are an expert visual analyst with perfect vision and meticulous attention to detail. Your task is to produce an exhaustive, accurate description of an image for a downstream text-only language model that cannot see the image.\\n\\nStructure your description as:\\n\\n1. IMAGE TYPE: What kind of image is this (photograph, screenshot, diagram, chart, illustration, document scan, UI mockup, etc.)?\\n\\n2. OVERALL CONTENT: A comprehensive summary of everything visible.\\n\\n3. TEXT/OCR: Transcribe ALL visible text exactly as written, preserving:\\n - Original spelling, formatting, and hierarchy\\n - Line breaks and spatial layout\\n - Numbers, codes, identifiers, and labels\\n - Captions, watermarks, signatures\\n If text is partially visible, transcribe what you can and mark gaps with [...].\\n\\n4. VISUAL ELEMENTS: Describe in detail:\\n - Objects, people, and their positions/relationships\\n - Colors, shapes, textures\\n - Spatial layout and composition\\n - UI elements (buttons, menus, fields, tabs) if a screenshot\\n\\n5. DATA/CHARTS: If charts, tables, or data visualizations are present:\\n - Chart type and axes\\n - Data values, ranges, and trends\\n - Table structure and cell contents\\n\\n6. CONTEXTUAL CLUES: Date/time indicators, language, cultural context, technical domain indicators.\\n\\n7. QUALITY NOTES: Any blur, artifacts, obstructions, or ambiguity.\\n\\nRules:\\n- Describe what is VISIBLE, not what you infer.\\n- Be exhaustive: omit nothing visible. When in doubt, include it.\\n- For uncertain elements, state your uncertainty rather than guessing.\\n- Do not summarize or abbreviate.\\n- Output only the description, no preamble.\",\n vision_prompt_version: 2,\n vision_max_images: 5,\n vision_max_description_tokens: 4096,\n vision_reasoning_effort: \"none\",\n vision_timeout_ms: 0,\n vision_cache_size: 1000,\n vision_cache_ttl_ms: 604800000,\n vision_cache_max_rows: 10000,\n vision_persistent_cache: true,\n vision_concurrency: 1,\n vision_max_dimension: 2048,\n vision_jpeg_quality: 92,\n vision_image_format: \"png\",\n vision_image_detail: \"high\",\n concurrency_main_reservation: 1,\n concurrency_vision_reservation: 1,\n capture_body_max_bytes: 10_000_000,\n queue_max_depth: 100,\n ws_backpressure_limit: 1_048_576,\n ws_close_on_backpressure_limit: true,\n vision_pending_max_batch: 50,\n compression_enabled: true,\n upstream_timeout_ms: 300000,\n};\n\n/** Validation result. */\nexport interface ValidationResult {\n ok: boolean;\n errors: string[];\n warnings: string[];\n normalized: RawConfig;\n}\n\n/** Reload result returned by the reload API. */\nexport interface ReloadResult {\n ok: boolean;\n errors: string[];\n warnings: string[];\n applied: string[];\n restartRequired: string[];\n configPath: string;\n}\n\n/**\n * Fields stored as integers on disk. The UI sends strings from <input> elements,\n * so coerce numeric strings to numbers before validation and before writing.\n * This is defense-in-depth: protects both the UI path and direct API callers.\n */\nconst INT_FIELDS: (keyof RawConfig)[] = [\n \"port\",\n \"max_captures\",\n \"idle_timeout\",\n \"warmer_interval_ms\",\n \"usage_refresh_ms\",\n \"models_refresh_ms\",\n \"concurrency_hard_cap\",\n \"concurrency_soft_limit\",\n \"rate_limit_requests\",\n \"queue_timeout_ms\",\n \"max_queue_depth\",\n \"release_cooldown_ms\",\n \"breaker_threshold\",\n \"breaker_window_ms\",\n \"breaker_cooldown_ms\",\n \"vision_prompt_version\",\n \"vision_max_images\",\n \"vision_max_description_tokens\",\n \"vision_timeout_ms\",\n \"vision_cache_size\",\n \"vision_cache_ttl_ms\",\n \"vision_cache_max_rows\",\n \"vision_max_dimension\",\n \"vision_jpeg_quality\",\n \"vision_concurrency\",\n \"concurrency_main_reservation\",\n \"concurrency_vision_reservation\",\n \"capture_body_max_bytes\",\n \"queue_max_depth\",\n \"ws_backpressure_limit\",\n \"vision_pending_max_batch\",\n \"upstream_timeout_ms\",\n];\n\n/**\n * Coerce a raw config patch so that numeric strings become numbers and empty\n * strings for nullable fields become null. HTML form inputs always produce\n * strings; without this, Number.isInteger(\"7777\") === false and validation\n * rejects every numeric field the UI sends.\n *\n * Returns a new object; does not mutate the input.\n */\nfunction coerceRawForValidation(raw: RawConfigInput): RawConfig {\n const out = { ...raw } as Record<string, unknown>;\n // Strip keys that are no longer in RawConfig (e.g. background_vision,\n // vision_force_intercept_capable, use_write_worker — now derived/hardcoded).\n // This prevents dead keys from persisting through save cycles.\n const knownKeys = new Set(Object.keys(DEFAULT_CONFIG));\n for (const k of Object.keys(out)) {\n if (!knownKeys.has(k)) {\n delete out[k];\n }\n }\n for (const k of INT_FIELDS) {\n const v = out[k];\n if (typeof v === \"string\" && v.length > 0) {\n const n = Number(v);\n if (!Number.isNaN(n)) {\n out[k] = n;\n }\n }\n }\n return out as unknown as RawConfig;\n}\n\ninterface FieldRule {\n name: string;\n errors: (n: RawConfig) => string[];\n}\n\ninterface WarningRule {\n name: string;\n warning: (n: RawConfig) => string | null;\n}\nconst FIELD_RULES: FieldRule[] = [\n {\n name: \"port\",\n errors: (n) =>\n n.port !== undefined && (!Number.isInteger(n.port) || n.port < 1 || n.port > 65535)\n ? [\"port must be an integer between 1 and 65535\"]\n : [],\n },\n {\n name: \"max_captures\",\n errors: (n) =>\n n.max_captures !== undefined && (!Number.isInteger(n.max_captures) || n.max_captures < 1)\n ? [\"max_captures must be a positive integer\"]\n : [],\n },\n {\n name: \"db_path\",\n errors: (n) =>\n n.db_path !== undefined && (typeof n.db_path !== \"string\" || n.db_path.length === 0)\n ? [\"db_path must be a non-empty string\"]\n : [],\n },\n {\n name: \"idle_timeout\",\n errors: (n) =>\n n.idle_timeout !== undefined &&\n (!Number.isInteger(n.idle_timeout) || n.idle_timeout < 1 || n.idle_timeout > 255)\n ? [\"idle_timeout must be an integer between 1 and 255\"]\n : [],\n },\n {\n name: \"upstream_protocol\",\n errors: (n) => {\n if (n.upstream_protocol === undefined) return [];\n const v = String(n.upstream_protocol).toLowerCase();\n return v !== \"http1.1\" && v !== \"http2\" && v !== \"h2\"\n ? [\"upstream_protocol must be 'http1.1' or 'http2'\"]\n : [];\n },\n },\n ...(\n [\"stamp_claude_code_enabled\", \"stamp_reasoning_effort_enabled\", \"compression_enabled\"] as const\n ).map((field) => ({\n name: field,\n errors: (n: RawConfig) =>\n n[field] !== undefined && typeof n[field] !== \"boolean\" ? [`${field} must be a boolean`] : [],\n })),\n {\n name: \"warmer_enabled\",\n errors: (n) =>\n n.warmer_enabled !== undefined && typeof n.warmer_enabled !== \"boolean\"\n ? [\"warmer_enabled must be a boolean\"]\n : [],\n },\n {\n name: \"warmer_interval_ms\",\n errors: (n) =>\n n.warmer_interval_ms !== undefined &&\n (!Number.isInteger(n.warmer_interval_ms) || n.warmer_interval_ms < 1000)\n ? [\"warmer_interval_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"umans_api_key\",\n errors: (n) =>\n n.umans_api_key !== undefined && typeof n.umans_api_key !== \"string\"\n ? [\"umans_api_key must be a string\"]\n : [],\n },\n {\n name: \"usage_refresh_ms\",\n errors: (n) =>\n n.usage_refresh_ms !== undefined &&\n (!Number.isInteger(n.usage_refresh_ms) || n.usage_refresh_ms < 1000)\n ? [\"usage_refresh_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"models_refresh_ms\",\n errors: (n) =>\n n.models_refresh_ms !== undefined &&\n (!Number.isInteger(n.models_refresh_ms) || n.models_refresh_ms < 1000)\n ? [\"models_refresh_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"concurrency_hard_cap\",\n errors: (n) =>\n n.concurrency_hard_cap !== undefined &&\n (!Number.isInteger(n.concurrency_hard_cap) || n.concurrency_hard_cap < 1)\n ? [\"concurrency_hard_cap must be an integer >= 1\"]\n : [],\n },\n {\n name: \"concurrency_soft_limit\",\n errors: (n) =>\n n.concurrency_soft_limit !== undefined &&\n (!Number.isInteger(n.concurrency_soft_limit) || n.concurrency_soft_limit < 1)\n ? [\"concurrency_soft_limit must be an integer >= 1\"]\n : [],\n },\n {\n // Cross-field: only checked when hard_cap is an integer >= 3.\n name: \"concurrency_main_reservation\",\n errors: (n) => {\n if (\n n.concurrency_hard_cap === undefined ||\n !Number.isInteger(n.concurrency_hard_cap) ||\n n.concurrency_main_reservation === undefined\n ) {\n return [];\n }\n const resMax = n.concurrency_hard_cap - 2;\n if (resMax < 1) return [];\n if (!Number.isInteger(n.concurrency_main_reservation) || n.concurrency_main_reservation < 1) {\n return [\"concurrency_main_reservation must be a positive integer (min 1)\"];\n }\n if (n.concurrency_main_reservation > resMax) {\n return [`concurrency_main_reservation must be <= hard_cap - 2 (=${resMax})`];\n }\n return [];\n },\n },\n {\n // Cross-field: only checked when hard_cap is an integer >= 3.\n name: \"concurrency_vision_reservation\",\n errors: (n) => {\n if (\n n.concurrency_hard_cap === undefined ||\n !Number.isInteger(n.concurrency_hard_cap) ||\n n.concurrency_vision_reservation === undefined\n ) {\n return [];\n }\n const resMax = n.concurrency_hard_cap - 2;\n if (resMax < 1) return [];\n if (\n !Number.isInteger(n.concurrency_vision_reservation) ||\n n.concurrency_vision_reservation < 1\n ) {\n return [\"concurrency_vision_reservation must be a positive integer (min 1)\"];\n }\n if (n.concurrency_vision_reservation > resMax) {\n return [`concurrency_vision_reservation must be <= hard_cap - 2 (=${resMax})`];\n }\n return [];\n },\n },\n {\n name: \"rate_limit_requests\",\n errors: (n) =>\n n.rate_limit_requests !== undefined &&\n n.rate_limit_requests !== null &&\n (!Number.isInteger(n.rate_limit_requests) || n.rate_limit_requests < -1)\n ? [\n \"rate_limit_requests must be -1 (unlimited), 0 (auto-derive from /v1/usage), or a positive integer\",\n ]\n : [],\n },\n {\n name: \"queue_timeout_ms\",\n errors: (n) =>\n n.queue_timeout_ms !== undefined &&\n (!Number.isInteger(n.queue_timeout_ms) || n.queue_timeout_ms < 100)\n ? [\"queue_timeout_ms must be an integer >= 100\"]\n : [],\n },\n {\n name: \"max_queue_depth\",\n errors: (n) =>\n n.max_queue_depth !== undefined &&\n (!Number.isInteger(n.max_queue_depth) || n.max_queue_depth < 1)\n ? [\"max_queue_depth must be a positive integer\"]\n : [],\n },\n {\n name: \"release_cooldown_ms\",\n errors: (n) =>\n n.release_cooldown_ms !== undefined &&\n (!Number.isInteger(n.release_cooldown_ms) || n.release_cooldown_ms < 0)\n ? [\"release_cooldown_ms must be a non-negative integer\"]\n : [],\n },\n {\n name: \"breaker_threshold\",\n errors: (n) =>\n n.breaker_threshold !== undefined &&\n (!Number.isInteger(n.breaker_threshold) || n.breaker_threshold < 1)\n ? [\"breaker_threshold must be a positive integer\"]\n : [],\n },\n {\n name: \"breaker_window_ms\",\n errors: (n) =>\n n.breaker_window_ms !== undefined &&\n (!Number.isInteger(n.breaker_window_ms) || n.breaker_window_ms < 1000)\n ? [\"breaker_window_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"breaker_cooldown_ms\",\n errors: (n) =>\n n.breaker_cooldown_ms !== undefined &&\n (!Number.isInteger(n.breaker_cooldown_ms) || n.breaker_cooldown_ms < 1000)\n ? [\"breaker_cooldown_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"vision_strategy\",\n errors: (n) =>\n n.vision_strategy !== undefined && ![\"never\", \"catalog\", \"always\"].includes(n.vision_strategy)\n ? [\"vision_strategy must be 'never', 'catalog', or 'always'\"]\n : [],\n },\n {\n name: \"vision_model\",\n errors: (n) =>\n n.vision_model !== undefined && typeof n.vision_model !== \"string\"\n ? [\"vision_model must be a string\"]\n : [],\n },\n {\n name: \"vision_prompt\",\n errors: (n) =>\n n.vision_prompt !== undefined &&\n (typeof n.vision_prompt !== \"string\" || n.vision_prompt.length === 0)\n ? [\"vision_prompt must be a non-empty string\"]\n : [],\n },\n {\n name: \"vision_prompt_version\",\n errors: (n) =>\n n.vision_prompt_version !== undefined &&\n (!Number.isInteger(n.vision_prompt_version) || n.vision_prompt_version < 1)\n ? [\"vision_prompt_version must be a positive integer\"]\n : [],\n },\n {\n name: \"vision_max_images\",\n errors: (n) =>\n n.vision_max_images !== undefined &&\n (!Number.isInteger(n.vision_max_images) ||\n n.vision_max_images < 1 ||\n n.vision_max_images > 100)\n ? [\"vision_max_images must be an integer between 1 and 100\"]\n : [],\n },\n {\n name: \"vision_max_description_tokens\",\n errors: (n) =>\n n.vision_max_description_tokens !== undefined &&\n (!Number.isInteger(n.vision_max_description_tokens) ||\n n.vision_max_description_tokens < 1 ||\n n.vision_max_description_tokens > 200000)\n ? [\"vision_max_description_tokens must be an integer between 1 and 200000\"]\n : [],\n },\n {\n name: \"vision_timeout_ms\",\n errors: (n) =>\n n.vision_timeout_ms !== undefined &&\n (!Number.isInteger(n.vision_timeout_ms) || n.vision_timeout_ms < 0)\n ? [\"vision_timeout_ms must be a non-negative integer (0 = no timeout)\"]\n : [],\n },\n {\n name: \"vision_cache_size\",\n errors: (n) =>\n n.vision_cache_size !== undefined &&\n (!Number.isInteger(n.vision_cache_size) || n.vision_cache_size < 100)\n ? [\"vision_cache_size must be an integer >= 100\"]\n : [],\n },\n {\n name: \"vision_concurrency\",\n errors: (n) =>\n n.vision_concurrency !== undefined &&\n (!Number.isInteger(n.vision_concurrency) ||\n n.vision_concurrency < 1 ||\n n.vision_concurrency > 20)\n ? [\"vision_concurrency must be an integer between 1 and 20\"]\n : [],\n },\n {\n name: \"vision_reasoning_effort\",\n errors: (n) =>\n n.vision_reasoning_effort !== undefined &&\n n.vision_reasoning_effort !== null &&\n ![\"none\", \"low\", \"medium\", \"high\"].includes(n.vision_reasoning_effort)\n ? [\"vision_reasoning_effort must be 'none', 'low', 'medium', 'high', or null\"]\n : [],\n },\n {\n name: \"vision_max_dimension\",\n errors: (n) =>\n n.vision_max_dimension !== undefined &&\n (!Number.isInteger(n.vision_max_dimension) ||\n n.vision_max_dimension < 256 ||\n n.vision_max_dimension > 8192)\n ? [\"vision_max_dimension must be an integer between 256 and 8192\"]\n : [],\n },\n {\n name: \"vision_jpeg_quality\",\n errors: (n) =>\n n.vision_jpeg_quality !== undefined &&\n (!Number.isInteger(n.vision_jpeg_quality) ||\n n.vision_jpeg_quality < 1 ||\n n.vision_jpeg_quality > 100)\n ? [\"vision_jpeg_quality must be an integer between 1 and 100\"]\n : [],\n },\n {\n name: \"vision_image_format\",\n errors: (n) =>\n n.vision_image_format !== undefined && ![\"jpeg\", \"png\"].includes(n.vision_image_format)\n ? [\"vision_image_format must be 'jpeg' or 'png'\"]\n : [],\n },\n {\n name: \"vision_image_detail\",\n errors: (n) =>\n n.vision_image_detail !== undefined &&\n ![\"auto\", \"low\", \"high\"].includes(n.vision_image_detail)\n ? [\"vision_image_detail must be 'auto', 'low', or 'high'\"]\n : [],\n },\n {\n name: \"vision_cache_ttl_ms\",\n errors: (n) =>\n n.vision_cache_ttl_ms !== undefined &&\n (!Number.isInteger(n.vision_cache_ttl_ms) || n.vision_cache_ttl_ms < 1000)\n ? [\"vision_cache_ttl_ms must be an integer >= 1000\"]\n : [],\n },\n {\n name: \"vision_cache_max_rows\",\n errors: (n) =>\n n.vision_cache_max_rows !== undefined &&\n (!Number.isInteger(n.vision_cache_max_rows) || n.vision_cache_max_rows < 100)\n ? [\"vision_cache_max_rows must be an integer >= 100\"]\n : [],\n },\n {\n name: \"vision_persistent_cache\",\n errors: (n) =>\n n.vision_persistent_cache !== undefined && typeof n.vision_persistent_cache !== \"boolean\"\n ? [\"vision_persistent_cache must be a boolean\"]\n : [],\n },\n {\n name: \"capture_body_max_bytes\",\n errors: (n) =>\n n.capture_body_max_bytes !== undefined &&\n (!Number.isInteger(n.capture_body_max_bytes) || n.capture_body_max_bytes < 0)\n ? [\"capture_body_max_bytes must be a non-negative integer (0 = unlimited)\"]\n : [],\n },\n {\n name: \"queue_max_depth\",\n errors: (n) =>\n n.queue_max_depth !== undefined &&\n (!Number.isInteger(n.queue_max_depth) || n.queue_max_depth < 1)\n ? [\"queue_max_depth must be a positive integer\"]\n : [],\n },\n {\n name: \"ws_backpressure_limit\",\n errors: (n) =>\n n.ws_backpressure_limit !== undefined &&\n (!Number.isInteger(n.ws_backpressure_limit) || n.ws_backpressure_limit < 0)\n ? [\"ws_backpressure_limit must be a non-negative integer (0 = Bun default)\"]\n : [],\n },\n {\n name: \"ws_close_on_backpressure_limit\",\n errors: (n) =>\n n.ws_close_on_backpressure_limit !== undefined &&\n typeof n.ws_close_on_backpressure_limit !== \"boolean\"\n ? [\"ws_close_on_backpressure_limit must be a boolean\"]\n : [],\n },\n {\n name: \"vision_pending_max_batch\",\n errors: (n) =>\n n.vision_pending_max_batch !== undefined &&\n (!Number.isInteger(n.vision_pending_max_batch) || n.vision_pending_max_batch < 1)\n ? [\"vision_pending_max_batch must be a positive integer\"]\n : [],\n },\n];\n\nconst WARNING_RULES: WarningRule[] = [\n {\n name: \"warmer_disabled\",\n warning: (n) =>\n n.warmer_enabled === false\n ? \"Connection warmer is disabled — first request after idle will have ~750ms cold-start penalty\"\n : null,\n },\n {\n name: \"rate_limit_disabled\",\n warning: (n) =>\n n.rate_limit_requests === -1\n ? \"Rate limiting is unlimited (rate_limit_requests=-1). No request cap is enforced.\"\n : null,\n },\n {\n name: \"stamp_claude_code_off\",\n warning: (n) =>\n n.stamp_claude_code_enabled !== true\n ? \"Claude Code stamping is off — ephemeral cache entries will have no default TTL, no top_k/max_tokens/thinking/output_config/context_management injection\"\n : null,\n },\n {\n name: \"umans_api_key_empty\",\n warning: (n) =>\n n.umans_api_key === \"\" || n.umans_api_key === undefined\n ? \"umans_api_key is empty — proxy runs in fail-safe mode (worst-case limits, priority_low=true). Set umans_api_key in the Server section to enable usage-based limits.\"\n : null,\n },\n];\n\n/**\n * Validate a raw config object. Returns errors (blocking), warnings (non-blocking),\n * and a normalized copy with defaults filled in.\n */\nexport function validateConfig(raw: RawConfigInput): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const coerced = coerceRawForValidation(raw);\n const n: RawConfig = { ...DEFAULT_CONFIG, ...coerced };\n\n for (const rule of FIELD_RULES) {\n errors.push(...rule.errors(n));\n }\n\n for (const rule of WARNING_RULES) {\n const msg = rule.warning(n);\n if (msg !== null) {\n warnings.push(msg);\n }\n }\n\n return { ok: errors.length === 0, errors, warnings, normalized: n };\n}\n\n/**\n * Write the default config template if no config file exists.\n */\nexport function ensureConfigFile(): string {\n const path = resolveConfigPath();\n if (!existsSync(path)) {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(DEFAULT_CONFIG, null, 2), \"utf-8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n // File permissions are best-effort — some filesystems (e.g. Windows) don't support chmod.\n }\n }\n return path;\n}\n\nfunction resolveUpstreamProtocol(raw: string | undefined): UpstreamProtocol {\n const v = (raw ?? \"http1.1\").toLowerCase();\n if (v === \"http2\" || v === \"h2\") return \"http2\";\n return \"http1.1\";\n}\n\nfunction num(val: number | string | undefined | null, fallback: number): number {\n if (val === undefined || val === null) return fallback;\n const n = Number(val);\n return Number.isNaN(n) ? fallback : n;\n}\n\nfunction str(val: string | undefined, fallback: string): string {\n return val ?? fallback;\n}\n\nfunction loadJsonConfig(path: string): RawConfig {\n if (!existsSync(path)) return {};\n try {\n const text = readFileSync(path, \"utf-8\");\n const parsed = JSON.parse(text) as RawConfig;\n return parsed ?? {};\n } catch {\n return {};\n }\n}\n\n/**\n * Coerce a raw config value to a boolean from various input shapes.\n * Accepts true/false, \"true\"/\"false\", 1/0.\n */\nfunction bool(val: unknown, fallback: boolean): boolean {\n if (val === undefined || val === null) return fallback;\n if (typeof val === \"boolean\") return val;\n if (typeof val === \"string\") return val === \"true\" || val === \"1\";\n if (typeof val === \"number\") return val !== 0;\n return fallback;\n}\n\nfunction envOrRawNum(\n envVal: string | undefined,\n raw: RawConfig,\n key: keyof RawConfig,\n fallback: number,\n): number {\n if (envVal !== undefined) return num(envVal, fallback);\n const rawVal = raw[key];\n return typeof rawVal === \"number\" && !Number.isNaN(rawVal) ? rawVal : fallback;\n}\n\nfunction envOrRawBool(\n envVal: string | undefined,\n raw: RawConfig,\n key: keyof RawConfig,\n fallback: boolean,\n): boolean {\n if (envVal !== undefined) return bool(envVal, fallback);\n const rawVal = raw[key];\n return typeof rawVal === \"boolean\" ? rawVal : fallback;\n}\n\n/**\n * Load configuration.\n * Precedence: env vars > JSON config file > defaults.\n * Writes the default config on first run if no file exists.\n * Removed-from-config fields (target, openai_path, warmer_path, vision_target,\n * rate_limit_window_seconds) are hardcoded — app is Umans-specific.\n * Misconfigured numeric fields fall back to their defaults.\n */\nexport function loadConfig(env: Record<string, string | undefined> = process.env): ProxyConfig {\n const configPath = ensureConfigFile();\n const raw = loadJsonConfig(configPath);\n\n const port = num(env.PORT ?? raw.port, 1945);\n const host = \"127.0.0.1\";\n const target = (env.TARGET ?? UPSTREAM_TARGET).replace(/\\/+$/, \"\");\n const maxCaptures = num(env.MAX_CAPTURES ?? raw.max_captures, 200);\n const dbPath = str(env.DB_PATH ?? raw.db_path, \"./umans-gate.db\");\n const idleTimeout = Math.min(num(env.IDLE_TIMEOUT ?? raw.idle_timeout, 255), 255);\n const upstreamProtocol = resolveUpstreamProtocol(env.UPSTREAM_PROTOCOL ?? raw.upstream_protocol);\n const stampClaudeCode = bool(\n env.STAMP_CLAUDE_CODE_ENABLED ?? raw.stamp_claude_code_enabled,\n false,\n );\n const stampReasoningEffortEnabled = bool(\n env.STAMP_REASONING_EFFORT_ENABLED ?? raw.stamp_reasoning_effort_enabled,\n false,\n );\n const stampReasoningEffort = stampReasoningEffortEnabled ? STAMP_REASONING_EFFORT_VALUE : null;\n const openaiPath = OPENAI_CHAT_PATH;\n const warmerEnabled =\n env.WARMER_ENABLED !== undefined\n ? env.WARMER_ENABLED !== \"false\" && env.WARMER_ENABLED !== \"0\"\n : raw.warmer_enabled !== false;\n const warmerIntervalMs = num(env.WARMER_INTERVAL_MS ?? raw.warmer_interval_ms, 20000);\n const warmerPath = WARMER_PATH;\n\n const umansApiKey = env.UMANS_API_KEY ?? raw.umans_api_key ?? null;\n const usageRefreshMs = num(env.USAGE_REFRESH_MS ?? raw.usage_refresh_ms, 60000);\n const modelsRefreshMs = num(env.MODELS_REFRESH_MS ?? raw.models_refresh_ms, 3600000);\n const concurrencyHardCap = num(env.CONCURRENCY_HARD_CAP ?? raw.concurrency_hard_cap, 1);\n const concurrencySoftLimit = num(env.CONCURRENCY_SOFT_LIMIT ?? raw.concurrency_soft_limit, 1);\n const rateLimitRequests = num(env.RATE_LIMIT_REQUESTS ?? raw.rate_limit_requests, 0);\n const queueTimeoutMs = num(env.QUEUE_TIMEOUT_MS ?? raw.queue_timeout_ms, 30000);\n const maxQueueDepth = num(env.MAX_QUEUE_DEPTH ?? raw.max_queue_depth, 256);\n const releaseCooldownMs = num(env.RELEASE_COOLDOWN_MS ?? raw.release_cooldown_ms, 1000);\n const breakerThreshold = num(env.BREAKER_THRESHOLD ?? raw.breaker_threshold, 5);\n const breakerWindowMs = num(env.BREAKER_WINDOW_MS ?? raw.breaker_window_ms, 300000);\n const breakerCooldownMs = num(env.BREAKER_COOLDOWN_MS ?? raw.breaker_cooldown_ms, 60000);\n\n const visionStrategy = str(env.VISION_STRATEGY ?? raw.vision_strategy, \"catalog\") as\n | \"never\"\n | \"catalog\"\n | \"always\";\n const visionTarget =\n env.VISION_TARGET ?? `${UPSTREAM_TARGET.replace(/\\/+$/, \"\")}${VISION_TARGET_PATH}`;\n const visionModel = env.VISION_MODEL ?? raw.vision_model ?? \"umans-flash\";\n const visionPrompt = str(\n env.VISION_PROMPT ?? raw.vision_prompt,\n DEFAULT_CONFIG.vision_prompt ?? \"Describe this image concisely.\",\n );\n const visionPromptVersion = num(env.VISION_PROMPT_VERSION ?? raw.vision_prompt_version, 2);\n const visionMaxImages = num(env.VISION_MAX_IMAGES ?? raw.vision_max_images, 5);\n const visionMaxDescriptionTokens = num(\n env.VISION_MAX_DESCRIPTION_TOKENS ?? raw.vision_max_description_tokens,\n 4096,\n );\n const visionReasoningEffortRaw = env.VISION_REASONING_EFFORT ?? raw.vision_reasoning_effort;\n const visionReasoningEffort =\n visionReasoningEffortRaw === undefined || visionReasoningEffortRaw === null\n ? null\n : (visionReasoningEffortRaw as \"none\" | \"low\" | \"medium\" | \"high\");\n const visionTimeoutMs = num(env.VISION_TIMEOUT_MS ?? raw.vision_timeout_ms, 0);\n const visionCacheSize = num(env.VISION_CACHE_SIZE ?? raw.vision_cache_size, 1000);\n const visionCacheTtlMs = num(env.VISION_CACHE_TTL_MS ?? raw.vision_cache_ttl_ms, 604800000);\n const visionCacheMaxRows = num(env.VISION_CACHE_MAX_ROWS ?? raw.vision_cache_max_rows, 10000);\n const visionPersistentCache =\n env.VISION_PERSISTENT_CACHE !== undefined\n ? env.VISION_PERSISTENT_CACHE !== \"false\" && env.VISION_PERSISTENT_CACHE !== \"0\"\n : raw.vision_persistent_cache !== false;\n const visionConcurrency = num(env.VISION_CONCURRENCY ?? raw.vision_concurrency, 1);\n const visionMaxDimension = num(env.VISION_MAX_DIMENSION ?? raw.vision_max_dimension, 2048);\n const visionJpegQuality = num(env.VISION_JPEG_QUALITY ?? raw.vision_jpeg_quality, 92);\n const visionImageFormat = (env.VISION_IMAGE_FORMAT ?? raw.vision_image_format ?? \"png\") as\n | \"jpeg\"\n | \"png\";\n const visionImageDetail = (env.VISION_IMAGE_DETAIL ?? raw.vision_image_detail ?? \"high\") as\n | \"auto\"\n | \"low\"\n | \"high\";\n // Derived from vision_strategy: \"catalog\" uses cache-first (background) mode,\n // \"always\" forces intercept even for vision-capable models.\n const backgroundVision = visionStrategy === \"catalog\";\n const concurrencyMainReservation = num(\n env.CONCURRENCY_MAIN_RESERVATION ?? raw.concurrency_main_reservation,\n 1,\n );\n const concurrencyVisionReservation = num(\n env.CONCURRENCY_VISION_RESERVATION ?? raw.concurrency_vision_reservation,\n 1,\n );\n\n const visionForceInterceptCapable = visionStrategy === \"always\";\n\n const captureBodyMaxBytes = envOrRawNum(\n env.CAPTURE_BODY_MAX_BYTES,\n raw,\n \"capture_body_max_bytes\",\n DEFAULT_CONFIG.capture_body_max_bytes ?? 1_000_000,\n );\n const queueMaxDepth = envOrRawNum(\n env.QUEUE_MAX_DEPTH,\n raw,\n \"queue_max_depth\",\n DEFAULT_CONFIG.queue_max_depth ?? 100,\n );\n const wsBackpressureLimit = envOrRawNum(\n env.WS_BACKPRESSURE_LIMIT,\n raw,\n \"ws_backpressure_limit\",\n DEFAULT_CONFIG.ws_backpressure_limit ?? 1_048_576,\n );\n const wsCloseOnBackpressureLimit = envOrRawBool(\n env.WS_CLOSE_ON_BACKPRESSURE_LIMIT,\n raw,\n \"ws_close_on_backpressure_limit\",\n DEFAULT_CONFIG.ws_close_on_backpressure_limit ?? true,\n );\n const visionPendingMaxBatch = envOrRawNum(\n env.VISION_PENDING_MAX_BATCH,\n raw,\n \"vision_pending_max_batch\",\n DEFAULT_CONFIG.vision_pending_max_batch ?? 50,\n );\n const compressionEnabled = envOrRawBool(\n env.COMPRESSION_ENABLED,\n raw,\n \"compression_enabled\",\n DEFAULT_CONFIG.compression_enabled ?? true,\n );\n const useWriteWorker = false;\n const upstreamTimeoutMs = envOrRawNum(\n env.UPSTREAM_TIMEOUT_MS,\n raw,\n \"upstream_timeout_ms\",\n DEFAULT_CONFIG.upstream_timeout_ms ?? 300000,\n );\n\n return {\n port,\n host,\n target,\n maxCaptures,\n dbPath,\n viewerPrefix: \"/dashboard\",\n flushIntervalMs: 50,\n flushBatch: 25,\n idleTimeout,\n upstreamProtocol,\n incomingProtocol: \"http1.1\" as IncomingProtocol,\n stampClaudeCode,\n stampReasoningEffort,\n openaiPath,\n warmerEnabled,\n warmerIntervalMs,\n warmerPath,\n umansApiKey,\n usageRefreshMs,\n modelsRefreshMs,\n concurrencyHardCap,\n concurrencySoftLimit,\n rateLimitRequests,\n queueTimeoutMs,\n maxQueueDepth,\n releaseCooldownMs,\n breakerThreshold,\n breakerWindowMs,\n breakerCooldownMs,\n visionStrategy,\n visionTarget,\n visionModel,\n visionPrompt,\n visionPromptVersion,\n visionMaxImages,\n visionMaxDescriptionTokens,\n visionReasoningEffort,\n visionTimeoutMs,\n visionCacheSize,\n visionCacheTtlMs,\n visionCacheMaxRows,\n visionPersistentCache,\n visionConcurrency,\n visionMaxDimension,\n visionJpegQuality,\n visionImageFormat,\n visionImageDetail,\n backgroundVision,\n concurrencyMainReservation,\n concurrencyVisionReservation,\n visionForceInterceptCapable,\n captureBodyMaxBytes,\n queueMaxDepth,\n wsBackpressureLimit,\n wsCloseOnBackpressureLimit,\n visionPendingMaxBatch,\n compressionEnabled,\n useWriteWorker,\n upstreamTimeoutMs,\n };\n}\n\n/**\n * Read the raw config.json from disk (for the config UI).\n * Returns defaults merged with the file contents (no env override).\n */\nexport function readConfigFile(): RawConfig {\n const path = ensureConfigFile();\n const raw = loadJsonConfig(path);\n return { ...DEFAULT_CONFIG, ...raw };\n}\n\n/**\n * Save a partial config to disk (validate first, merge with existing).\n * Returns validation result + the merged config that was written.\n */\nexport function saveConfig(patch: RawConfigInput): {\n ok: boolean;\n errors: string[];\n warnings: string[];\n written: RawConfig | null;\n} {\n const existing = readConfigFile();\n const merged: RawConfig = { ...existing, ...coerceRawForValidation(patch) };\n const result = validateConfig(merged);\n if (!result.ok) {\n return { ok: false, errors: result.errors, warnings: result.warnings, written: null };\n }\n const path = resolveConfigPath();\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(result.normalized, null, 2), \"utf-8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n // Best-effort: not all platforms support chmod.\n }\n return { ok: true, errors: [], warnings: result.warnings, written: result.normalized };\n}\n\n/**\n * Reset config to defaults on disk, preserving `umans_api_key` so the user is\n * not locked out of the upstream. Returns the written config.\n */\nexport function resetConfig(): { ok: boolean; written: RawConfig | null } {\n const existing = readConfigFile();\n const reset: RawConfig = {\n ...DEFAULT_CONFIG,\n umans_api_key: existing.umans_api_key ?? DEFAULT_CONFIG.umans_api_key,\n };\n const path = resolveConfigPath();\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(reset, null, 2), \"utf-8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n // Best-effort: not all platforms support chmod.\n }\n return { ok: true, written: reset };\n}\n\n/**\n * Fields that require a server restart to take effect (cannot be hot-reloaded).\n * Everything else can be applied to the live ProxyConfig in-place via reloadConfig().\n */\nconst RESTART_REQUIRED_FIELDS = new Set<keyof RawConfig>([\n \"port\",\n \"max_captures\",\n \"db_path\",\n \"idle_timeout\",\n \"upstream_protocol\",\n \"warmer_enabled\",\n \"warmer_interval_ms\",\n \"usage_refresh_ms\",\n \"umans_api_key\",\n \"models_refresh_ms\",\n \"vision_strategy\",\n \"vision_model\",\n \"vision_prompt\",\n \"vision_prompt_version\",\n \"vision_max_images\",\n \"vision_max_description_tokens\",\n \"vision_reasoning_effort\",\n \"vision_timeout_ms\",\n \"vision_cache_size\",\n \"vision_cache_ttl_ms\",\n \"vision_cache_max_rows\",\n \"vision_persistent_cache\",\n \"vision_max_dimension\",\n \"vision_jpeg_quality\",\n \"vision_image_format\",\n \"vision_image_detail\",\n \"vision_concurrency\",\n]);\n\n/**\n * Table of hot-reloadable raw keys and the in-place assignment each performs\n * on the live ProxyConfig. Drives the data-driven apply loop below.\n */\nconst RELOAD_FIELDS: Array<{\n rawKey: keyof RawConfig;\n apply: (live: ProxyConfig, fresh: ProxyConfig) => void;\n}> = [\n {\n rawKey: \"stamp_claude_code_enabled\",\n apply: (live, fresh) => {\n live.stampClaudeCode = fresh.stampClaudeCode;\n },\n },\n {\n rawKey: \"stamp_reasoning_effort_enabled\",\n apply: (live, fresh) => {\n live.stampReasoningEffort = fresh.stampReasoningEffort;\n },\n },\n {\n rawKey: \"rate_limit_requests\",\n apply: (live, fresh) => {\n live.rateLimitRequests = fresh.rateLimitRequests;\n },\n },\n {\n rawKey: \"queue_timeout_ms\",\n apply: (live, fresh) => {\n live.queueTimeoutMs = fresh.queueTimeoutMs;\n },\n },\n {\n rawKey: \"max_queue_depth\",\n apply: (live, fresh) => {\n live.maxQueueDepth = fresh.maxQueueDepth;\n },\n },\n {\n rawKey: \"release_cooldown_ms\",\n apply: (live, fresh) => {\n live.releaseCooldownMs = fresh.releaseCooldownMs;\n },\n },\n {\n rawKey: \"breaker_threshold\",\n apply: (live, fresh) => {\n live.breakerThreshold = fresh.breakerThreshold;\n },\n },\n {\n rawKey: \"breaker_window_ms\",\n apply: (live, fresh) => {\n live.breakerWindowMs = fresh.breakerWindowMs;\n },\n },\n {\n rawKey: \"breaker_cooldown_ms\",\n apply: (live, fresh) => {\n live.breakerCooldownMs = fresh.breakerCooldownMs;\n },\n },\n {\n rawKey: \"concurrency_main_reservation\",\n apply: (live, fresh) => {\n live.concurrencyMainReservation = fresh.concurrencyMainReservation;\n },\n },\n {\n rawKey: \"concurrency_vision_reservation\",\n apply: (live, fresh) => {\n live.concurrencyVisionReservation = fresh.concurrencyVisionReservation;\n },\n },\n {\n rawKey: \"concurrency_hard_cap\",\n apply: (live, fresh) => {\n live.concurrencyHardCap = fresh.concurrencyHardCap;\n },\n },\n {\n rawKey: \"concurrency_soft_limit\",\n apply: (live, fresh) => {\n live.concurrencySoftLimit = fresh.concurrencySoftLimit;\n },\n },\n {\n rawKey: \"compression_enabled\",\n apply: (live, fresh) => {\n live.compressionEnabled = fresh.compressionEnabled;\n },\n },\n {\n rawKey: \"capture_body_max_bytes\",\n apply: (live, fresh) => {\n live.captureBodyMaxBytes = fresh.captureBodyMaxBytes;\n },\n },\n];\n\n/**\n * Apply reloaded config to a live ProxyConfig in-place.\n * Only applies fields that can be hot-reloaded; flags restart-required changes.\n * Returns lists of applied fields and restart-required fields.\n */\nexport function applyReloadToConfig(\n live: ProxyConfig,\n fresh: ProxyConfig,\n oldRaw: RawConfig,\n newRaw: RawConfig,\n): { applied: string[]; restartRequired: string[] } {\n const applied: string[] = [];\n const restartRequired: string[] = [];\n\n // Compare raw keys to detect changes.\n for (const key of Object.keys(newRaw) as (keyof RawConfig)[]) {\n const oldVal = oldRaw[key];\n const newVal = newRaw[key];\n const changed = JSON.stringify(oldVal) !== JSON.stringify(newVal);\n if (!changed) continue;\n\n if (RESTART_REQUIRED_FIELDS.has(key)) {\n restartRequired.push(key);\n } else {\n applied.push(key);\n }\n }\n\n // Apply hot-reloadable fields to the live ProxyConfig.\n // These are the fields that proxy.ts and stamp.ts read per-request.\n for (const { rawKey, apply } of RELOAD_FIELDS) {\n if (applied.includes(rawKey)) apply(live, fresh);\n }\n\n return { applied, restartRequired };\n}\n","// Self-update logic for umans-gate.\n// Detects install method (npm global, standalone executable, or bun dev)\n// and performs the appropriate update action.\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\n\nconst GITHUB_API = \"https://api.github.com/repos/codegiveness/umans-gate/releases/latest\";\n\ninterface GithubRelease {\n tag_name: string;\n assets: Array<{ name: string; browser_download_url: string }>;\n}\n\n/** Fetch latest version from GitHub Releases. */\nasync function fetchLatestVersion(): Promise<string | null> {\n try {\n const resp = await fetch(GITHUB_API, {\n headers: { \"User-Agent\": \"umans-gate-updater\" },\n });\n if (!resp.ok) return null;\n const data = (await resp.json()) as GithubRelease;\n return data.tag_name.replace(/^v/, \"\");\n } catch {\n return null;\n }\n}\n\n/** Check if running as a compiled standalone executable. */\nfunction isCompiledExecutable(): boolean {\n // In compiled mode, process.execPath points to the binary itself,\n // not to a bun/node executable.\n const execPath = process.execPath;\n return existsSync(execPath) && execPath.includes(\"umans-gate\");\n}\n\n/** Check if running as npm global install. */\nfunction isNpmGlobal(): boolean {\n try {\n const npmRoot = execSync(\"npm root -g\", { encoding: \"utf-8\" }).trim();\n return existsSync(`${npmRoot}/umans-gate`);\n } catch {\n return false;\n }\n}\n\n/** Compare two semver strings (returns -1, 0, 1). */\nfunction compareVersions(a: string, b: string): number {\n const pa = a.split(\".\").map(Number);\n const pb = b.split(\".\").map(Number);\n for (let i = 0; i < Math.max(pa.length, pb.length); i++) {\n const va = pa[i] ?? 0;\n const vb = pb[i] ?? 0;\n if (va < vb) return -1;\n if (va > vb) return 1;\n }\n return 0;\n}\n\n/** Check for available update without installing. Prints result and exits. */\nexport async function checkForUpdate(currentVersion: string): Promise<void> {\n console.log(\"Checking for updates...\");\n const latest = await fetchLatestVersion();\n if (!latest) {\n console.error(\"Could not fetch latest version from GitHub.\");\n process.exit(1);\n }\n\n const cmp = compareVersions(currentVersion, latest);\n if (cmp < 0) {\n console.log(`Update available: ${currentVersion} → ${latest}`);\n console.log(\"Run `umans-gate update` to install.\");\n } else if (cmp === 0) {\n console.log(`Already up to date (v${currentVersion}).`);\n } else {\n console.log(`Running ahead of latest release (${currentVersion} > ${latest}).`);\n }\n}\n\n/** Perform the update based on install method. */\nexport async function performUpdate(currentVersion: string): Promise<void> {\n console.log(\"Checking for updates...\");\n const latest = await fetchLatestVersion();\n if (!latest) {\n console.error(\"Could not fetch latest version from GitHub.\");\n process.exit(1);\n }\n\n const cmp = compareVersions(currentVersion, latest);\n if (cmp >= 0) {\n console.log(`Already up to date (v${currentVersion}).`);\n return;\n }\n\n console.log(`Updating: ${currentVersion} → ${latest}`);\n\n if (isNpmGlobal()) {\n console.log(\"Install method: npm global\");\n try {\n execSync(\"npm update -g umans-gate\", { stdio: \"inherit\" });\n console.log(\"Update complete.\");\n } catch {\n console.error(\"npm update failed. Try manually: npm install -g umans-gate@latest\");\n process.exit(1);\n }\n } else if (isCompiledExecutable()) {\n console.log(\"Install method: standalone executable\");\n console.log(\"Please download the latest binary from:\");\n console.log(\" https://github.com/codegiveness/umans-gate/releases/latest\");\n console.log(\"Or reinstall via the install script:\");\n console.log(\n \" curl -fsSL https://raw.githubusercontent.com/codegiveness/umans-gate/main/install.sh | sh\",\n );\n } else {\n console.log(\"Install method: development\");\n console.log(\"Pull the latest changes and reinstall:\");\n console.log(\" git pull && bun install && bun run build\");\n }\n}\n","// Uninstall logic for umans-gate.\n// Removes the standalone binary, npm global package, and optionally config files.\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, rmSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { resolveConfigPath } from \"./config.js\";\n\ninterface UninstallOptions {\n keepConfig: boolean;\n}\n\n/** Check if running as a compiled standalone executable. */\nfunction isCompiledExecutable(): boolean {\n const execPath = process.execPath;\n return existsSync(execPath) && execPath.includes(\"umans-gate\");\n}\n\n/** Check if running as npm global install. */\nfunction isNpmGlobal(): boolean {\n try {\n const npmRoot = execSync(\"npm root -g\", { encoding: \"utf-8\" }).trim();\n return existsSync(`${npmRoot}/umans-gate`);\n } catch {\n return false;\n }\n}\n\n/** Remove the standalone executable binary. */\nfunction removeStandaloneBinary(): void {\n const execPath = process.execPath;\n if (existsSync(execPath) && execPath.includes(\"umans-gate\")) {\n const dir = dirname(execPath);\n try {\n rmSync(execPath);\n console.log(`Removed binary: ${execPath}`);\n // Remove symlink if exists (e.g. in /usr/local/bin)\n const symlinkPath = join(\"/usr/local/bin\", \"umans-gate\");\n if (existsSync(symlinkPath)) {\n rmSync(symlinkPath);\n console.log(`Removed symlink: ${symlinkPath}`);\n }\n // If the binary directory is now empty, remove it\n const { readdirSync, rmdirSync } = require(\"node:fs\");\n if (readdirSync(dir).length === 0) {\n rmdirSync(dir);\n console.log(`Removed empty directory: ${dir}`);\n }\n } catch (err) {\n console.error(`Failed to remove binary: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n}\n\n/** Remove the npm global package. */\nfunction removeNpmGlobal(): void {\n try {\n execSync(\"npm uninstall -g umans-gate\", { stdio: \"inherit\" });\n console.log(\"Removed npm global package: umans-gate\");\n } catch {\n console.error(\"Failed to uninstall npm package. Try: npm uninstall -g umans-gate\");\n }\n}\n\n/** Remove configuration files. */\nfunction removeConfig(): void {\n const configPath = resolveConfigPath();\n if (existsSync(configPath)) {\n try {\n rmSync(configPath);\n console.log(`Removed config: ${configPath}`);\n } catch (err) {\n console.error(`Failed to remove config: ${err instanceof Error ? err.message : String(err)}`);\n }\n } else {\n console.log(\"No config file found.\");\n }\n}\n\n/** Perform the uninstall based on install method and options. */\nexport async function uninstall(options: UninstallOptions): Promise<void> {\n console.log(\"Uninstalling umans-gate...\\n\");\n\n let removed = false;\n\n if (isNpmGlobal()) {\n console.log(\"Install method: npm global\");\n removeNpmGlobal();\n removed = true;\n }\n\n if (isCompiledExecutable()) {\n console.log(\"Install method: standalone executable\");\n removeStandaloneBinary();\n removed = true;\n }\n\n if (!removed) {\n console.log(\"No global installation found.\");\n console.log(\"If running from source, simply delete the project directory.\");\n }\n\n if (!options.keepConfig) {\n console.log(\"\\nRemoving configuration...\");\n removeConfig();\n } else {\n console.log(\"\\nKeeping configuration files (--keep-config).\");\n }\n\n console.log(\"\\nUninstall complete.\");\n}\n","#!/usr/bin/env bun\n// CLI entry point for umans-gate.\n// Usage: bun src/cli.ts (or after build: umans-gate)\n// Point your harness base URL → http://localhost:1945\n// Open the inspector → http://localhost:1945/dashboard/\n\nimport { Command } from \"commander\";\nimport pkg from \"../package.json\" with { type: \"json\" };\nimport { readConfigFile, resolveConfigPath } from \"./config.js\";\nimport { createProxyServer } from \"./index.js\";\n\nconst VERSION: string = pkg.version;\n\nconst program = new Command();\n\nprogram\n .name(\"umans-gate\")\n .description(\"LLM capture proxy with Anthropic cache_control TTL stamping\")\n .version(VERSION)\n .option(\"--port <number>\", \"listen port\")\n .option(\"--target <url>\", \"upstream target URL\")\n .action((options) => {\n const config: Record<string, unknown> = {};\n if (options.port) config.port = Number(options.port);\n if (options.target) config.target = options.target;\n createProxyServer(Object.keys(config).length > 0 ? { config } : undefined);\n });\n\nprogram\n .command(\"update\")\n .description(\"Update umans-gate to the latest version\")\n .option(\"--check\", \"check if update is available without installing\")\n .action(async (options) => {\n const { checkForUpdate, performUpdate } = await import(\"./updater.js\");\n if (options.check) {\n await checkForUpdate(VERSION);\n } else {\n await performUpdate(VERSION);\n }\n });\n\nprogram\n .command(\"uninstall\")\n .description(\"Remove umans-gate\")\n .option(\"--keep-config\", \"keep configuration files\")\n .action(async (options) => {\n const { uninstall } = await import(\"./uninstaller.js\");\n await uninstall({ keepConfig: options.keepConfig ?? false });\n });\n\nprogram\n .command(\"config\")\n .description(\"Show or edit configuration\")\n .argument(\"[action]\", \"show | path\")\n .action((action: string | undefined) => {\n const configPath = resolveConfigPath();\n if (action === \"path\") {\n console.log(configPath);\n return;\n }\n // Default: show\n const cfg = readConfigFile();\n console.log(JSON.stringify(cfg, null, 2));\n console.log(`\\nConfig file: ${configPath}`);\n });\n\nprogram.parse(process.argv);\n","{\n \"name\": \"umans-gate\",\n \"version\": \"0.1.1\",\n \"description\": \"LLM capture proxy with Anthropic cache_control TTL stamping and live inspection dashboard\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"author\": \"umans.ai\",\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"homepage\": \"https://github.com/umans-ai/umans-gate\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/codegiveness/umans-gate.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/umans-ai/umans-gate/issues\"\n },\n \"keywords\": [\n \"llm\",\n \"proxy\",\n \"capture\",\n \"anthropic\",\n \"openai\",\n \"cache-control\",\n \"ttl\",\n \"inspector\",\n \"debugger\",\n \"websocket\"\n ],\n \"bin\": {\n \"umans-gate\": \"dist/cli.js\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"dashboard/dist\",\n \"README.md\",\n \"LICENSE\",\n \"CHANGELOG.md\"\n ],\n \"engines\": {\n \"bun\": \">=1.1.0\"\n },\n \"scripts\": {\n \"dev\": \"bun src/cli.ts\",\n \"build\": \"bun run build:dashboard && bun run build:embed-assets && bun run build:server\",\n \"build:server\": \"bash scripts/build-server.sh\",\n \"build:dashboard\": \"cd dashboard && bun run build\",\n \"build:embed-assets\": \"bun scripts/embed-assets.ts\",\n \"test\": \"bun test\",\n \"test:dashboard\": \"cd dashboard && bun run test\",\n \"test:dashboard:watch\": \"cd dashboard && bun run test:watch\",\n \"test:all\": \"bun run test && bun run test:dashboard\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"format\": \"biome format --write .\",\n \"typecheck\": \"tsc --noEmit\",\n \"clean\": \"rm -rf dist dashboard/dist dashboard/node_modules capture.db capture.db-shm capture.db-wal\",\n \"prepublishOnly\": \"bun run build\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^1.9.4\",\n \"@playwright/test\": \"^1.61.1\",\n \"playwright\": \"^1.61.1\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.7.2\"\n },\n \"dependencies\": {\n \"@types/bun\": \"^1.3.14\",\n \"commander\": \"^12\",\n \"lru-cache\": \"^11.5.1\"\n }\n}\n","// Startup banner — prints the proxy configuration to stderr.\n\nimport type { ProxyConfig } from \"./types.js\";\n\n/** Print the startup banner showing all resolved config values. */\nexport function printBanner(config: ProxyConfig): void {\n const displayHost = \"localhost\";\n const cacheDesc = config.stampClaudeCode\n ? \"on (claude code: ttl, top_k, max_tokens, thinking, output_config, context_management)\"\n : \"off (transparent passthrough)\";\n\n console.log(\"umans-gate v0.1.0 — LLM capture proxy\");\n console.log();\n console.log(` target ${config.target}`);\n console.log(` listen ${config.host}:${config.port}`);\n console.log(` proto in ${config.incomingProtocol} → out ${config.upstreamProtocol}`);\n console.log(` cache ${cacheDesc}`);\n if (config.visionStrategy !== \"never\") {\n console.log(\n ` vision ${config.visionStrategy} (model=${config.visionModel}, concurrency=${config.visionConcurrency})`,\n );\n }\n if (config.warmerEnabled) {\n console.log(` warm every ${config.warmerIntervalMs}ms → ${config.warmerPath}`);\n }\n console.log(` proxy http://${displayHost}:${config.port}/`);\n console.log(` dashboard http://${displayHost}:${config.port}${config.viewerPrefix}/`);\n console.log(` store ${config.dbPath} (keeps last ${config.maxCaptures})`);\n console.log();\n}\n","// Public API — createProxyServer() factory.\n// Exports the main server creation function and key types for programmatic use.\n\nimport { printBanner } from \"./banner.js\";\nimport {\n type RawConfig,\n type ReloadResult,\n applyReloadToConfig,\n loadConfig,\n readConfigFile,\n resolveConfigPath,\n saveConfig,\n} from \"./config.js\";\nimport { CaptureDB } from \"./db.js\";\nimport { syncPricing } from \"./economics.js\";\nimport { computeRequestWeight } from \"./helpers.js\";\nimport { ConcurrencyGate, GATE_RECONFIG_FIELDS, gateOptionsFromConfig } from \"./limiter/index.js\";\nimport { createLogger } from \"./logger.js\";\nimport { metrics } from \"./metrics.js\";\nimport { ModelsClient } from \"./models.js\";\nimport { type RateLimiterRef, createProxyHandler } from \"./proxy.js\";\nimport { WriteQueue } from \"./queue.js\";\nimport type { CaptureStore } from \"./queue.js\";\nimport { SlidingWindowRateLimiter } from \"./rate.js\";\nimport type { ProxyConfig } from \"./types.js\";\nimport { UmansUsageClient } from \"./usage.js\";\nimport { createViewerRouter } from \"./viewer.js\";\nimport { DescriptionCache } from \"./vision/cache.js\";\nimport type { VisionLookup } from \"./vision/detect.js\";\nimport { VisionHandoff } from \"./vision/handoff.js\";\nimport { PersistentDescriptionStore } from \"./vision/persistent-cache.js\";\nimport { CompositeVisionSink, DbVisionSink, WsBroadcastVisionSink } from \"./vision/sink.js\";\nimport { ConnectionWarmer } from \"./warmer.js\";\nimport { WorkerCaptureStore } from \"./workers/worker-store.js\";\nimport { type BunServerWebSocket, WsBroadcaster } from \"./ws.js\";\n\nconst log = createLogger(\"server\");\n\nexport { loadConfig, readConfigFile, saveConfig, validateConfig } from \"./config.js\";\nexport { resolveConfigDir, resolveConfigPath, ensureConfigFile } from \"./config.js\";\nexport type { RawConfig, RawConfigInput, ValidationResult, ReloadResult } from \"./config.js\";\nexport { CaptureDB } from \"./db.js\";\nexport { WsBroadcaster } from \"./ws.js\";\nexport type { BunServerWebSocket } from \"./ws.js\";\nexport { WriteQueue } from \"./queue.js\";\nexport { stampCacheTtl } from \"./stamp.js\";\nexport { ConnectionWarmer } from \"./warmer.js\";\nexport { ConcurrencyGate } from \"./limiter/index.js\";\nexport { SlidingWindowRateLimiter } from \"./rate.js\";\nexport { UmansUsageClient } from \"./usage.js\";\nexport type {\n ProxyConfig,\n CaptureConfig,\n CaptureRow,\n CaptureSummary,\n GateConfig,\n ProtocolConfig,\n QueueConfig,\n StampConfig,\n WsMessage,\n GateStats,\n UsageSnapshot,\n BreakerState,\n} from \"./types.js\";\nexport type { TimedChunk } from \"./usage-extract.js\";\n\n/** Whitelist of LLM API routes that the proxy will capture + forward.\n * Matches umans API surface (verified from app.umans.ai/offers/code/docs). */\nconst LLM_ROUTES = new Set([\n \"POST /v1/messages\",\n \"GET /v1/models\",\n \"GET /v1/models/info\",\n \"POST /v1/chat/completions\",\n]);\n\n/** Options for {@link createRequestDispatcher}. */\ninterface RequestDispatcherOptions {\n handleViewer: (url: URL, req: Request) => Promise<Response | null>;\n handleProxy: (req: Request, url: URL) => Promise<Response>;\n handleHealth: () => Response;\n handleMetrics: () => Response;\n viewerPrefix: string;\n}\n\n/**\n * Create the request dispatcher that routes incoming requests to:\n * 1. WebSocket upgrade (returns 101 on success, 400 on failure)\n * 2. Viewer routes (dashboard + REST API under the viewer prefix)\n * 3. Health check endpoint (`GET /health`)\n * 4. Metrics endpoint (`GET /metrics`)\n * 5. LLM proxy routes (whitelisted method+path combinations)\n * 6. 404 fallback for non-LLM paths\n */\nfunction createRequestDispatcher(options: RequestDispatcherOptions) {\n const { handleViewer, handleProxy, handleHealth, handleMetrics, viewerPrefix: VIEWER } = options;\n\n return async (req: Request, server: Bun.Server<undefined>): Promise<Response> => {\n const url = new URL(req.url);\n\n // WebSocket upgrade\n if (url.pathname === `${VIEWER}/ws`) {\n if (server.upgrade(req)) return new Response(null, { status: 101 });\n return new Response(\"upgrade failed\", { status: 400 });\n }\n\n // Viewer routes (dashboard + REST API)\n if (url.pathname === VIEWER || url.pathname.startsWith(`${VIEWER}/`)) {\n const resp = await handleViewer(url, req);\n return resp ?? new Response(\"not found\", { status: 404 });\n }\n\n // Health check endpoint\n if (url.pathname === \"/health\" && req.method === \"GET\") {\n return handleHealth();\n }\n\n // Metrics endpoint (Prometheus text format)\n if (url.pathname === \"/metrics\" && req.method === \"GET\") {\n return handleMetrics();\n }\n\n // Reject non-LLM paths (favicon, preflight, health checks, etc.)\n const routeKey = `${req.method} ${url.pathname}`;\n if (!LLM_ROUTES.has(routeKey)) {\n return new Response(JSON.stringify({ error: \"not_an_llm_endpoint\", path: url.pathname }), {\n status: 404,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n\n // Proxy route — disable idle timeout for long streaming responses\n server.timeout(req, 0);\n return handleProxy(req, url);\n };\n}\n\nconst DEFAULT_RATE_WINDOW_SECONDS = 18000;\n\nfunction createRateLimiter(\n rateLimitRequests: number,\n snap: { requestsHardCap: number | null; requestsWindowSeconds: number | null } | null,\n): SlidingWindowRateLimiter | null {\n if (rateLimitRequests === -1) return null;\n if (rateLimitRequests > 0) {\n const windowSeconds = snap?.requestsWindowSeconds ?? DEFAULT_RATE_WINDOW_SECONDS;\n return new SlidingWindowRateLimiter({ limit: rateLimitRequests, windowSeconds });\n }\n // rateLimitRequests === 0: auto-derive from usage snapshot\n if (snap && snap.requestsHardCap !== null && snap.requestsHardCap > 0) {\n const windowSeconds = snap.requestsWindowSeconds ?? DEFAULT_RATE_WINDOW_SECONDS;\n return new SlidingWindowRateLimiter({ limit: snap.requestsHardCap, windowSeconds });\n }\n return null;\n}\n\n/** Options for creating a proxy server. */\nexport interface CreateProxyServerOptions {\n /** Override env config. Pass a partial config to merge with defaults.\n * Note: `host` is hardcoded to `127.0.0.1` and cannot be overridden. */\n config?: Omit<Partial<ProxyConfig>, \"host\">;\n /** Use an existing CaptureDB instance instead of creating one. */\n db?: CaptureDB;\n /** Use an existing WsBroadcaster instead of creating one. */\n ws?: WsBroadcaster;\n /** Print the startup banner (default: true). */\n banner?: boolean;\n}\n\n/** The running proxy server handle. */\nexport interface ProxyServer {\n server: ReturnType<typeof Bun.serve>;\n db: CaptureDB;\n ws: WsBroadcaster;\n queue: WriteQueue;\n warmer: ConnectionWarmer | null;\n gate: ConcurrencyGate;\n usage: UmansUsageClient;\n models: ModelsClient;\n rate: SlidingWindowRateLimiter | null;\n config: ProxyConfig;\n /** Reload config from disk and apply hot-reloadable fields. */\n reloadConfig(): ReloadResult;\n shutdown(): Promise<void>;\n}\n\n/**\n * Create and start the LLM capture proxy server.\n *\n * This is the main programmatic entry point. It sets up the database,\n * WebSocket broadcaster, write-behind queue, viewer router, and proxy handler,\n * then starts listening on the configured port.\n */\nexport function createProxyServer(options: CreateProxyServerOptions = {}): ProxyServer {\n const envConfig = loadConfig();\n const config: ProxyConfig = { ...envConfig, ...options.config, host: \"127.0.0.1\" };\n\n const db = options.db ?? new CaptureDB(config);\n const ws = options.ws ?? new WsBroadcaster();\n const writeStore: CaptureStore = config.useWriteWorker\n ? new WorkerCaptureStore(config.dbPath, config.compressionEnabled)\n : db;\n const queue = new WriteQueue(writeStore, config, (messages) => {\n for (const msg of messages) {\n ws.broadcast(msg);\n }\n });\n const warmer = config.warmerEnabled ? new ConnectionWarmer(config) : null;\n\n const usage = new UmansUsageClient(config);\n const models = new ModelsClient({\n target: config.target,\n apiKey: config.umansApiKey,\n refreshMs: config.modelsRefreshMs,\n });\n models.start();\n models.onChange(() => {\n try {\n syncPricing(db.rawDb, models.list());\n } catch (err) {\n log.error(\"pricing sync failed\", { error: err instanceof Error ? err.message : String(err) });\n }\n });\n try {\n syncPricing(db.rawDb, models.list());\n } catch {\n // Models not fetched yet — onChange will sync after first poll.\n }\n const gate = new ConcurrencyGate(gateOptionsFromConfig(config));\n const rateRef: RateLimiterRef = { current: createRateLimiter(config.rateLimitRequests, null) };\n\n usage.onChange((snap) => {\n gate.setSoftLimit(snap.concurrencySoftLimit);\n let effective = snap.concurrencySoftLimit;\n if (snap.priorityLow) effective = Math.max(1, effective - 1);\n const boxed = snap.boxedUntil !== null && snap.boxedUntil > Date.now();\n if (boxed && snap.boxedReason !== \"rate_limited\") {\n gate.resize(1);\n } else {\n gate.resize(effective);\n }\n // Auto-derive rate limiter from usage snapshot when rate_limit_requests=0\n if (config.rateLimitRequests === 0 && snap.requestsHardCap !== null) {\n if (rateRef.current === null) {\n rateRef.current = createRateLimiter(0, snap);\n }\n } else if (config.rateLimitRequests > 0 && snap.requestsWindowSeconds !== null) {\n if (rateRef.current === null) {\n rateRef.current = createRateLimiter(config.rateLimitRequests, snap);\n }\n }\n ws.broadcast({ type: \"gate\", stats: gate.getStats(snap) });\n });\n gate.onStatsChange(() => {\n ws.broadcast({ type: \"gate\", stats: gate.getStats(usage.getSnapshot()) });\n });\n usage.start();\n\n function applyLimitsFromSource(\n source: { hardCap: number; softLimit: number },\n persist = false,\n ): void {\n if (persist) {\n saveConfig({\n concurrency_hard_cap: source.hardCap,\n concurrency_soft_limit: source.softLimit,\n });\n }\n config.concurrencyHardCap = source.hardCap;\n config.concurrencySoftLimit = source.softLimit;\n gate.setHardCap(source.hardCap);\n gate.setSoftLimit(source.softLimit);\n ws.broadcast({ type: \"gate\", stats: gate.getStats(usage.getSnapshot()) });\n }\n\n const persistentStore = config.visionPersistentCache\n ? new PersistentDescriptionStore(\n db,\n config.visionCacheTtlMs,\n config.visionCacheMaxRows,\n config.visionPendingMaxBatch,\n )\n : null;\n const visionCache = new DescriptionCache(\n config.visionCacheSize,\n config.visionCacheTtlMs,\n persistentStore,\n );\n\n const catalog: VisionLookup | null = config.visionStrategy !== \"never\" ? models : null;\n\n const visionModelName = config.visionModel ?? \"\";\n const visionWeight = computeRequestWeight(visionModelName, models);\n\n const visionSink = new CompositeVisionSink([\n new DbVisionSink(db, config),\n new WsBroadcastVisionSink(ws, config),\n ]);\n const vision =\n config.visionStrategy !== \"never\"\n ? new VisionHandoff(\n {\n strategy: config.visionStrategy,\n target: config.visionTarget,\n model: config.visionModel,\n prompt: config.visionPrompt,\n promptVersion: config.visionPromptVersion,\n maxImages: config.visionMaxImages,\n maxDescriptionTokens: config.visionMaxDescriptionTokens,\n reasoningEffort: config.visionReasoningEffort,\n timeoutMs: config.visionTimeoutMs,\n cacheSize: config.visionCacheSize,\n cacheTtlMs: config.visionCacheTtlMs,\n cacheMaxRows: config.visionCacheMaxRows,\n persistentCache: config.visionPersistentCache,\n concurrency: config.visionConcurrency,\n apiKey: config.umansApiKey || null,\n forceInterceptCapable: config.visionForceInterceptCapable,\n maxDimension: config.visionMaxDimension,\n jpegQuality: config.visionJpegQuality,\n imageFormat: config.visionImageFormat,\n imageDetail: config.visionImageDetail,\n visionWeight,\n backgroundVision: config.backgroundVision,\n },\n visionCache,\n catalog,\n gate,\n db,\n visionSink,\n config,\n )\n : null;\n\n if (persistentStore && config.visionPersistentCache) {\n const warmed = persistentStore.warmIntoCache(\n (key, description) => visionCache.warm(key, description),\n config.visionCacheSize,\n );\n if (warmed > 0) {\n console.log(`[vision] Warmed ${warmed} descriptions from persistent store`);\n }\n }\n\n const { handleProxy } = createProxyHandler(\n db,\n ws,\n queue,\n config,\n gate,\n rateRef,\n vision,\n models,\n () => warmer?.notifyTraffic(),\n );\n let lastRawConfig: RawConfig = readConfigFile();\n\n const reloadConfig = (): ReloadResult => {\n const oldRaw = lastRawConfig;\n const newRaw = readConfigFile();\n const fresh = loadConfig();\n const { applied, restartRequired } = applyReloadToConfig(config, fresh, oldRaw, newRaw);\n lastRawConfig = newRaw;\n\n if (applied.some((k) => GATE_RECONFIG_FIELDS.has(k as keyof ProxyConfig))) {\n gate.reconfigure(gateOptionsFromConfig(config));\n }\n\n if (applied.includes(\"concurrency_hard_cap\")) {\n gate.setHardCap(config.concurrencyHardCap);\n }\n if (applied.includes(\"concurrency_soft_limit\")) {\n gate.setSoftLimit(config.concurrencySoftLimit);\n }\n\n if (applied.includes(\"compression_enabled\")) {\n db.compressionEnabled = config.compressionEnabled;\n }\n\n if (applied.includes(\"rate_limit_requests\")) {\n rateRef.current = createRateLimiter(config.rateLimitRequests, usage.getSnapshot());\n }\n\n ws.broadcast({ type: \"gate\", stats: gate.getStats(usage.getSnapshot()) });\n\n return {\n ok: true,\n errors: [],\n warnings: [],\n applied,\n restartRequired,\n configPath: resolveConfigPath(),\n };\n };\n\n const refreshLimits = async (): Promise<\n | {\n ok: true;\n hardCap: number;\n softLimit: number;\n requestsLimit: number | null;\n requestsHardCap: number | null;\n requestsWindowSeconds: number | null;\n }\n | { ok: false; error: string }\n > => {\n const r = await usage.fetchLimitsFromSource();\n if (r.ok) {\n applyLimitsFromSource({ hardCap: r.hardCap, softLimit: r.softLimit }, true);\n const rl = await usage.fetchRequestsLimit();\n if (rl.ok) {\n const snap = usage.getSnapshot();\n if (config.rateLimitRequests === 0 && rl.hardCap !== null && rl.hardCap > 0) {\n rateRef.current = createRateLimiter(0, {\n requestsHardCap: rl.hardCap,\n requestsWindowSeconds: rl.windowSeconds,\n });\n } else if (config.rateLimitRequests > 0 && rl.windowSeconds !== null) {\n rateRef.current = createRateLimiter(config.rateLimitRequests, {\n requestsHardCap: rl.hardCap,\n requestsWindowSeconds: rl.windowSeconds,\n });\n } else if (config.rateLimitRequests === 0 && rl.hardCap === null) {\n // Upstream reports unlimited (e.g. Code Max) — persist -1 so the\n // config UI reflects the effective state instead of staying at 0.\n saveConfig({ rate_limit_requests: -1 });\n config.rateLimitRequests = -1;\n rateRef.current = null;\n }\n ws.broadcast({ type: \"gate\", stats: gate.getStats(snap) });\n return {\n ok: true,\n hardCap: r.hardCap,\n softLimit: r.softLimit,\n requestsLimit: rl.limit,\n requestsHardCap: rl.hardCap,\n requestsWindowSeconds: rl.windowSeconds,\n };\n }\n return {\n ok: true,\n hardCap: r.hardCap,\n softLimit: r.softLimit,\n requestsLimit: null,\n requestsHardCap: null,\n requestsWindowSeconds: null,\n };\n }\n return r;\n };\n\n if (config.umansApiKey && config.concurrencyHardCap <= 1) {\n void refreshLimits();\n }\n\n const { handleViewer, VIEWER } = createViewerRouter({\n db,\n ws,\n config,\n gate,\n usage,\n vision,\n models,\n reloadConfig,\n refreshLimits,\n restart: () => {\n shutdown().finally(() => process.exit(0));\n },\n });\n\n const handleHealth = (): Response => {\n const stats = gate.getStats(usage.getSnapshot());\n return new Response(\n JSON.stringify({\n status: \"ok\",\n uptime: process.uptime(),\n pendingRequests: server.pendingRequests,\n pendingWebSockets: server.pendingWebSockets,\n gateActive: stats.active,\n gateLimit: stats.softLimit,\n queueDepth: queue.length,\n }),\n { status: 200, headers: { \"content-type\": \"application/json\" } },\n );\n };\n\n const handleMetrics = (): Response => {\n const stats = gate.getStats(usage.getSnapshot());\n metrics.set(\"umans_gate_uptime_seconds\", process.uptime(), \"Process uptime in seconds\");\n metrics.set(\"umans_gate_pending_requests\", server.pendingRequests, \"In-flight HTTP requests\");\n metrics.set(\n \"umans_gate_pending_websockets\",\n server.pendingWebSockets,\n \"Connected WebSocket clients\",\n );\n metrics.set(\"umans_gate_gate_active\", stats.active, \"Active concurrency permits\");\n metrics.set(\"umans_gate_gate_limit\", stats.softLimit, \"Concurrency soft limit\");\n metrics.set(\"umans_gate_queue_depth\", queue.length, \"Write queue depth\");\n return new Response(metrics.format(), {\n status: 200,\n headers: { \"content-type\": \"text/plain; version=0.0.4; charset=utf-8\" },\n });\n };\n\n const fetch = createRequestDispatcher({\n handleViewer,\n handleProxy,\n handleHealth,\n handleMetrics,\n viewerPrefix: VIEWER,\n });\n\n const server = Bun.serve({\n port: config.port,\n hostname: config.host,\n reusePort: true,\n idleTimeout: config.idleTimeout,\n fetch,\n error(err): Response {\n log.error(\"uncaught server error\", {\n message: err.message,\n stack: err.stack,\n });\n return new Response(\n JSON.stringify({ error: \"internal_error\", message: \"Internal proxy error\" }),\n { status: 500, headers: { \"content-type\": \"application/json\" } },\n );\n },\n websocket: {\n open(socket) {\n ws.add(socket as unknown as BunServerWebSocket);\n },\n message() {},\n close(socket) {\n ws.remove(socket as unknown as BunServerWebSocket);\n },\n backpressureLimit: config.wsBackpressureLimit > 0 ? config.wsBackpressureLimit : undefined,\n closeOnBackpressureLimit: config.wsCloseOnBackpressureLimit,\n },\n });\n\n if (options.banner !== false) {\n printBanner(config);\n }\n\n warmer?.start();\n\n let shuttingDown = false;\n const shutdown = async (): Promise<void> => {\n if (shuttingDown) return;\n shuttingDown = true;\n\n log.info(\"graceful shutdown: draining in-flight requests...\");\n\n warmer?.stop();\n models.stop();\n usage.stop();\n\n server.stop(false);\n\n const drainDeadline = Date.now() + 5000;\n while (server.pendingRequests > 0 && Date.now() < drainDeadline) {\n await Bun.sleep(100);\n }\n if (server.pendingRequests > 0) {\n log.warn(`drain timeout: ${server.pendingRequests} requests still in-flight`);\n }\n\n gate.shutdown();\n await queue.flushNow();\n if (writeStore instanceof WorkerCaptureStore) {\n await writeStore.close();\n }\n db.close();\n\n process.removeListener(\"SIGINT\", sigHandler);\n process.removeListener(\"SIGTERM\", sigHandler);\n };\n\n const sigHandler = () => {\n shutdown().finally(() => process.exit(0));\n };\n\n process.once(\"SIGINT\", sigHandler);\n process.once(\"SIGTERM\", sigHandler);\n\n process.on(\"unhandledRejection\", (reason) => {\n log.error(\"unhandledRejection\", { reason: String(reason) });\n });\n process.on(\"uncaughtException\", (err) => {\n log.error(\"uncaughtException\", { message: err.message, stack: err.stack });\n });\n\n return {\n server,\n db,\n ws,\n queue,\n warmer,\n gate,\n usage,\n models,\n rate: rateRef.current,\n config,\n reloadConfig,\n shutdown,\n };\n}\n","// SQLite capture store using bun:sqlite.\n// WAL mode + write-behind queue for non-blocking captures.\n\nimport { Database } from \"bun:sqlite\";\nimport { compressText, decompressText } from \"./compress.js\";\nimport { accountCaptureUsage, backfillFromCaptures, migrateEconomicsSchema } from \"./economics.js\";\nimport type { CaptureRow, CaptureState, ProxyConfig } from \"./types.js\";\nimport {\n LATEST_N_PER_MODEL_VIEW,\n PERFORMANCE_STATS_SQL,\n USAGE_COLUMNS_DDL,\n} from \"./usage-extract.js\";\nimport type { PerformanceStatsRow, UsageMetrics } from \"./usage-extract.js\";\nimport { VisionDescriptionStore } from \"./vision-description-store.js\";\nimport type { VisionCallRecord } from \"./vision/handoff.js\";\n\n/** Prepared statement parameter types. */\ninterface InsertParams {\n $method: string;\n $path: string;\n $url: string;\n $rh: string;\n $rb: string;\n $rs: number;\n $st: number;\n $state: string;\n $inp: string;\n $outp: string;\n}\n\nexport interface UpdateParams {\n $id: number;\n $status: number;\n $rh: string;\n $rb: string;\n $rs: number;\n $ct: string;\n $sse: number;\n $dur: number;\n $fin: number;\n $status_source: \"upstream\" | \"gate\" | null;\n $gate_reason: string | null;\n $usage?: UsageMetrics | null;\n $model?: string | null;\n}\n\ninterface VisionInsertParams {\n $method: string;\n $path: string;\n $url: string;\n $rh: string;\n $rb: string;\n $rs: number;\n $status: number | null;\n $rh2: string;\n $rb2: string;\n $rs2: number;\n $ct: string;\n $dur: number;\n $state: CaptureState;\n $started_at: number;\n $finished_at: number;\n $inp: string;\n $outp: string;\n $model: string;\n $parent_capture_id: number | null;\n $vision_meta: string | null;\n $provider: string | null;\n $streaming: number | null;\n $input_tokens: number | null;\n $output_tokens: number | null;\n $cache_creation_tokens: number | null;\n $cache_read_tokens: number | null;\n $total_input_tokens: number | null;\n $total_output_tokens: number | null;\n $thinking_tokens: number | null;\n $ttft_ms: number | null;\n $tps: number | null;\n $usage_missing: number | null;\n $metrics_extracted_at: number | null;\n}\n\nexport interface VisionUpdateParams {\n $id: number;\n $status: number | null;\n $rh: string;\n $rb: string;\n $rs: number;\n $ct: string;\n $sse: number;\n $dur: number;\n $fin: number;\n $status_source: \"upstream\" | \"gate\" | null;\n $gate_reason: string | null;\n $vision_meta: string | null;\n $model: string | null;\n $provider: string | null;\n $streaming: number | null;\n $input_tokens: number | null;\n $output_tokens: number | null;\n $cache_creation_tokens: number | null;\n $cache_read_tokens: number | null;\n $total_input_tokens: number | null;\n $total_output_tokens: number | null;\n $thinking_tokens: number | null;\n $ttft_ms: number | null;\n $tps: number | null;\n $usage_missing: number | null;\n $metrics_extracted_at: number | null;\n}\n\n/** Flatten a UsageMetrics object into the `$`-prefixed params for stmtUpdate.\n * Returns nulls when usage is absent so a single prepared statement suffices. */\nexport function flattenUsage(usage: UsageMetrics | null | undefined) {\n if (!usage) {\n return {\n $provider: null,\n $streaming: null,\n $input_tokens: null,\n $output_tokens: null,\n $cache_creation_tokens: null,\n $cache_read_tokens: null,\n $total_input_tokens: null,\n $total_output_tokens: null,\n $thinking_tokens: null,\n $ttft_ms: null,\n $tps: null,\n $usage_missing: null,\n $metrics_extracted_at: null,\n };\n }\n return {\n $provider: usage.provider,\n $streaming: usage.streaming ? 1 : 0,\n $input_tokens: usage.input_tokens,\n $output_tokens: usage.output_tokens,\n $cache_creation_tokens: usage.cache_creation_tokens,\n $cache_read_tokens: usage.cache_read_tokens,\n $total_input_tokens: usage.total_input_tokens,\n $total_output_tokens: usage.total_output_tokens,\n $thinking_tokens: usage.thinking_tokens,\n $ttft_ms: usage.ttft_ms,\n $tps: usage.tps,\n $usage_missing: usage.usage_missing ? 1 : 0,\n $metrics_extracted_at: Date.now(),\n };\n}\n\n/** Add a column only if it doesn't already exist (migration safety). */\nfunction addColumnIfMissing(db: Database, name: string, type: string): void {\n try {\n db.exec(`ALTER TABLE captures ADD COLUMN ${name} ${type}`);\n } catch {\n // Column already exists — expected for existing DBs.\n }\n}\n\n/** Run all schema migrations/pragmas for a capture database.\n * Idempotent: safe to call on every startup, including existing databases. */\nexport function migrateCaptureSchema(db: Database): void {\n db.exec(\"PRAGMA journal_mode = WAL;\");\n db.exec(\"PRAGMA synchronous = NORMAL;\");\n db.exec(\"PRAGMA temp_store = MEMORY;\");\n db.exec(\"PRAGMA cache_size = -64000;\"); // 64MB page cache (was 20MB)\n db.exec(\"PRAGMA mmap_size = 268435456;\"); // 256MB memory-mapped I/O\n db.exec(\"PRAGMA journal_size_limit = 67108864;\"); // 64MB WAL cap\n db.exec(\"PRAGMA busy_timeout = 5000;\"); // 5s wait on lock contention\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS captures (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n method TEXT NOT NULL,\n path TEXT NOT NULL,\n url TEXT NOT NULL,\n request_headers TEXT,\n request_body TEXT,\n request_size INTEGER DEFAULT 0,\n response_status INTEGER,\n response_headers TEXT,\n response_body TEXT,\n response_size INTEGER DEFAULT 0,\n content_type TEXT,\n is_sse INTEGER DEFAULT 0,\n duration_ms INTEGER DEFAULT 0,\n state TEXT DEFAULT 'streaming',\n started_at INTEGER,\n finished_at INTEGER,\n incoming_protocol TEXT,\n upstream_protocol TEXT\n );\n `);\n\n // Add columns to existing DBs (no-op if already present).\n addColumnIfMissing(db, \"incoming_protocol\", \"TEXT\");\n addColumnIfMissing(db, \"upstream_protocol\", \"TEXT\");\n // Vision merge: flag vision-call rows + link to parent capture.\n addColumnIfMissing(db, \"is_vision\", \"INTEGER DEFAULT 0\");\n addColumnIfMissing(db, \"parent_capture_id\", \"INTEGER\");\n // Vision metadata JSON (status, httpStatus, latencyMs, description, error,\n // imageHash, imageSize, model, target) stored separately so request_body\n // and response_body can hold the actual HTTP request/response exchanged\n // with the vision model.\n addColumnIfMissing(db, \"vision_meta\", \"TEXT\");\n // Status source: \"upstream\" = response from upstream API, \"gate\" = proxy-generated.\n addColumnIfMissing(db, \"status_source\", \"TEXT\");\n // Human-readable explanation when the proxy (gate) generated the HTTP status.\n addColumnIfMissing(db, \"gate_reason\", \"TEXT\");\n\n // Token-usage columns: split DDL on ';', strip SQL comments, exec each individually so\n // SQLite doesn't halt on the first \"duplicate column\" error when an existing DB is reopened.\n for (const raw of USAGE_COLUMNS_DDL.split(\";\")) {\n const stmt = raw\n .split(\"\\n\")\n .filter((line) => !line.trim().startsWith(\"--\"))\n .join(\"\\n\")\n .trim();\n if (!stmt) continue;\n try {\n db.exec(stmt);\n } catch {\n // Column already exists — expected for existing DBs.\n }\n }\n\n // Latest-N-per-model materialized view (IF NOT EXISTS → safe on every restart).\n db.exec(LATEST_N_PER_MODEL_VIEW);\n\n // Covering indexes for fast aggregation queries.\n // Without these, every stats query scans the entire captures table.\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_captures_model_started\n ON captures(model, started_at DESC)\n WHERE model IS NOT NULL AND state = 'done';\n `);\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_captures_model_usage\n ON captures(model, usage_missing, started_at DESC)\n WHERE model IS NOT NULL;\n `);\n\n // Null out tps values computed before the 1-second generation-time floor\n // was introduced. Idempotent: after the first run no rows match because\n // computeTps() already returns null for short generations.\n db.exec(`\n UPDATE captures\n SET tps = NULL\n WHERE tps IS NOT NULL\n AND duration_ms IS NOT NULL\n AND (duration_ms - COALESCE(ttft_ms, 0)) < 1000\n `);\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS vision_descriptions (\n key TEXT PRIMARY KEY,\n image_hash TEXT NOT NULL,\n model TEXT NOT NULL,\n prompt_version INTEGER NOT NULL,\n description TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n last_accessed_at INTEGER NOT NULL\n );\n `);\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_vision_desc_last_accessed\n ON vision_descriptions(last_accessed_at ASC);\n `);\n\n // Economics schema (model_pricing + daily_usage tables, usage_accounted column).\n migrateEconomicsSchema(db);\n // Account captures from before the economics migration.\n backfillFromCaptures(db);\n}\n\n/** Capture database — wraps a bun:sqlite Database with prepared statements. */\nexport class CaptureDB {\n private db: Database;\n private stmtInsert: ReturnType<Database[\"prepare\"]>;\n private stmtUpdate: ReturnType<Database[\"prepare\"]>;\n private stmtDeleteOld: ReturnType<Database[\"prepare\"]>;\n private stmtGet: ReturnType<Database[\"prepare\"]>;\n private stmtList: ReturnType<Database[\"prepare\"]>;\n private stmtCount: ReturnType<Database[\"prepare\"]>;\n private stmtSetState: ReturnType<Database[\"prepare\"]>;\n private stmtUpdateRequestBody: ReturnType<Database[\"prepare\"]>;\n private stmtPerformanceStats: ReturnType<Database[\"prepare\"]>;\n private stmtInsertVision: ReturnType<Database[\"prepare\"]>;\n private stmtUpdateVision: ReturnType<Database[\"prepare\"]>;\n private stmtListVision: ReturnType<Database[\"prepare\"]>;\n private stmtClearVision: ReturnType<Database[\"prepare\"]>;\n private stmtListVisionRecords: ReturnType<Database[\"prepare\"]>;\n private readonly visionDescStore: VisionDescriptionStore;\n private rowCount: number;\n readonly maxCaptures: number;\n compressionEnabled: boolean;\n\n constructor(\n config: Pick<ProxyConfig, \"dbPath\" | \"maxCaptures\"> &\n Partial<Pick<ProxyConfig, \"compressionEnabled\">>,\n ) {\n this.db = new Database(config.dbPath);\n this.maxCaptures = config.maxCaptures;\n this.compressionEnabled = config.compressionEnabled ?? true;\n\n migrateCaptureSchema(this.db);\n\n this.stmtInsert = this.db.prepare(`\n INSERT INTO captures (method, path, url, request_headers, request_body, request_size, started_at, state, incoming_protocol, upstream_protocol)\n VALUES ($method, $path, $url, $rh, $rb, $rs, $st, $state, $inp, $outp)\n `);\n this.stmtUpdate = this.db.prepare(`\n UPDATE captures SET\n response_status = $status,\n response_headers = $rh,\n response_body = $rb,\n response_size = $rs,\n content_type = $ct,\n is_sse = $sse,\n duration_ms = $dur,\n state = 'done',\n finished_at = $fin,\n status_source = $status_source,\n gate_reason = $gate_reason,\n provider = $provider,\n streaming = $streaming,\n model = $model,\n input_tokens = $input_tokens,\n output_tokens = $output_tokens,\n cache_creation_tokens = $cache_creation_tokens,\n cache_read_tokens = $cache_read_tokens,\n total_input_tokens = $total_input_tokens,\n total_output_tokens = $total_output_tokens,\n thinking_tokens = $thinking_tokens,\n ttft_ms = $ttft_ms,\n tps = $tps,\n usage_missing = $usage_missing,\n metrics_extracted_at = $metrics_extracted_at\n WHERE id = $id\n `);\n this.stmtDeleteOld = this.db.prepare(\n \"DELETE FROM captures WHERE id NOT IN (SELECT id FROM captures ORDER BY id DESC LIMIT $n)\",\n );\n this.sweepStaleCaptures();\n this.stmtGet = this.db.prepare(\"SELECT * FROM captures WHERE id = $id\");\n this.stmtList = this.db.prepare(\n `SELECT id, method, path, response_status, is_sse, content_type,\n request_size, response_size, duration_ms, state, started_at, finished_at,\n incoming_protocol, upstream_protocol, model, usage_missing,\n ttft_ms, tps, input_tokens, output_tokens,\n cache_creation_tokens, cache_read_tokens,\n total_input_tokens, total_output_tokens, is_vision,\n status_source, gate_reason\n FROM captures ORDER BY id DESC LIMIT ?`,\n );\n this.stmtCount = this.db.prepare(\"SELECT COUNT(*) AS c FROM captures\");\n this.stmtSetState = this.db.prepare(\"UPDATE captures SET state = $state WHERE id = $id\");\n this.stmtUpdateRequestBody = this.db.prepare(\n \"UPDATE captures SET request_body = $rb, request_size = $rs WHERE id = $id\",\n );\n this.stmtPerformanceStats = this.db.prepare(PERFORMANCE_STATS_SQL);\n this.stmtInsertVision = this.db.prepare(\n `INSERT INTO captures\n (method, path, url, request_headers, request_body, request_size,\n response_status, response_headers, response_body, response_size,\n content_type, is_sse, duration_ms, state, started_at, finished_at,\n incoming_protocol, upstream_protocol, model,\n is_vision, parent_capture_id, vision_meta,\n provider, streaming, input_tokens, output_tokens,\n cache_creation_tokens, cache_read_tokens,\n total_input_tokens, total_output_tokens, thinking_tokens,\n ttft_ms, tps, usage_missing, metrics_extracted_at)\n VALUES\n ($method, $path, $url, $rh, $rb, $rs,\n $status, $rh2, $rb2, $rs2,\n $ct, 0, $dur, $state, $started_at, $finished_at,\n $inp, $outp, $model,\n 1, $parent_capture_id, $vision_meta,\n $provider, $streaming, $input_tokens, $output_tokens,\n $cache_creation_tokens, $cache_read_tokens,\n $total_input_tokens, $total_output_tokens, $thinking_tokens,\n $ttft_ms, $tps, $usage_missing, $metrics_extracted_at)`,\n );\n this.stmtUpdateVision = this.db.prepare(`\n UPDATE captures SET\n response_status = $status,\n response_headers = $rh,\n response_body = $rb,\n response_size = $rs,\n content_type = $ct,\n is_sse = $sse,\n duration_ms = $dur,\n state = 'done',\n finished_at = $fin,\n status_source = $status_source,\n gate_reason = $gate_reason,\n vision_meta = $vision_meta,\n provider = $provider,\n streaming = $streaming,\n model = $model,\n input_tokens = $input_tokens,\n output_tokens = $output_tokens,\n cache_creation_tokens = $cache_creation_tokens,\n cache_read_tokens = $cache_read_tokens,\n total_input_tokens = $total_input_tokens,\n total_output_tokens = $total_output_tokens,\n thinking_tokens = $thinking_tokens,\n ttft_ms = $ttft_ms,\n tps = $tps,\n usage_missing = $usage_missing,\n metrics_extracted_at = $metrics_extracted_at\n WHERE id = $id\n `);\n this.stmtListVision = this.db.prepare(\n `SELECT id, method, path, response_status, is_sse, content_type,\n request_size, response_size, duration_ms, state, started_at, finished_at,\n incoming_protocol, upstream_protocol, model, usage_missing,\n ttft_ms, tps, input_tokens, output_tokens,\n cache_creation_tokens, cache_read_tokens,\n total_input_tokens, total_output_tokens, is_vision, parent_capture_id,\n status_source, gate_reason\n FROM captures WHERE is_vision = 1\n ORDER BY id DESC LIMIT ?`,\n );\n this.stmtClearVision = this.db.prepare(\"DELETE FROM captures WHERE is_vision = 1\");\n this.stmtListVisionRecords = this.db.prepare(\n `SELECT id, response_body, request_body, vision_meta, started_at, finished_at, parent_capture_id, model,\n state, incoming_protocol, upstream_protocol\n FROM captures WHERE is_vision = 1\n ORDER BY id DESC LIMIT ?`,\n );\n this.visionDescStore = new VisionDescriptionStore(this.db);\n this.rowCount = (this.stmtCount.get() as { c: number }).c;\n }\n\n /** Mark captures stuck in \"streaming\" or \"enqueued\" from a previous run as \"done\".\n * Called once on startup so the dashboard doesn't show phantom \"running\" rows. */\n private sweepStaleCaptures(): void {\n this.db\n .prepare(\"UPDATE captures SET state = 'done' WHERE state IN ('streaming', 'enqueued')\")\n .run();\n }\n\n /** Insert a new capture row and enforce the ring buffer. Returns the new id. */\n startCapture(params: InsertParams): number {\n const compressed = {\n ...params,\n $rh: compressText(params.$rh, this.compressionEnabled),\n $rb: compressText(params.$rb, this.compressionEnabled),\n };\n let id = 0;\n this.db.transaction(() => {\n id = Number(this.stmtInsert.run(compressed as unknown as never).lastInsertRowid);\n if (++this.rowCount > this.maxCaptures) {\n this.stmtDeleteOld.run({ $n: this.maxCaptures });\n this.rowCount = this.maxCaptures;\n }\n })();\n return id;\n }\n\n /** Update a capture row with response data. */\n updateCapture(params: UpdateParams): void {\n const compressed = {\n ...params,\n $rh: compressText(params.$rh, this.compressionEnabled),\n $rb: compressText(params.$rb, this.compressionEnabled),\n };\n this.db.transaction(() => {\n this.stmtUpdate.run(compressed as unknown as never);\n accountCaptureUsage(this.db, params.$id);\n })();\n }\n\n /** Transition a capture's state (enqueued → streaming → done). */\n setState(id: number, state: \"enqueued\" | \"streaming\" | \"done\"): void {\n this.stmtSetState.run({ $state: state, $id: id });\n }\n\n /** Update request_body and request_size after in-flight modification (e.g. vision handoff). */\n updateRequestBody(id: number, body: string, size: number): void {\n const compressedBody = compressText(body, this.compressionEnabled);\n this.stmtUpdateRequestBody.run({ $rb: compressedBody, $rs: size, $id: id });\n }\n\n /** Batch-update multiple captures in a single transaction. */\n async batchUpdate(items: Array<{ id: number; res: Omit<UpdateParams, \"$id\"> }>): Promise<void> {\n this.db.transaction(() => {\n for (const it of items) {\n this.stmtUpdate.run({\n ...it.res,\n $rh: compressText(it.res.$rh, this.compressionEnabled),\n $rb: compressText(it.res.$rb, this.compressionEnabled),\n ...flattenUsage(it.res.$usage),\n $model: it.res.$model ?? null,\n $id: it.id,\n } as unknown as never);\n accountCaptureUsage(this.db, it.id);\n }\n })();\n }\n\n /** Get a single capture by id (full row including bodies). */\n get(id: number): CaptureRow | null {\n const raw = this.stmtGet.get({ $id: id }) as Record<string, unknown> | undefined;\n if (!raw) return null;\n raw.request_headers = decompressText(raw.request_headers as string | Uint8Array | null);\n raw.request_body = decompressText(raw.request_body as string | Uint8Array | null);\n raw.response_headers = decompressText(raw.response_headers as string | Uint8Array | null);\n raw.response_body = decompressText(raw.response_body as string | Uint8Array | null);\n return raw as unknown as CaptureRow;\n }\n\n /** List recent capture summaries (no body data). */\n list(limit: number): CaptureRow[] {\n return this.stmtList.all(Math.min(limit, 1000)) as CaptureRow[];\n }\n\n /** Compute per-model performance stats entirely in SQL (p10/p50/p95, sums, cached%).\n * Uses the v_latest_requests_per_model view + nearest-rank percentiles.\n * Returns PerformanceStatsRow[] — no JS post-processing needed. */\n getPerformanceStats(): PerformanceStatsRow[] {\n const rows = this.stmtPerformanceStats.all() as Array<Record<string, unknown>>;\n return rows.map((row) => ({\n model: row.model as string,\n provider: (row.provider as \"anthropic\" | \"openai\") ?? \"anthropic\",\n request_count: Number(row.request_count) || 0,\n streaming_count: Number(row.streaming_count) || 0,\n total_input_tokens: Number(row.total_input_tokens) || 0,\n total_output_tokens: Number(row.total_output_tokens) || 0,\n total_cache_read_tokens: Number(row.total_cache_read_tokens) || 0,\n total_thinking_tokens: Number(row.total_thinking_tokens) || 0,\n cached_pct: Number(row.cached_pct) || 0,\n ttft_mean: (row.ttft_mean as number | null) ?? null,\n ttft_p10: (row.ttft_p10 as number | null) ?? null,\n ttft_p50: (row.ttft_p50 as number | null) ?? null,\n ttft_p95: (row.ttft_p95 as number | null) ?? null,\n ttft_outlier_count: 0,\n tps_mean: (row.tps_mean as number | null) ?? null,\n tps_p10: (row.tps_p10 as number | null) ?? null,\n tps_p50: (row.tps_p50 as number | null) ?? null,\n tps_p95: (row.tps_p95 as number | null) ?? null,\n tps_outlier_count: 0,\n }));\n }\n\n /** Delete all captures. */\n clear(): void {\n this.db.prepare(\"DELETE FROM captures\").run();\n this.rowCount = 0;\n }\n\n /** Insert a vision-call capture row. Returns the new row id. */\n insertVisionCapture(params: VisionInsertParams): number {\n const compressed = {\n ...params,\n $rh: compressText(params.$rh, this.compressionEnabled),\n $rb: compressText(params.$rb, this.compressionEnabled),\n $rh2: compressText(params.$rh2, this.compressionEnabled),\n $rb2: compressText(params.$rb2, this.compressionEnabled),\n };\n let id = 0;\n this.db.transaction(() => {\n id = Number(this.stmtInsertVision.run(compressed as unknown as never).lastInsertRowid);\n if (++this.rowCount > this.maxCaptures) {\n this.stmtDeleteOld.run({ $n: this.maxCaptures });\n this.rowCount = this.maxCaptures;\n }\n })();\n return id;\n }\n\n /** Update a vision-call capture row with final response data and mark it done. */\n updateVisionCapture(params: VisionUpdateParams): void {\n const compressed = {\n ...params,\n $rh: compressText(params.$rh, this.compressionEnabled),\n $rb: compressText(params.$rb, this.compressionEnabled),\n };\n this.db.transaction(() => {\n this.stmtUpdateVision.run(compressed as unknown as never);\n accountCaptureUsage(this.db, params.$id);\n })();\n }\n\n /** List recent vision-call capture summaries (is_vision=1). */\n listVisionCaptures(limit: number): CaptureRow[] {\n return this.stmtListVision.all(Math.min(limit, 1000)) as CaptureRow[];\n }\n\n /** Delete only vision-call captures (is_vision=1). */\n clearVisionCaptures(): void {\n const deleted = this.stmtClearVision.run();\n this.rowCount = Math.max(0, this.rowCount - (deleted.changes ?? 0));\n }\n\n /**\n * Reconstruct VisionCallRecord[] from stored vision-call rows.\n * The metadata is persisted as JSON in vision_meta by addRecord().\n */\n getVisionCallRecords(limit: number): VisionCallRecord[] {\n const rows = this.stmtListVisionRecords.all(Math.min(limit, 1000)) as Array<{\n id: number;\n response_body: string | Uint8Array | null;\n request_body: string | Uint8Array | null;\n vision_meta: string | null;\n started_at: number | null;\n finished_at: number | null;\n parent_capture_id: number | null;\n model: string | null;\n state: string | null;\n incoming_protocol: string | null;\n upstream_protocol: string | null;\n }>;\n for (const row of rows) {\n row.response_body = decompressText(row.response_body);\n row.request_body = decompressText(row.request_body);\n }\n const records: VisionCallRecord[] = [];\n for (const row of rows) {\n if (!row.vision_meta) continue;\n try {\n const data = JSON.parse(row.vision_meta) as {\n status: VisionCallRecord[\"status\"];\n httpStatus: number | null;\n latencyMs: number;\n description: string;\n error: string | null;\n imageHash: string | null;\n imageSize: number;\n model: string;\n target: string;\n };\n records.push({\n id: row.id,\n timestamp: row.finished_at ?? row.started_at ?? Date.now(),\n captureId: row.parent_capture_id,\n model: data.model ?? row.model ?? \"\",\n target: data.target ?? \"\",\n imageSize: data.imageSize ?? 0,\n imageHash: data.imageHash ?? null,\n status: data.status,\n httpStatus: data.httpStatus ?? null,\n latencyMs: data.latencyMs ?? 0,\n description: data.description ?? \"\",\n error: data.error ?? null,\n incomingProtocol: row.incoming_protocol ?? \"\",\n upstreamProtocol: row.upstream_protocol ?? \"\",\n state: (row.state ?? \"done\") as CaptureState,\n });\n } catch {\n // Corrupt JSON — skip this row.\n }\n }\n return records;\n }\n\n /** Close the database connection. */\n close(): void {\n this.db.close();\n }\n\n /** Access the raw bun:sqlite Database (for economics queries). */\n get rawDb(): Database {\n return this.db;\n }\n\n /** Insert or update a vision description in the persistent store. */\n upsertVisionDescription(params: {\n $key: string;\n $image_hash: string;\n $model: string;\n $prompt_version: number;\n $description: string;\n $now: number;\n }): void {\n this.visionDescStore.upsert(params);\n }\n\n /** Look up a vision description by key. Returns null if not found. */\n getVisionDescription(key: string): { description: string; created_at: number } | null {\n return this.visionDescStore.get(key);\n }\n\n deleteVisionDescription(key: string): void {\n this.visionDescStore.delete(key);\n }\n\n evictVisionDescriptions(cutoff: number, maxRows: number): number {\n return this.visionDescStore.evict(cutoff, maxRows);\n }\n\n /** List recent non-expired vision descriptions for cache warming. Returns up to `limit` entries. */\n listVisionDescriptionsForWarming(\n limit: number,\n cutoff: number,\n ): Array<{ key: string; description: string }> {\n return this.visionDescStore.listForWarming(limit, cutoff);\n }\n}\n","export const COMPRESSION_THRESHOLD = 256;\n\nexport function compressText(value: string | null, enabled: boolean): string | Uint8Array | null {\n if (value === null) {\n return null;\n }\n if (!enabled) {\n return value;\n }\n // Fast-path: if char count >= threshold, byte count is guaranteed >= threshold\n // (UTF-8 uses 1-4 bytes per char, so byte count >= char count). Skip the scan.\n if (value.length >= COMPRESSION_THRESHOLD) {\n return Bun.zstdCompressSync(Buffer.from(value, \"utf-8\"), { level: 3 });\n }\n // Short strings: check byte count for multi-byte content (CJK, emoji).\n if (Buffer.byteLength(value, \"utf-8\") < COMPRESSION_THRESHOLD) {\n return value;\n }\n return Bun.zstdCompressSync(Buffer.from(value, \"utf-8\"), { level: 3 });\n}\n\nexport function decompressText(value: string | Uint8Array | null): string | null {\n if (value === null) {\n return null;\n }\n if (typeof value === \"string\") {\n return value;\n }\n if (value instanceof Uint8Array) {\n try {\n return Bun.zstdDecompressSync(value).toString(\"utf-8\");\n } catch {\n return null;\n }\n }\n return null;\n}\n","type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nconst ENV_LEVEL = (process.env.UMANS_LOG_LEVEL ?? \"info\") as LogLevel;\nconst globalMinPriority = LEVEL_PRIORITY[ENV_LEVEL] ?? LEVEL_PRIORITY.info;\n\ninterface LogContext {\n captureId?: string | number;\n module?: string;\n [k: string]: unknown;\n}\n\nexport interface Logger {\n debug(msg: string, ctx?: LogContext): void;\n info(msg: string, ctx?: LogContext): void;\n warn(msg: string, ctx?: LogContext): void;\n error(msg: string, ctx?: LogContext): void;\n child(ctx: LogContext): Logger;\n}\n\nexport function createLogger(module: string): Logger {\n return makeLogger({ module });\n}\n\nfunction makeLogger(baseCtx: LogContext): Logger {\n const log = (level: LogLevel, msg: string, ctx?: LogContext) => {\n if (LEVEL_PRIORITY[level] < globalMinPriority) return;\n const merged = ctx ? { ...baseCtx, ...ctx } : baseCtx;\n const ctxStr =\n Object.keys(merged).length > 0\n ? ` ${Object.entries(merged)\n .map(([k, v]) => `${k}=${v}`)\n .join(\" \")}`\n : \"\";\n const line = `[${level}] ${msg}${ctxStr}`;\n if (level === \"error\" || level === \"warn\") {\n console.error(line);\n } else {\n console.log(line);\n }\n };\n return {\n debug: (m, c) => log(\"debug\", m, c),\n info: (m, c) => log(\"info\", m, c),\n warn: (m, c) => log(\"warn\", m, c),\n error: (m, c) => log(\"error\", m, c),\n child: (ctx) => makeLogger({ ...baseCtx, ...ctx }),\n };\n}\n","// Economics module — persistent daily usage accumulation + model pricing sync.\n//\n// This module provides:\n// - model_pricing table: stores per-model pricing (synced from /v1/models API)\n// - daily_usage table: accumulates per-day, per-model token totals + costs\n// - Captures usage_accounted flag on the captures table for idempotent accounting\n//\n// Design constraints (from user):\n// 1. Centralized sync: piggyback on existing ModelsClient poll — no extra API calls.\n// 2. Price-as-of-day: costs are frozen at accumulation time; historical rows are\n// never recomputed when pricing changes.\n// 3. New model mitigation: unknown models accumulate with $0 cost + pricing_known=0;\n// when sync discovers pricing, those rows are backfilled once.\n\nimport type { Database } from \"bun:sqlite\";\nimport { createLogger } from \"./logger.js\";\nimport type { ModelEntry } from \"./models.js\";\n\nconst log = createLogger(\"economics\");\n\n/** Ratio of cache_read price to input price (derived from Umans pricing page). */\nconst CACHE_READ_RATIO = 0.186;\n\n/** Seed pricing for known Umans models (used when API hasn't returned pricing yet). */\nconst SEED_PRICING: Record<string, { input: number; output: number; cache_read: number }> = {\n \"umans-glm-5.2\": { input: 1.4, output: 4.4, cache_read: 0.26 },\n \"umans-kimi-k2.7\": { input: 0.95, output: 4.0, cache_read: 0.19 },\n \"umans-coder\": { input: 0.95, output: 4.0, cache_read: 0.19 },\n \"umans-flash\": { input: 0.15, output: 1.0, cache_read: 0.05 },\n \"umans-qwen3.6-35b-a3b\": { input: 0.15, output: 1.0, cache_read: 0.05 },\n};\n\n/** SQL DDL for economics tables. Idempotent — safe to run on every startup. */\nexport const ECONOMICS_DDL = `\nCREATE TABLE IF NOT EXISTS model_pricing (\n model_id TEXT PRIMARY KEY,\n input_per_mtoken REAL NOT NULL DEFAULT 0,\n output_per_mtoken REAL NOT NULL DEFAULT 0,\n cache_read_per_mtoken REAL NOT NULL DEFAULT 0,\n pricing_known INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL DEFAULT 0\n);\n\nCREATE TABLE IF NOT EXISTS daily_usage (\n date TEXT NOT NULL,\n model TEXT NOT NULL,\n requests INTEGER NOT NULL DEFAULT 0,\n input_tokens INTEGER NOT NULL DEFAULT 0,\n output_tokens INTEGER NOT NULL DEFAULT 0,\n cache_read_tokens INTEGER NOT NULL DEFAULT 0,\n cache_creation_tokens INTEGER NOT NULL DEFAULT 0,\n thinking_tokens INTEGER NOT NULL DEFAULT 0,\n cost_input REAL NOT NULL DEFAULT 0,\n cost_output REAL NOT NULL DEFAULT 0,\n cost_cache_read REAL NOT NULL DEFAULT 0,\n cost_cache_creation REAL NOT NULL DEFAULT 0,\n cost_total REAL NOT NULL DEFAULT 0,\n pricing_known INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (date, model)\n) WITHOUT ROWID;\n` as const;\n\n/** SQL to add the usage_accounted column to captures (idempotency flag). */\nexport const USAGE_ACCOUNTED_DDL =\n \"ALTER TABLE captures ADD COLUMN usage_accounted INTEGER DEFAULT 0\";\n\n/** Result of a pricing lookup for a single model. */\ninterface PricingRow {\n input_per_mtoken: number;\n output_per_mtoken: number;\n cache_read_per_mtoken: number;\n pricing_known: number;\n}\n\n/** Run economics schema migrations. Idempotent. */\nexport function migrateEconomicsSchema(db: Database): void {\n db.exec(ECONOMICS_DDL);\n\n // Add usage_accounted column to captures (no-op if already exists).\n try {\n db.exec(USAGE_ACCOUNTED_DDL);\n } catch {\n // Column already exists — expected for existing DBs.\n }\n\n // Covering index for unaccounted captures (fast incremental accounting).\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_captures_usage_accounted\n ON captures(usage_accounted, id)\n WHERE usage_accounted = 0;\n `);\n\n // Seed pricing for known models if model_pricing is empty.\n const count = (db.prepare(\"SELECT COUNT(*) AS c FROM model_pricing\").get() as { c: number }).c;\n if (count === 0) {\n const now = Date.now();\n const stmt = db.prepare(\n `INSERT INTO model_pricing\n (model_id, input_per_mtoken, output_per_mtoken, cache_read_per_mtoken, pricing_known, updated_at)\n VALUES ($model_id, $input, $output, $cache_read, 0, $now)`,\n );\n for (const [modelId, pricing] of Object.entries(SEED_PRICING)) {\n stmt.run({\n $model_id: modelId,\n $input: pricing.input,\n $output: pricing.output,\n $cache_read: pricing.cache_read,\n $now: now,\n });\n }\n log.info(`seeded ${Object.keys(SEED_PRICING).length} model pricing entries`);\n }\n}\n\n/** Get pricing for a model, or null if unknown. */\nfunction getPricing(db: Database, modelId: string): PricingRow | null {\n const row = db\n .prepare(\n `SELECT input_per_mtoken, output_per_mtoken, cache_read_per_mtoken, pricing_known\n FROM model_pricing WHERE model_id = $model_id`,\n )\n .get({ $model_id: modelId }) as PricingRow | undefined;\n return row ?? null;\n}\n\n/**\n * Account a single capture's usage into daily_usage.\n * Idempotent: only processes captures where usage_accounted=0.\n * Must be called within the same transaction as the capture UPDATE.\n *\n * Costs are computed using current model_pricing at accumulation time\n * and frozen in daily_usage (price-as-of-day constraint).\n */\nexport function accountCaptureUsage(db: Database, captureId: number): void {\n const capture = db\n .prepare(\n `SELECT model, input_tokens, output_tokens, cache_read_tokens,\n cache_creation_tokens, thinking_tokens, usage_missing, started_at\n FROM captures\n WHERE id = $id AND usage_accounted = 0`,\n )\n .get({ $id: captureId }) as\n | {\n model: string | null;\n input_tokens: number | null;\n output_tokens: number | null;\n cache_read_tokens: number | null;\n cache_creation_tokens: number | null;\n thinking_tokens: number | null;\n usage_missing: number | null;\n started_at: number | null;\n }\n | undefined;\n\n if (!capture) return; // Already accounted or not found.\n\n // Skip if no model or usage is missing.\n if (!capture.model || capture.usage_missing) {\n db.prepare(\"UPDATE captures SET usage_accounted = 1 WHERE id = $id\").run({\n $id: captureId,\n });\n return;\n }\n\n const model = capture.model;\n const startedAt = capture.started_at ?? Date.now();\n const date = new Date(startedAt).toISOString().slice(0, 10); // YYYY-MM-DD\n\n const inputTokens = capture.input_tokens ?? 0;\n const outputTokens = capture.output_tokens ?? 0;\n const cacheReadTokens = capture.cache_read_tokens ?? 0;\n const cacheCreationTokens = capture.cache_creation_tokens ?? 0;\n const thinkingTokens = capture.thinking_tokens ?? 0;\n\n // Get pricing (may be null for unknown models).\n const pricing = getPricing(db, model);\n\n // Compute costs: $ = tokens / 1_000_000 * price_per_mtoken.\n // Unknown models accumulate with $0 cost + pricing_known=0.\n const inputPrice = pricing?.input_per_mtoken ?? 0;\n const outputPrice = pricing?.output_per_mtoken ?? 0;\n const cacheReadPrice = pricing?.cache_read_per_mtoken ?? 0;\n const pricingKnown = pricing?.pricing_known ?? 0;\n\n // Cache creation is priced at the input rate (Anthropic convention).\n const cacheCreationPrice = inputPrice;\n\n const costInput = (inputTokens / 1_000_000) * inputPrice;\n const costOutput = (outputTokens / 1_000_000) * outputPrice;\n const costCacheRead = (cacheReadTokens / 1_000_000) * cacheReadPrice;\n const costCacheCreation = (cacheCreationTokens / 1_000_000) * cacheCreationPrice;\n const costTotal = costInput + costOutput + costCacheRead + costCacheCreation;\n\n // UPSERT into daily_usage (accumulate on conflict).\n db.prepare(\n `INSERT INTO daily_usage\n (date, model, requests, input_tokens, output_tokens,\n cache_read_tokens, cache_creation_tokens, thinking_tokens,\n cost_input, cost_output, cost_cache_read, cost_cache_creation,\n cost_total, pricing_known)\n VALUES\n ($date, $model, 1, $input_tokens, $output_tokens,\n $cache_read_tokens, $cache_creation_tokens, $thinking_tokens,\n $cost_input, $cost_output, $cost_cache_read, $cost_cache_creation,\n $cost_total, $pricing_known)\n ON CONFLICT(date, model) DO UPDATE SET\n requests = requests + excluded.requests,\n input_tokens = input_tokens + excluded.input_tokens,\n output_tokens = output_tokens + excluded.output_tokens,\n cache_read_tokens = cache_read_tokens + excluded.cache_read_tokens,\n cache_creation_tokens = cache_creation_tokens + excluded.cache_creation_tokens,\n thinking_tokens = thinking_tokens + excluded.thinking_tokens,\n cost_input = cost_input + excluded.cost_input,\n cost_output = cost_output + excluded.cost_output,\n cost_cache_read = cost_cache_read + excluded.cost_cache_read,\n cost_cache_creation = cost_cache_creation + excluded.cost_cache_creation,\n cost_total = cost_total + excluded.cost_total,\n pricing_known = excluded.pricing_known`,\n ).run({\n $date: date,\n $model: model,\n $input_tokens: inputTokens,\n $output_tokens: outputTokens,\n $cache_read_tokens: cacheReadTokens,\n $cache_creation_tokens: cacheCreationTokens,\n $thinking_tokens: thinkingTokens,\n $cost_input: costInput,\n $cost_output: costOutput,\n $cost_cache_read: costCacheRead,\n $cost_cache_creation: costCacheCreation,\n $cost_total: costTotal,\n $pricing_known: pricingKnown,\n });\n\n // Mark capture as accounted.\n db.prepare(\"UPDATE captures SET usage_accounted = 1 WHERE id = $id\").run({\n $id: captureId,\n });\n}\n\n/**\n * Backfill daily_usage from existing captures that haven't been accounted yet.\n * Called once on migration. Each capture is accounted individually to ensure\n * correct date bucketing (based on started_at).\n */\nexport function backfillFromCaptures(db: Database): number {\n const ids = db\n .prepare(\n `SELECT id FROM captures WHERE usage_accounted = 0 AND model IS NOT NULL AND usage_missing = 0\n ORDER BY id ASC`,\n )\n .all() as Array<{ id: number }>;\n\n let count = 0;\n db.transaction(() => {\n for (const { id } of ids) {\n accountCaptureUsage(db, id);\n count++;\n }\n })();\n\n if (count > 0) {\n log.info(`backfilled ${count} captures into daily_usage`);\n }\n return count;\n}\n\n/**\n * Sync pricing from ModelsClient into model_pricing table.\n * Called periodically — uses models.list() which is a local map lookup (no API call).\n *\n * For each model from the API:\n * - If exists in model_pricing: update input/output from API, preserve cache_read\n * (API doesn't return cache_read; we keep the seed value or last known).\n * - If new: insert with API pricing + estimated cache_read = input * 0.186, pricing_known=0.\n *\n * After syncing, backfill any daily_usage rows that were accumulated with $0 cost\n * (pricing_known=0) for models that now have pricing.\n */\nexport function syncPricing(\n db: Database,\n models: ModelEntry[],\n): {\n updated: number;\n inserted: number;\n backfilled: number;\n} {\n const now = Date.now();\n let updated = 0;\n let inserted = 0;\n\n const upsertStmt = db.prepare(\n `INSERT INTO model_pricing\n (model_id, input_per_mtoken, output_per_mtoken, cache_read_per_mtoken, pricing_known, updated_at)\n VALUES ($model_id, $input, $output, $cache_read, $pricing_known, $now)\n ON CONFLICT(model_id) DO UPDATE SET\n input_per_mtoken = $input,\n output_per_mtoken = $output,\n cache_read_per_mtoken = CASE\n WHEN model_pricing.pricing_known = 0 AND $pricing_known = 1 THEN $cache_read\n WHEN model_pricing.cache_read_per_mtoken = 0 THEN $cache_read\n ELSE model_pricing.cache_read_per_mtoken\n END,\n pricing_known = MAX(model_pricing.pricing_known, $pricing_known),\n updated_at = $now`,\n );\n\n db.transaction(() => {\n for (const model of models) {\n if (!model.pricing) continue; // Skip models without pricing data.\n\n const existing = getPricing(db, model.id);\n const inputPerMtoken = model.pricing.input;\n const outputPerMtoken = model.pricing.output;\n\n // Estimate cache_read if not already known.\n const estimatedCacheRead = Math.round(inputPerMtoken * CACHE_READ_RATIO * 1000) / 1000;\n\n if (existing) {\n // Update input/output from API; preserve cache_read if already set.\n const cacheRead =\n existing.cache_read_per_mtoken > 0 ? existing.cache_read_per_mtoken : estimatedCacheRead;\n upsertStmt.run({\n $model_id: model.id,\n $input: inputPerMtoken,\n $output: outputPerMtoken,\n $cache_read: cacheRead,\n $pricing_known: 1,\n $now: now,\n });\n updated++;\n } else {\n // New model — insert with estimated cache_read.\n upsertStmt.run({\n $model_id: model.id,\n $input: inputPerMtoken,\n $output: outputPerMtoken,\n $cache_read: estimatedCacheRead,\n $pricing_known: 1,\n $now: now,\n });\n inserted++;\n }\n }\n })();\n\n // Backfill daily_usage rows that were accumulated with pricing_known=0\n // for models that now have pricing. This is a one-time correction per model.\n const backfilled = backfillUnpricedDailyUsage(db);\n\n if (updated + inserted > 0) {\n log.info(\n `pricing sync: ${updated} updated, ${inserted} inserted, ${backfilled} daily_usage rows backfilled`,\n );\n }\n\n return { updated, inserted, backfilled };\n}\n\n/**\n * Backfill daily_usage rows that were accumulated with pricing_known=0\n * (unknown models with $0 cost) for models that now have pricing.\n *\n * Recomputes costs for those rows using current model_pricing.\n * Only affects rows where pricing_known=0 and the model now has pricing.\n */\nfunction backfillUnpricedDailyUsage(db: Database): number {\n // Find daily_usage rows with pricing_known=0 that have a matching model_pricing entry.\n const rows = db\n .prepare(\n `SELECT du.date, du.model, du.requests, du.input_tokens, du.output_tokens,\n du.cache_read_tokens, du.cache_creation_tokens, du.thinking_tokens,\n mp.input_per_mtoken, mp.output_per_mtoken, mp.cache_read_per_mtoken\n FROM daily_usage du\n JOIN model_pricing mp ON du.model = mp.model_id\n WHERE du.pricing_known = 0`,\n )\n .all() as Array<{\n date: string;\n model: string;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n thinking_tokens: number;\n input_per_mtoken: number;\n output_per_mtoken: number;\n cache_read_per_mtoken: number;\n }>;\n\n if (rows.length === 0) return 0;\n\n const updateStmt = db.prepare(\n `UPDATE daily_usage SET\n cost_input = $cost_input,\n cost_output = $cost_output,\n cost_cache_read = $cost_cache_read,\n cost_cache_creation = $cost_cache_creation,\n cost_total = $cost_total,\n pricing_known = 1\n WHERE date = $date AND model = $model`,\n );\n\n db.transaction(() => {\n for (const row of rows) {\n const costInput = (row.input_tokens / 1_000_000) * row.input_per_mtoken;\n const costOutput = (row.output_tokens / 1_000_000) * row.output_per_mtoken;\n const costCacheRead = (row.cache_read_tokens / 1_000_000) * row.cache_read_per_mtoken;\n const costCacheCreation = (row.cache_creation_tokens / 1_000_000) * row.input_per_mtoken;\n const costTotal = costInput + costOutput + costCacheRead + costCacheCreation;\n\n updateStmt.run({\n $date: row.date,\n $model: row.model,\n $cost_input: costInput,\n $cost_output: costOutput,\n $cost_cache_read: costCacheRead,\n $cost_cache_creation: costCacheCreation,\n $cost_total: costTotal,\n });\n }\n })();\n\n return rows.length;\n}\n\n// ---------------------------------------------------------------------------\n// Query functions for the dashboard API\n// ---------------------------------------------------------------------------\n\nexport interface DailyUsageRow {\n date: string;\n model: string;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n thinking_tokens: number;\n cost_input: number;\n cost_output: number;\n cost_cache_read: number;\n cost_cache_creation: number;\n cost_total: number;\n pricing_known: number;\n}\n\nexport interface MonthSummary {\n year: number;\n month: number;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n thinking_tokens: number;\n cost_input: number;\n cost_output: number;\n cost_cache_read: number;\n cost_cache_creation: number;\n cost_total: number;\n has_unpriced: boolean;\n per_model: Array<{\n model: string;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n cost_total: number;\n }>;\n}\n\nexport interface ModelPricingRow {\n model_id: string;\n input_per_mtoken: number;\n output_per_mtoken: number;\n cache_read_per_mtoken: number;\n pricing_known: number;\n updated_at: number;\n}\n\n/** Get daily usage rows (most recent first, limited). */\nexport function getDailyUsage(db: Database, limit = 90): DailyUsageRow[] {\n const rows = db\n .prepare(\n `SELECT date, model, requests, input_tokens, output_tokens,\n cache_read_tokens, cache_creation_tokens, thinking_tokens,\n cost_input, cost_output, cost_cache_read, cost_cache_creation,\n cost_total, pricing_known\n FROM daily_usage\n ORDER BY date DESC, model ASC\n LIMIT $limit`,\n )\n .all({ $limit: limit }) as DailyUsageRow[];\n return rows;\n}\n\n/** Get month summary (totals + per-model breakdown). */\nexport function getMonthSummary(db: Database, year: number, month: number): MonthSummary {\n const monthPrefix = `${year}-${String(month).padStart(2, \"0\")}%`;\n const totals = db\n .prepare(\n `SELECT\n COUNT(*) as request_count,\n SUM(requests) as requests,\n SUM(input_tokens) as input_tokens,\n SUM(output_tokens) as output_tokens,\n SUM(cache_read_tokens) as cache_read_tokens,\n SUM(cache_creation_tokens) as cache_creation_tokens,\n SUM(thinking_tokens) as thinking_tokens,\n SUM(cost_input) as cost_input,\n SUM(cost_output) as cost_output,\n SUM(cost_cache_read) as cost_cache_read,\n SUM(cost_cache_creation) as cost_cache_creation,\n SUM(cost_total) as cost_total,\n MAX(CASE WHEN pricing_known = 0 THEN 1 ELSE 0 END) as has_unpriced\n FROM daily_usage\n WHERE date LIKE $prefix`,\n )\n .get({ $prefix: monthPrefix }) as {\n request_count: number;\n requests: number | null;\n input_tokens: number | null;\n output_tokens: number | null;\n cache_read_tokens: number | null;\n cache_creation_tokens: number | null;\n thinking_tokens: number | null;\n cost_input: number | null;\n cost_output: number | null;\n cost_cache_read: number | null;\n cost_cache_creation: number | null;\n cost_total: number | null;\n has_unpriced: number | null;\n };\n\n const perModel = db\n .prepare(\n `SELECT model,\n SUM(requests) as requests,\n SUM(input_tokens) as input_tokens,\n SUM(output_tokens) as output_tokens,\n SUM(cache_read_tokens) as cache_read_tokens,\n SUM(cache_creation_tokens) as cache_creation_tokens,\n SUM(cost_total) as cost_total\n FROM daily_usage\n WHERE date LIKE $prefix\n GROUP BY model\n ORDER BY cost_total DESC`,\n )\n .all({ $prefix: monthPrefix }) as Array<{\n model: string;\n requests: number;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n cost_total: number;\n }>;\n\n return {\n year,\n month,\n requests: totals.requests ?? 0,\n input_tokens: totals.input_tokens ?? 0,\n output_tokens: totals.output_tokens ?? 0,\n cache_read_tokens: totals.cache_read_tokens ?? 0,\n cache_creation_tokens: totals.cache_creation_tokens ?? 0,\n thinking_tokens: totals.thinking_tokens ?? 0,\n cost_input: totals.cost_input ?? 0,\n cost_output: totals.cost_output ?? 0,\n cost_cache_read: totals.cost_cache_read ?? 0,\n cost_cache_creation: totals.cost_cache_creation ?? 0,\n cost_total: totals.cost_total ?? 0,\n has_unpriced: totals.has_unpriced === 1,\n per_model: perModel,\n };\n}\n\n/** Get available months (year-month pairs that have data). */\nexport function getAvailableMonths(db: Database): Array<{ year: number; month: number }> {\n const rows = db\n .prepare(\"SELECT DISTINCT substr(date, 1, 7) as ym FROM daily_usage ORDER BY ym DESC\")\n .all() as Array<{ ym: string }>;\n return rows.map((r) => ({\n year: Number(r.ym.slice(0, 4)),\n month: Number(r.ym.slice(5, 7)),\n }));\n}\n\n/** Get current pricing table. */\nexport function getPricingTable(db: Database): ModelPricingRow[] {\n return db\n .prepare(\n `SELECT model_id, input_per_mtoken, output_per_mtoken, cache_read_per_mtoken,\n pricing_known, updated_at\n FROM model_pricing\n ORDER BY model_id ASC`,\n )\n .all() as ModelPricingRow[];\n}\n","// SSE parsing helpers for Anthropic and OpenAI streaming responses.\n\nimport type { AnthropicSseEvent, OpenAIStreamChunk, TimedChunk } from \"./types.js\";\n\n/**\n * Parse an Anthropic SSE response from timed chunks into events with\n * timing markers. Each event gets the timestamp of the chunk it was found\n * in, fixing the old 1:1 round-robin assignment that broke when a single\n * HTTP chunk contained multiple SSE events.\n */\nexport function parseAnthropicSse(chunks: TimedChunk[]): AnthropicSseEvent[] {\n const events: AnthropicSseEvent[] = [];\n\n const parseEventBlock = (block: string, evType: string): AnthropicSseEvent | null => {\n const lines = block.split(\"\\n\");\n let type = evType;\n let dataLine: string | null = null;\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n type = line.slice(7).trim();\n } else if (line.startsWith(\"data: \")) {\n dataLine = line.slice(6);\n }\n }\n if (dataLine == null) return null;\n if (dataLine === \"[DONE]\") return null;\n try {\n const parsed = JSON.parse(dataLine) as Record<string, unknown>;\n return {\n type: (parsed.type as string) ?? type,\n ...parsed,\n };\n } catch {\n return null;\n }\n };\n\n let buf = \"\";\n let evType = \"\";\n for (const { text, time } of chunks) {\n buf += text;\n // Process complete SSE event blocks (delimited by \\n\\n).\n let idx = buf.indexOf(\"\\n\\n\");\n while (idx !== -1) {\n const block = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n // Extract event type from the block if present.\n for (const line of block.split(\"\\n\")) {\n if (line.startsWith(\"event: \")) evType = line.slice(7).trim();\n }\n const ev = parseEventBlock(block, evType);\n if (ev) {\n ev.received_at = time;\n events.push(ev);\n }\n idx = buf.indexOf(\"\\n\\n\");\n }\n }\n // Parse any remaining incomplete block in the buffer.\n if (buf.trim()) {\n const ev = parseEventBlock(buf, evType);\n if (ev) {\n const lastTime = chunks[chunks.length - 1]?.time;\n if (lastTime != null) ev.received_at = lastTime;\n events.push(ev);\n }\n }\n return events;\n}\n\n/**\n * Parse an OpenAI SSE response from timed chunks into chunks with timing\n * markers. Each chunk is parsed individually and its timestamp is assigned\n * to all events found within it.\n */\nexport function parseOpenAiSse(chunks: TimedChunk[]): OpenAIStreamChunk[] {\n const result: OpenAIStreamChunk[] = [];\n\n const parseDataLine = (data: string): OpenAIStreamChunk | null => {\n if (data === \"[DONE]\") return null;\n try {\n return JSON.parse(data) as OpenAIStreamChunk;\n } catch {\n return null;\n }\n };\n\n let buf = \"\";\n for (const { text, time } of chunks) {\n buf += text;\n let idx = buf.indexOf(\"\\n\\n\");\n while (idx !== -1) {\n const block = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n for (const line of block.split(\"\\n\")) {\n if (line.startsWith(\"data: \")) {\n const ch = parseDataLine(line.slice(6));\n if (ch) {\n ch.received_at = time;\n result.push(ch);\n }\n }\n }\n idx = buf.indexOf(\"\\n\\n\");\n }\n }\n if (buf.trim()) {\n for (const line of buf.split(\"\\n\")) {\n if (line.startsWith(\"data: \")) {\n const ch = parseDataLine(line.slice(6));\n if (ch) {\n const lastTime = chunks[chunks.length - 1]?.time;\n if (lastTime != null) ch.received_at = lastTime;\n result.push(ch);\n }\n }\n }\n }\n return result;\n}\n","/**\n * Extract a model name from a parsed request body.\n * Returns the string value of `body.model` when the body is a non-null object\n * and `model` is a string; otherwise returns `undefined`.\n */\nexport function extractModelName(body: unknown): string | undefined {\n if (body === null || typeof body !== \"object\") return undefined;\n const model = (body as { model?: unknown }).model;\n if (typeof model !== \"string\") return undefined;\n return model;\n}\n","// Internal numeric helpers for usage extraction.\n// Not re-exported by the barrel — internal to the usage module.\n\nimport type { UsageMetrics } from \"./types.js\";\n\nexport function num(v: unknown): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : 0;\n}\n\nexport function numOr(v: unknown, fallback: number): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\n\nexport function computeTps(\n output: number | null,\n durationMs: number | null,\n ttftMs: number | null,\n): number | null {\n if (output == null || output <= 0) return null;\n if (durationMs == null) return null;\n const genMs = ttftMs != null && ttftMs > 0 ? durationMs - ttftMs : durationMs;\n // Require at least 1 second of generation time — below this the rate is\n // dominated by timing noise and produces nonsensical values (e.g. 44k t/s).\n if (genMs < 1000) return null;\n return (output / genMs) * 1000;\n}\n\nexport function emptyMetrics(\n provider: \"anthropic\" | \"openai\",\n streaming: boolean,\n durationMs: number | null,\n): UsageMetrics {\n return {\n provider,\n streaming,\n input_tokens: null,\n output_tokens: null,\n cache_creation_tokens: null,\n cache_read_tokens: null,\n total_input_tokens: null,\n total_output_tokens: null,\n thinking_tokens: null,\n ttft_ms: null,\n duration_ms: durationMs,\n tps: null,\n usage_missing: true,\n };\n}\n","// Extractors for LLM token usage metrics from Anthropic and OpenAI responses.\n//\n// Attribution rules are derived from official API docs:\n// Anthropic: https://platform.claude.com/docs/en/build-with-claude/streaming\n// OpenAI: https://developers.openai.com/api/docs/api-reference/chat\n\nimport { extractModelName } from \"../models/name.js\";\nimport { computeTps, emptyMetrics, num, numOr } from \"./helpers.js\";\nimport { parseAnthropicSse, parseOpenAiSse } from \"./sse-parse.js\";\nimport type {\n AnthropicSseEvent,\n AnthropicUsage,\n OpenAIStreamChunk,\n OpenAIUsage,\n TimedChunk,\n UsageMetrics,\n} from \"./types.js\";\n\ntype ResponseLike = {\n responseBody: string;\n chunks: TimedChunk[];\n durationMs: number;\n requestStartedAt: number;\n};\n\ntype UsageExtractResult = UsageMetrics;\n\ntype Extractor = (res: ResponseLike) => UsageExtractResult;\n\ntype ProviderExtractors = { stream: Extractor; batch: Extractor };\n\nconst EXTRACTORS: Record<string, ProviderExtractors> = {\n anthropic: {\n stream: (res) =>\n extractAnthropicStreaming(\n parseAnthropicSse(res.chunks),\n res.requestStartedAt,\n res.durationMs,\n ),\n batch: (res) => extractAnthropicNonStreaming(JSON.parse(res.responseBody), res.durationMs),\n },\n openai: {\n stream: (res) =>\n extractOpenAiStreaming(parseOpenAiSse(res.chunks), res.requestStartedAt, res.durationMs),\n batch: (res) => extractOpenAiNonStreaming(JSON.parse(res.responseBody), res.durationMs),\n },\n};\n\n/**\n * Extract Anthropic usage from a NON-STREAMING response body.\n *\n * Rules (per https://platform.claude.com/docs/en/build-with-claude/working-with-messages):\n * - usage lives at top-level `usage`\n * - cache_creation_input_tokens / cache_read_input_tokens are null when no caching, 0 when caching enabled but nothing cached\n * - total input = input_tokens + cache_creation_input_tokens + cache_read_input_tokens\n */\nexport function extractAnthropicNonStreaming(\n body: unknown,\n durationMs: number | null,\n): UsageMetrics {\n const b = body as { usage?: AnthropicUsage };\n const u = b?.usage;\n if (!u) {\n return emptyMetrics(\"anthropic\", false, durationMs);\n }\n const input = num(u.input_tokens);\n const output = num(u.output_tokens);\n const cacheCreate = numOr(u.cache_creation_input_tokens, 0);\n const cacheRead = numOr(u.cache_read_input_tokens, 0);\n const totalInput = input + cacheCreate + cacheRead;\n const thinking = u.output_tokens_details?.thinking_tokens ?? null;\n return {\n provider: \"anthropic\",\n streaming: false,\n input_tokens: input,\n output_tokens: output,\n cache_creation_tokens: cacheCreate,\n cache_read_tokens: cacheRead,\n total_input_tokens: totalInput,\n total_output_tokens: output,\n thinking_tokens: thinking,\n ttft_ms: null, // TTFT not derivable from non-streaming response\n duration_ms: durationMs,\n tps: computeTps(output, durationMs, null),\n usage_missing: false,\n };\n}\n\n/**\n * Extract Anthropic usage from a STREAMING response (array of SSE events).\n *\n * Rules (per https://platform.claude.com/docs/en/build-with-claude/streaming):\n * - message_start.message.usage seeds input_tokens + cache_* (null → 0)\n * - message_delta.usage.output_tokens is CUMULATIVE — always overwrite\n * - message_delta.usage.cache_* / input_tokens only overwrite if != null\n * - ping events carry no usage\n * - TTFT = delta from request start to first content_block_delta event (any delta type)\n */\nexport function extractAnthropicStreaming(\n events: AnthropicSseEvent[],\n requestStartedAt: number,\n wallClockDurationMs?: number,\n): UsageMetrics {\n let input: number | null = null;\n let output: number | null = null;\n let cacheCreate = 0;\n let cacheRead = 0;\n let thinking: number | null = null;\n let ttftMs: number | null = null;\n let lastEventAt: number | null = null;\n let sawUsage = false;\n\n for (const ev of events) {\n if (ev.received_at) lastEventAt = ev.received_at;\n\n if (ev.type === \"message_start\") {\n const u = ev.message?.usage;\n if (u) {\n input = num(u.input_tokens);\n cacheCreate = numOr(u.cache_creation_input_tokens, 0);\n cacheRead = numOr(u.cache_read_input_tokens, 0);\n // output_tokens here is ~1 (placeholder) — do NOT use as final\n sawUsage = true;\n }\n } else if (ev.type === \"message_delta\") {\n const u = ev.usage;\n if (u) {\n // output_tokens is cumulative — ALWAYS overwrite\n if (u.output_tokens != null) {\n output = num(u.output_tokens);\n sawUsage = true;\n }\n // cache / input fields only overwrite if present and non-null\n if (u.input_tokens != null) input = num(u.input_tokens);\n if (u.cache_creation_input_tokens != null) cacheCreate = num(u.cache_creation_input_tokens);\n if (u.cache_read_input_tokens != null) cacheRead = num(u.cache_read_input_tokens);\n if (u.output_tokens_details?.thinking_tokens != null) {\n thinking = u.output_tokens_details.thinking_tokens;\n }\n }\n } else if (ev.type === \"content_block_delta\" && ttftMs === null && ev.received_at) {\n ttftMs = ev.received_at - requestStartedAt;\n }\n }\n\n const eventDurationMs = lastEventAt ? lastEventAt - requestStartedAt : null;\n // Wall-clock floor: proxy flush runs after the last chunk, so wallClockDurationMs\n // is always >= eventBasedDuration. This prevents collapse when all SSE events\n // share one HTTP chunk timestamp.\n const durationMs =\n wallClockDurationMs != null\n ? Math.max(eventDurationMs ?? 0, wallClockDurationMs)\n : eventDurationMs;\n const totalInput = input != null ? input + cacheCreate + cacheRead : null;\n\n return {\n provider: \"anthropic\",\n streaming: true,\n input_tokens: input,\n output_tokens: output,\n cache_creation_tokens: cacheCreate,\n cache_read_tokens: cacheRead,\n total_input_tokens: totalInput,\n total_output_tokens: output,\n thinking_tokens: thinking,\n ttft_ms: ttftMs,\n duration_ms: durationMs,\n tps: computeTps(output, durationMs, ttftMs),\n usage_missing: !sawUsage,\n };\n}\n\n/**\n * Extract OpenAI usage from a NON-STREAMING response body.\n *\n * Rules (per https://developers.openai.com/api/docs/api-reference/chat):\n * - usage always present with prompt_tokens, completion_tokens, total_tokens\n * - prompt_tokens_details.cached_tokens defaults to 0 but may be omitted by compatible providers\n * - completion_tokens_details.reasoning_tokens present for reasoning models\n */\nexport function extractOpenAiNonStreaming(body: unknown, durationMs: number | null): UsageMetrics {\n const b = body as { usage?: OpenAIUsage };\n const u = b?.usage;\n if (!u) {\n return emptyMetrics(\"openai\", false, durationMs);\n }\n const prompt = num(u.prompt_tokens);\n const completion = num(u.completion_tokens);\n const cached = numOr(u.prompt_tokens_details?.cached_tokens, 0);\n const reasoning = u.completion_tokens_details?.reasoning_tokens ?? null;\n return {\n provider: \"openai\",\n streaming: false,\n input_tokens: prompt,\n output_tokens: completion,\n cache_creation_tokens: null, // OpenAI has no \"cache creation\" concept\n cache_read_tokens: cached,\n total_input_tokens: prompt,\n total_output_tokens: completion,\n thinking_tokens: reasoning,\n ttft_ms: null,\n duration_ms: durationMs,\n tps: computeTps(completion, durationMs, null),\n usage_missing: false,\n };\n}\n\n/**\n * Extract OpenAI usage from a STREAMING response (array of chunks).\n *\n * Rules (per https://developers.openai.com/api/docs/api-reference/chat):\n * - Without stream_options.include_usage, every chunk has usage: null → no usage\n * - With include_usage, the FINAL chunk before [DONE] carries the full usage object\n * (per spec choices: [], but compatible providers may include a non-empty choices array)\n * - TTFT = delta from request start to first chunk with a non-empty delta\n * (content, reasoning_content, or tool_calls — whichever arrives first)\n * - If stream is aborted, the usage chunk may never arrive\n */\nexport function extractOpenAiStreaming(\n chunks: OpenAIStreamChunk[],\n requestStartedAt: number,\n wallClockDurationMs?: number,\n): UsageMetrics {\n let usage: OpenAIUsage | null = null;\n let ttftMs: number | null = null;\n let lastChunkAt: number | null = null;\n\n for (const ch of chunks) {\n if (ch.received_at) lastChunkAt = ch.received_at;\n // TTFT = time to first non-empty delta of any kind: content, reasoning, or tool calls.\n // Reasoning models emit reasoning_content first; tool-calling responses may only have tool_calls.\n if (ttftMs === null && ch.received_at && Array.isArray(ch.choices) && ch.choices.length > 0) {\n const d = ch.choices[0]?.delta;\n if (d && (d.content || d.reasoning_content || d.tool_calls)) {\n ttftMs = ch.received_at - requestStartedAt;\n }\n }\n // Usage chunk: usage is non-null. Per OpenAI spec this is the final chunk\n // with choices: [], but compatible providers (e.g. umans-coder) send it\n // with a non-empty choices array containing an empty delta — so we key\n // solely on the presence of the usage object.\n if (ch.usage != null) {\n usage = ch.usage;\n }\n }\n\n const eventDurationMs = lastChunkAt ? lastChunkAt - requestStartedAt : null;\n // Wall-clock floor: proxy flush runs after the last chunk, so wallClockDurationMs\n // is always >= eventBasedDuration. This prevents collapse when all SSE events\n // share one HTTP chunk timestamp.\n const durationMs =\n wallClockDurationMs != null\n ? Math.max(eventDurationMs ?? 0, wallClockDurationMs)\n : eventDurationMs;\n\n if (!usage) {\n const m = emptyMetrics(\"openai\", true, durationMs);\n m.ttft_ms = ttftMs;\n m.tps = computeTps(null, durationMs, ttftMs);\n m.usage_missing = true;\n return m;\n }\n\n const prompt = num(usage.prompt_tokens);\n const completion = num(usage.completion_tokens);\n const cached = numOr(usage.prompt_tokens_details?.cached_tokens, 0);\n const reasoning = usage.completion_tokens_details?.reasoning_tokens ?? null;\n\n return {\n provider: \"openai\",\n streaming: true,\n input_tokens: prompt,\n output_tokens: completion,\n cache_creation_tokens: null,\n cache_read_tokens: cached,\n total_input_tokens: prompt,\n total_output_tokens: completion,\n thinking_tokens: reasoning,\n ttft_ms: ttftMs,\n duration_ms: durationMs,\n tps: computeTps(completion, durationMs, ttftMs),\n usage_missing: false,\n };\n}\n\n/** Extract model name from a request body (both Anthropic and OpenAI shapes). */\nexport function extractModel(requestBody: unknown): string {\n const model = extractModelName(requestBody);\n return model && model.length > 0 ? model : \"unknown\";\n}\n\n/**\n * Unified usage extraction entry point. Dispatches to the correct extractor\n * based on provider + streaming flags, parses the request body for the model\n * name, and stamps the model onto the returned metrics.\n *\n * @param opts.provider \"anthropic\" | \"openai\"\n * @param opts.streaming whether the response was SSE-streamed\n * @param opts.requestBody raw request body JSON string\n * @param opts.responseBody raw response body (SSE string for streaming, JSON for non-streaming)\n * @param opts.durationMs full request duration in ms (used for non-streaming + fallback)\n * @param opts.requestStartedAt epoch ms when the request started (used for streaming TTFT/duration)\n * @returns `{ model, metrics }` — model is the extracted model name, metrics has model stamped on it\n */\nexport function extractUsage(opts: {\n provider: \"anthropic\" | \"openai\";\n streaming: boolean;\n requestBody: string;\n responseBody: string;\n durationMs: number;\n requestStartedAt: number;\n chunks?: TimedChunk[];\n}): { model: string; metrics: UsageMetrics } {\n const parsedBody = JSON.parse(opts.requestBody);\n const model = extractModel(parsedBody);\n const key = opts.streaming ? \"stream\" : \"batch\";\n const extractor = EXTRACTORS[opts.provider]?.[key] ?? EXTRACTORS.openai[key];\n const metrics = extractor({\n responseBody: opts.responseBody,\n chunks: opts.chunks ?? [],\n durationMs: opts.durationMs,\n requestStartedAt: opts.requestStartedAt,\n });\n metrics.model = model;\n return { model, metrics };\n}\n","// SQL DDL: schema + view + per-model ring-buffer retention.\n//\n// These DDL constants define the SQL schema for capturing usage metrics.\n// They are imported by db.ts (for schema migration) and by tests.\n\n/**\n * SQL DDL to add token-usage columns to the captures table.\n * Run after the existing CREATE TABLE (uses ALTER TABLE for migration safety).\n */\nexport const USAGE_COLUMNS_DDL = `\n-- Token usage columns (added to captures table)\nALTER TABLE captures ADD COLUMN provider TEXT; -- 'anthropic' | 'openai'\nALTER TABLE captures ADD COLUMN model TEXT; -- e.g. 'claude-sonnet-4-5', 'gpt-4o'\nALTER TABLE captures ADD COLUMN streaming INTEGER DEFAULT 0;\nALTER TABLE captures ADD COLUMN input_tokens INTEGER;\nALTER TABLE captures ADD COLUMN output_tokens INTEGER;\nALTER TABLE captures ADD COLUMN cache_creation_tokens INTEGER;\nALTER TABLE captures ADD COLUMN cache_read_tokens INTEGER;\nALTER TABLE captures ADD COLUMN total_input_tokens INTEGER;\nALTER TABLE captures ADD COLUMN total_output_tokens INTEGER;\nALTER TABLE captures ADD COLUMN thinking_tokens INTEGER;\nALTER TABLE captures ADD COLUMN ttft_ms REAL;\nALTER TABLE captures ADD COLUMN tps REAL;\nALTER TABLE captures ADD COLUMN usage_missing INTEGER DEFAULT 0;\nALTER TABLE captures ADD COLUMN metrics_extracted_at INTEGER;\n` as const;\n\n/**\n * SQL view: latest N requests per model with all metrics.\n * Replace :N with the desired window size (default 100).\n *\n * Uses ROW_NUMBER() window function (SQLite 3.25+).\n */\nexport const LATEST_N_PER_MODEL_VIEW = `\nCREATE VIEW IF NOT EXISTS v_latest_requests_per_model AS\nWITH ranked AS (\n SELECT\n c.*,\n ROW_NUMBER() OVER (PARTITION BY c.model ORDER BY c.started_at DESC) AS rn\n FROM captures c\n WHERE c.model IS NOT NULL\n AND c.state = 'done'\n)\nSELECT * FROM ranked WHERE rn <= 100;\n` as const;\n\n/**\n * SQL: optimized per-model performance summary for the dashboard.\n *\n * Computes mean TTFT/TPS and nearest-rank percentiles (p10/p50/p95), plus\n * SUM of input/output/cached tokens and cached% in a single scan over the\n * latest 100 done requests per model. IQR/stddev removed — average is shown\n * as the primary metric.\n */\nexport const PERFORMANCE_STATS_SQL = `\nWITH latest_per_model AS (\n SELECT model, provider, streaming,\n ttft_ms, tps,\n total_input_tokens, total_output_tokens,\n cache_read_tokens, thinking_tokens,\n ROW_NUMBER() OVER (PARTITION BY model ORDER BY started_at DESC) AS rn\n FROM captures\n WHERE model IS NOT NULL AND state = 'done' AND usage_missing = 0\n),\nbase AS (\n SELECT model, provider, streaming,\n ttft_ms, tps,\n total_input_tokens, total_output_tokens,\n cache_read_tokens, thinking_tokens\n FROM latest_per_model\n WHERE rn <= 100\n),\nranked AS (\n SELECT\n model, provider, streaming,\n ttft_ms, tps,\n total_input_tokens, total_output_tokens, cache_read_tokens, thinking_tokens,\n ROW_NUMBER() OVER (PARTITION BY model ORDER BY ttft_ms NULLS LAST) AS ttft_rn,\n COUNT(ttft_ms) OVER (PARTITION BY model) AS ttft_cnt,\n ROW_NUMBER() OVER (PARTITION BY model ORDER BY tps NULLS LAST) AS tps_rn,\n COUNT(tps) OVER (PARTITION BY model) AS tps_cnt\n FROM base\n),\npctiles AS (\n SELECT\n model,\n MAX(CASE WHEN ttft_rn = CAST(CEIL(0.10 * ttft_cnt) AS INT) THEN ttft_ms END) AS ttft_p10,\n MAX(CASE WHEN ttft_rn = CAST(CEIL(0.50 * ttft_cnt) AS INT) THEN ttft_ms END) AS ttft_p50,\n MAX(CASE WHEN ttft_rn = CAST(CEIL(0.95 * ttft_cnt) AS INT) THEN ttft_ms END) AS ttft_p95,\n MAX(CASE WHEN tps_rn = CAST(CEIL(0.10 * tps_cnt) AS INT) THEN tps END) AS tps_p10,\n MAX(CASE WHEN tps_rn = CAST(CEIL(0.50 * tps_cnt) AS INT) THEN tps END) AS tps_p50,\n MAX(CASE WHEN tps_rn = CAST(CEIL(0.95 * tps_cnt) AS INT) THEN tps END) AS tps_p95\n FROM ranked\n GROUP BY model\n)\nSELECT\n r.model,\n r.provider,\n COUNT(*) AS request_count,\n SUM(CASE WHEN r.streaming = 1 THEN 1 ELSE 0 END) AS streaming_count,\n SUM(r.total_input_tokens) AS total_input_tokens,\n SUM(r.total_output_tokens) AS total_output_tokens,\n SUM(r.cache_read_tokens) AS total_cache_read_tokens,\n SUM(r.thinking_tokens) AS total_thinking_tokens,\n CASE\n WHEN SUM(r.total_input_tokens) > 0\n THEN CAST(SUM(r.cache_read_tokens) AS REAL) / SUM(r.total_input_tokens) * 100.0\n ELSE 0\n END AS cached_pct,\n AVG(r.ttft_ms) AS ttft_mean,\n MAX(p.ttft_p10) AS ttft_p10,\n MAX(p.ttft_p50) AS ttft_p50,\n MAX(p.ttft_p95) AS ttft_p95,\n AVG(r.tps) AS tps_mean,\n MAX(p.tps_p10) AS tps_p10,\n MAX(p.tps_p50) AS tps_p50,\n MAX(p.tps_p95) AS tps_p95\nFROM ranked r\nLEFT JOIN pctiles p USING (model)\nGROUP BY r.model, r.provider\nORDER BY request_count DESC;\n` as const;\n","// SQLite-backed persistent store for vision descriptions.\n// Extracted from CaptureDB to isolate the vision-description cache lifecycle\n// (upsert / get / delete / evict / warming-list) from capture persistence.\n\nimport type { Database } from \"bun:sqlite\";\n\ntype PreparedStatement = ReturnType<Database[\"prepare\"]>;\n\n/** Parameters for upsertVisionDescription. */\nexport interface VisionDescriptionUpsertParams {\n $key: string;\n $image_hash: string;\n $model: string;\n $prompt_version: number;\n $description: string;\n $now: number;\n}\n\n/**\n * Persistent vision-description store backed by the `vision_descriptions`\n * SQLite table. The table is created/managed by CaptureDB's schema migration;\n * this class only owns the prepared statements and method bodies that operate\n * on that table.\n */\nexport class VisionDescriptionStore {\n private readonly db: Database;\n private readonly stmtInsert: PreparedStatement;\n private readonly stmtGet: PreparedStatement;\n private readonly stmtTouch: PreparedStatement;\n private readonly stmtDelete: PreparedStatement;\n private readonly stmtEvict: PreparedStatement;\n private readonly stmtCount: PreparedStatement;\n private readonly stmtListForWarming: PreparedStatement;\n\n constructor(db: Database) {\n this.db = db;\n this.stmtInsert = db.prepare(\n `INSERT INTO vision_descriptions (key, image_hash, model, prompt_version, description, created_at, last_accessed_at)\n VALUES ($key, $image_hash, $model, $prompt_version, $description, $now, $now)\n ON CONFLICT(key) DO UPDATE SET\n description = excluded.description,\n created_at = excluded.created_at,\n last_accessed_at = excluded.last_accessed_at`,\n );\n this.stmtGet = db.prepare(\n \"SELECT description, created_at FROM vision_descriptions WHERE key = $key\",\n );\n this.stmtTouch = db.prepare(\n \"UPDATE vision_descriptions SET last_accessed_at = $now WHERE key = $key\",\n );\n this.stmtDelete = db.prepare(\"DELETE FROM vision_descriptions WHERE key = $key\");\n this.stmtEvict = db.prepare(\n `DELETE FROM vision_descriptions\n WHERE key IN (\n SELECT key FROM vision_descriptions\n WHERE created_at < $cutoff\n ORDER BY last_accessed_at ASC\n LIMIT $n\n )`,\n );\n this.stmtCount = db.prepare(\"SELECT COUNT(*) AS c FROM vision_descriptions\");\n this.stmtListForWarming = db.prepare(\n `SELECT key, description FROM vision_descriptions\n WHERE created_at > $cutoff\n ORDER BY last_accessed_at DESC\n LIMIT $n`,\n );\n }\n\n /** Insert or update a vision description in the persistent store. */\n upsert(params: VisionDescriptionUpsertParams): void {\n this.stmtInsert.run(params as unknown as never);\n }\n\n /** Look up a vision description by key. Returns null if not found.\n * Updates last_accessed_at on hit. */\n get(key: string): { description: string; created_at: number } | null {\n const row = this.stmtGet.get({ $key: key }) as\n | { description: string; created_at: number }\n | undefined;\n if (!row) return null;\n this.stmtTouch.run({ $key: key, $now: Date.now() });\n return row;\n }\n\n delete(key: string): void {\n this.stmtDelete.run({ $key: key });\n }\n\n /** Evict expired entries (created_at < cutoff) and enforce a max row ceiling.\n * Returns the number of rows deleted. */\n evict(cutoff: number, maxRows: number): number {\n let deleted = 0;\n this.db.transaction(() => {\n const expiredRes = this.stmtEvict.run({ $cutoff: cutoff, $n: 1000 }) as {\n changes: number;\n };\n deleted += expiredRes.changes;\n const count = (this.stmtCount.get() as { c: number }).c;\n if (count > maxRows) {\n const excess = count - maxRows;\n const overflowRes = this.db\n .prepare(\n \"DELETE FROM vision_descriptions WHERE key IN (SELECT key FROM vision_descriptions ORDER BY last_accessed_at ASC LIMIT ?)\",\n )\n .run(excess) as { changes: number };\n deleted += overflowRes.changes;\n }\n })();\n return deleted;\n }\n\n /** List recent non-expired vision descriptions for cache warming.\n * Returns up to `limit` entries. */\n listForWarming(limit: number, cutoff: number): Array<{ key: string; description: string }> {\n return this.stmtListForWarming.all({ $n: limit, $cutoff: cutoff }) as Array<{\n key: string;\n description: string;\n }>;\n }\n}\n","// HTTP header utilities.\n\n/** Hop-by-hop headers that must not be forwarded by a proxy. */\nexport const HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"te\",\n \"trailers\",\n \"transfer-encoding\",\n \"upgrade\",\n \"content-length\",\n \"host\",\n]);\n\n/** Convert a Headers object to a plain object (lowercase keys, comma-joined dups). */\nexport function headersToObject(h: Headers): Record<string, string> {\n const obj: Record<string, string> = {};\n h.forEach((v, k) => {\n const lk = k.toLowerCase();\n obj[lk] = obj[lk] === undefined ? v : `${obj[lk]}, ${v}`;\n });\n return obj;\n}\n\n/** Header names whose values must never be persisted to storage/dashboard. */\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\n\n/** Return a shallow copy of `headers` with sensitive values replaced by \"[REDACTED]\". */\nexport function redactHeaders(headers: Record<string, string>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) {\n out[k] = REDACTED_HEADERS.has(k.toLowerCase()) ? \"[REDACTED]\" : v;\n }\n return out;\n}\n","// Text encoder/decoder utilities.\n\nconst textDecoder = new TextDecoder(\"utf-8\", { fatal: true });\nconst textEncoder = new TextEncoder();\n\n/** Decode bytes to text, falling back to base64 prefix for invalid UTF-8. */\nexport function decodeText(buf: Uint8Array): string {\n try {\n return textDecoder.decode(buf);\n } catch {\n return `__B64__${Buffer.from(buf).toString(\"base64\")}`;\n }\n}\n\nexport { textDecoder, textEncoder };\n","// Capture summary builders.\n\nimport type {\n CaptureRow,\n CaptureState,\n CaptureSummary,\n ProtocolConfig,\n RequestMeta,\n ResponseMeta,\n} from \"../types.js\";\n\n/** Build a summary from a full capture row (no body data). */\nexport function summary(row: CaptureRow): CaptureSummary {\n return {\n id: row.id,\n method: row.method,\n path: row.path,\n response_status: row.response_status,\n is_sse: !!row.is_sse,\n content_type: row.content_type,\n request_size: row.request_size,\n response_size: row.response_size,\n duration_ms: row.duration_ms,\n state: row.state,\n started_at: row.started_at,\n finished_at: row.finished_at,\n incoming_protocol: row.incoming_protocol ?? \"\",\n upstream_protocol: row.upstream_protocol ?? \"\",\n model: row.model,\n usage_missing: row.usage_missing == null ? null : !!row.usage_missing,\n ttft_ms: row.ttft_ms ?? null,\n tps: row.tps ?? null,\n cache_creation_tokens: row.cache_creation_tokens ?? null,\n cache_read_tokens: row.cache_read_tokens ?? null,\n total_input_tokens: row.total_input_tokens ?? null,\n output_tokens: row.output_tokens ?? null,\n total_output_tokens: row.total_output_tokens ?? null,\n is_vision: !!row.is_vision,\n status_source: (row.status_source as \"upstream\" | \"gate\" | null) ?? null,\n gate_reason: row.gate_reason ?? null,\n };\n}\n\n/** Build a summary for a newly-started capture (before response). */\nexport function newSummary(\n id: number,\n reqMeta: RequestMeta,\n config: ProtocolConfig,\n state: CaptureState = \"streaming\",\n model: string | null = null,\n): CaptureSummary {\n return {\n id,\n method: reqMeta.method,\n path: reqMeta.path,\n response_status: null,\n is_sse: false,\n content_type: null,\n request_size: reqMeta.request_size,\n response_size: 0,\n duration_ms: 0,\n state,\n started_at: reqMeta.started_at,\n finished_at: null,\n incoming_protocol: config.incomingProtocol,\n upstream_protocol: config.upstreamProtocol,\n model,\n usage_missing: null,\n ttft_ms: null,\n tps: null,\n cache_creation_tokens: null,\n cache_read_tokens: null,\n total_input_tokens: null,\n output_tokens: null,\n total_output_tokens: null,\n is_vision: false,\n status_source: null,\n gate_reason: null,\n };\n}\n\n/** Build a summary from queued response metadata. */\nexport function buildSummary(\n id: number,\n reqMeta: RequestMeta,\n res: ResponseMeta,\n config: ProtocolConfig,\n): CaptureSummary {\n const u = res.$usage ?? null;\n return {\n id,\n method: reqMeta.method,\n path: reqMeta.path,\n response_status: res.$status,\n is_sse: !!res.$sse,\n content_type: res.$ct,\n request_size: reqMeta.request_size,\n response_size: res.$rs,\n duration_ms: res.$dur,\n state: \"done\",\n started_at: reqMeta.started_at,\n finished_at: res.$fin,\n incoming_protocol: config.incomingProtocol,\n upstream_protocol: config.upstreamProtocol,\n model: res.$model ?? null,\n usage_missing: u?.usage_missing ?? null,\n ttft_ms: u?.ttft_ms ?? null,\n tps: u?.tps ?? null,\n cache_creation_tokens: u?.cache_creation_tokens ?? null,\n cache_read_tokens: u?.cache_read_tokens ?? null,\n total_input_tokens: u?.total_input_tokens ?? null,\n output_tokens: u?.output_tokens ?? null,\n total_output_tokens: u?.total_output_tokens ?? null,\n is_vision: false,\n status_source: res.$status_source,\n gate_reason: res.$gate_reason,\n };\n}\n","// 429 response classification.\n\n/** Classify a 429 response as concurrency, rate-limit, or gateway-cdn. */\nexport function classify429(res: Response): \"concurrency\" | \"rate_limit\" | \"gateway\" {\n const server = res.headers.get(\"server\") ?? \"\";\n if (server.includes(\"cloudflare\") || server.includes(\"fastly\")) return \"gateway\";\n const ra = res.headers.get(\"retry-after\");\n if (ra && Number(ra) <= 10) return \"concurrency\";\n return \"rate_limit\";\n}\n","// Concurrency weight computation.\n\n/** Source of model weights derived from upstream pricing. */\nexport interface WeightModelSource {\n getWeight(modelId: string): number;\n}\n\n/** Compute the concurrency weight for a request.\n *\n * Weight is derived from model pricing via the provided WeightModelSource.\n * Returns 1 if the model is unknown or no source is available.\n */\nexport function computeRequestWeight(\n modelName: string | undefined,\n models: WeightModelSource | null,\n): number {\n if (typeof modelName !== \"string\" || modelName === \"\") return 1;\n if (models) return models.getWeight(modelName);\n return 1;\n}\n","import type { BreakerState, GateConfig, ProxyConfig } from \"../types.js\";\n\nexport type { BreakerState };\n\n/** Raw config keys that should trigger a gate reconfigure on reload. */\nexport const GATE_RECONFIG_FIELDS = new Set<keyof ProxyConfig>([\n \"breakerThreshold\",\n \"breakerWindowMs\",\n \"breakerCooldownMs\",\n \"queueTimeoutMs\",\n \"maxQueueDepth\",\n \"releaseCooldownMs\",\n \"concurrencyMainReservation\",\n \"concurrencyVisionReservation\",\n]);\n\nexport const SCALE = 1000;\n\nexport interface Waiter {\n resolve: (permit: Permit) => void;\n reject: (e: GateError) => void;\n enqueuedAt: number;\n weight: number;\n intention: string;\n signal?: AbortSignal;\n onAcquire?: () => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\nexport interface Permit {\n release: () => void;\n}\n\nexport interface ConcurrencyGateOptions {\n hardCap: number;\n softLimit: number;\n releaseCooldownMs: number;\n breakerThreshold: number;\n breakerWindowMs: number;\n breakerCooldownMs: number;\n maxQueueDepth: number;\n queueTimeoutMs: number;\n intentions?: Record<string, number>;\n}\n\nexport class GateError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = \"GateError\";\n this.code = code;\n }\n}\n\nexport function gateOptionsFromConfig(config: GateConfig): ConcurrencyGateOptions {\n return {\n hardCap: config.concurrencyHardCap,\n softLimit: config.concurrencySoftLimit,\n releaseCooldownMs: config.releaseCooldownMs,\n breakerThreshold: config.breakerThreshold,\n breakerWindowMs: config.breakerWindowMs,\n breakerCooldownMs: config.breakerCooldownMs,\n maxQueueDepth: config.maxQueueDepth,\n queueTimeoutMs: config.queueTimeoutMs,\n intentions: {\n main: config.concurrencyMainReservation,\n vision: config.concurrencyVisionReservation,\n },\n };\n}\n","import type { BreakerState } from \"./types.js\";\n\nexport class CircuitBreaker {\n private state: BreakerState = \"closed\";\n private concurrency429s: number[] = [];\n private openedAt = 0;\n private threshold: number;\n private windowMs: number;\n private cooldownMs: number;\n\n constructor(threshold: number, windowMs: number, cooldownMs: number) {\n this.threshold = threshold;\n this.windowMs = windowMs;\n this.cooldownMs = cooldownMs;\n }\n\n reconfigure(opts: { threshold?: number; windowMs?: number; cooldownMs?: number }): void {\n if (opts.threshold !== undefined) this.threshold = opts.threshold;\n if (opts.windowMs !== undefined) this.windowMs = opts.windowMs;\n if (opts.cooldownMs !== undefined) this.cooldownMs = opts.cooldownMs;\n }\n\n getState(): BreakerState {\n return this.state;\n }\n\n /** If open and cooldown has elapsed, transition to half_open. Returns the\n * current state AFTER any transition. */\n maybeHalfOpen(): BreakerState {\n if (this.state === \"open\") {\n const elapsed = Date.now() - this.openedAt;\n if (elapsed >= this.cooldownMs) {\n this.state = \"half_open\";\n }\n }\n return this.state;\n }\n\n record429(type: \"concurrency\" | \"rate_limit\" | \"gateway\"): void {\n if (type !== \"concurrency\") return;\n const now = Date.now();\n this.concurrency429s.push(now);\n this.concurrency429s = this.concurrency429s.filter((t) => now - t < this.windowMs);\n if (this.concurrency429s.length >= this.threshold) {\n if (this.state === \"closed\" || this.state === \"half_open\") {\n this.state = \"open\";\n this.openedAt = now;\n }\n }\n }\n\n recordSuccess(): void {\n if (this.state === \"half_open\") {\n this.state = \"closed\";\n this.concurrency429s = [];\n }\n }\n}\n","// Concurrency gate — resizable semaphore with circuit breaker + release cooldown.\n// 9th request at hard cap enqueues; waits for a slot or rejects on timeout/full.\n//\n// Internal accounting uses 1000× scaled integers (SCALE) to avoid floating-point\n// drift from fractional reservation ratios and weights. All public getters\n// return decimal values.\n//\n// Internally composed of two cohesive classes:\n// Semaphore — active count, acquire/release, waiter queue, FIFO ordering,\n// reservations, capacity checks, release cooldown.\n// CircuitBreaker — failure counting, state (closed/open/half-open), thresholds.\n// ConcurrencyGate preserves its existing public API and delegates to both.\n\nimport { createLogger } from \"../logger.js\";\nimport type { GateStats, UsageSnapshot } from \"../types.js\";\nimport { CircuitBreaker } from \"./circuit-breaker.js\";\nimport {\n type ConcurrencyGateOptions,\n GateError,\n type Permit,\n SCALE,\n type Waiter,\n} from \"./types.js\";\n\nconst log = createLogger(\"limiter\");\n\n// ---------------------------------------------------------------------------\n// Semaphore — active count, acquire/release, waiter queue, FIFO ordering,\n// reservations, capacity checks, release cooldown.\n// ---------------------------------------------------------------------------\n\nclass Semaphore {\n private active = 0;\n private limit = SCALE;\n private hardCap: number;\n private softLimit: number;\n private releaseCooldownMs: number;\n private maxQueueDepth: number;\n private queueTimeoutMs: number;\n private waiters: Waiter[] = [];\n private reservations: Record<string, number>;\n private activeByIntention: Record<string, number>;\n private queuedByIntention: Record<string, number>;\n private cooldownTimers = new Set<ReturnType<typeof setTimeout>>();\n private pendingReleases: Array<{ weight: number; intention: string }> = [];\n private releaseTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly onEmitStats: () => void;\n\n constructor(opts: ConcurrencyGateOptions, onEmitStats: () => void) {\n this.onEmitStats = onEmitStats;\n this.hardCap = Math.max(1, Math.floor(opts.hardCap)) * SCALE;\n this.softLimit = Math.max(1, Math.floor(opts.softLimit)) * SCALE;\n this.limit = Math.max(SCALE, Math.min(this.softLimit, this.hardCap));\n this.releaseCooldownMs = opts.releaseCooldownMs;\n this.maxQueueDepth = opts.maxQueueDepth;\n this.queueTimeoutMs = opts.queueTimeoutMs;\n const src = opts.intentions ?? { main: 1 };\n this.reservations = {};\n for (const key of Object.keys(src)) {\n this.reservations[key] = Math.round(src[key] * SCALE);\n }\n this.activeByIntention = {};\n this.queuedByIntention = {};\n for (const key of Object.keys(this.reservations)) {\n this.activeByIntention[key] = 0;\n this.queuedByIntention[key] = 0;\n }\n this.assertInvariants();\n }\n\n reconfigure(opts: Partial<ConcurrencyGateOptions>): void {\n if (opts.releaseCooldownMs !== undefined) this.releaseCooldownMs = opts.releaseCooldownMs;\n if (opts.maxQueueDepth !== undefined) this.maxQueueDepth = opts.maxQueueDepth;\n if (opts.queueTimeoutMs !== undefined) this.queueTimeoutMs = opts.queueTimeoutMs;\n if (opts.intentions !== undefined) {\n this.reservations = {};\n for (const key of Object.keys(opts.intentions)) {\n this.reservations[key] = Math.round(opts.intentions[key] * SCALE);\n }\n for (const key of Object.keys(this.reservations)) {\n if (this.activeByIntention[key] === undefined) this.activeByIntention[key] = 0;\n if (this.queuedByIntention[key] === undefined) this.queuedByIntention[key] = 0;\n }\n }\n this.assertInvariants();\n }\n\n getActive(): number {\n return this.active;\n }\n\n getLimit(): number {\n return this.limit;\n }\n\n getHardCap(): number {\n return this.hardCap;\n }\n\n getSoftLimit(): number {\n return this.softLimit;\n }\n\n getWaiterCount(): number {\n return this.waiters.length;\n }\n\n getReservations(): Record<string, number> {\n return this.reservations;\n }\n\n getActiveByIntention(): Record<string, number> {\n return this.activeByIntention;\n }\n\n getQueuedByIntention(): Record<string, number> {\n return this.queuedByIntention;\n }\n\n getIntentionActive(intention: string): number {\n return (this.activeByIntention[intention] ?? 0) / SCALE;\n }\n\n getIntentionQueued(intention: string): number {\n return this.queuedByIntention[intention] ?? 0;\n }\n\n resize(newLimit: number): boolean {\n const scaled = Math.round(newLimit * SCALE);\n const clamped = Math.max(SCALE, Math.min(scaled, this.hardCap));\n if (clamped === this.limit) return false;\n this.limit = clamped;\n this.drainWaiters();\n this.assertInvariants();\n return true;\n }\n\n setHardCap(newHardCap: number): boolean {\n const cap = Math.max(1, Math.floor(newHardCap)) * SCALE;\n if (cap === this.hardCap) return false;\n this.hardCap = cap;\n if (this.softLimit > cap) this.softLimit = cap;\n if (this.limit > cap) {\n this.limit = cap;\n this.drainWaiters();\n }\n if (this.active > cap) {\n const excess = (this.active - cap) / SCALE;\n log.warn(\n `[gate.ts] setHardCap(${newHardCap}) reduced cap below active=${this.active / SCALE}; ${excess} permits remain in-flight until completion (transient over-cap — see setHardCap docs)`,\n );\n }\n this.assertInvariants();\n return true;\n }\n\n setSoftLimit(newSoftLimit: number): boolean {\n const v = Math.max(1, Math.floor(newSoftLimit)) * SCALE;\n if (v === this.softLimit) return false;\n this.softLimit = v;\n this.assertInvariants();\n return true;\n }\n\n /** Attempts to acquire a permit. Returns true if granted immediately,\n * false if the caller should enqueue (or reject if queue is full).\n *\n * When total reservations exceed the limit (over-subscribed), the\n * proportional split in effectiveReservations() may reduce each\n * intention's reservation below a single permit's weight. In that\n * case, fall back to a pure capacity check: any intention may acquire\n * if active + weight ≤ limit. Reservations only gate borrowing when\n * they are not over-subscribed. */\n canGrant(intention: string, weight: number): boolean {\n const effRes = this.effectiveReservations();\n const reserved = effRes[intention] ?? 0;\n if (this.active + weight > this.limit) return false;\n if ((this.activeByIntention[intention] ?? 0) + weight <= reserved) {\n return true;\n }\n // When reservations are over-subscribed (proportional split reduced\n // them below raw values), allow pure capacity-based granting.\n const overSubscribed = Object.keys(this.reservations).some(\n (k) => (this.reservations[k] ?? 0) > (effRes[k] ?? 0),\n );\n if (overSubscribed) {\n return this.active + weight <= this.limit;\n }\n return this.active + weight <= this.capacity(intention, effRes);\n }\n\n grant(\n resolve: (p: Permit) => void,\n onAcquire: (() => void) | undefined,\n weight: number,\n intention: string,\n ): void {\n this.active += weight;\n this.activeByIntention[intention] = (this.activeByIntention[intention] ?? 0) + weight;\n onAcquire?.();\n this.assertInvariants();\n this.onEmitStats();\n let released = false;\n resolve({\n release: () => {\n if (released) return;\n released = true;\n this.releasePermit(weight, intention);\n },\n });\n }\n\n /** Enqueue a waiter. Returns true if enqueued, false if queue is full. */\n enqueue(waiter: Waiter): boolean {\n if (this.waiters.length >= this.maxQueueDepth) return false;\n this.waiters.push(waiter);\n this.queuedByIntention[waiter.intention] = (this.queuedByIntention[waiter.intention] ?? 0) + 1;\n return true;\n }\n\n removeWaiter(w: Waiter): void {\n const i = this.waiters.indexOf(w);\n if (i >= 0) {\n this.waiters.splice(i, 1);\n this.queuedByIntention[w.intention] = Math.max(\n 0,\n (this.queuedByIntention[w.intention] ?? 0) - 1,\n );\n this.assertInvariants();\n this.onEmitStats();\n }\n }\n\n drainWaiters(): void {\n if (this.waiters.length === 0) return;\n const remaining: Waiter[] = [];\n for (const next of this.waiters) {\n if (!this.canGrant(next.intention, next.weight)) {\n remaining.push(next);\n continue;\n }\n this.queuedByIntention[next.intention] = Math.max(\n 0,\n (this.queuedByIntention[next.intention] ?? 0) - 1,\n );\n clearTimeout(next.timeout);\n this.grant(next.resolve, next.onAcquire, next.weight, next.intention);\n }\n this.waiters = remaining;\n this.assertInvariants();\n }\n\n getQueueTimeoutMs(): number {\n return this.queueTimeoutMs;\n }\n\n shutdown(): void {\n for (const t of this.cooldownTimers) clearTimeout(t);\n this.cooldownTimers.clear();\n this.releaseTimer = null;\n this.pendingReleases = [];\n for (const w of this.waiters) {\n clearTimeout(w.timeout);\n this.queuedByIntention[w.intention] = Math.max(\n 0,\n (this.queuedByIntention[w.intention] ?? 0) - 1,\n );\n w.reject(new GateError(\"shutdown\", \"Shutting down\"));\n }\n this.waiters = [];\n this.assertInvariants();\n }\n\n private releasePermit(weight: number, intention: string): void {\n this.pendingReleases.push({ weight, intention });\n if (this.releaseTimer === null) {\n this.releaseTimer = setTimeout(() => {\n this.releaseTimer = null;\n const batch = this.pendingReleases;\n this.pendingReleases = [];\n for (const rel of batch) {\n this.active = Math.max(0, this.active - rel.weight);\n this.activeByIntention[rel.intention] = Math.max(\n 0,\n (this.activeByIntention[rel.intention] ?? 0) - rel.weight,\n );\n }\n this.drainWaiters();\n this.assertInvariants();\n this.onEmitStats();\n }, this.releaseCooldownMs);\n this.cooldownTimers.add(this.releaseTimer);\n }\n }\n\n /** Effective reservations clamped so sum(effRes) <= limit. Each is at least 1\n * when limit allows, but if limit < intention count, some may be 0. */\n private effectiveReservations(): Record<string, number> {\n const keys = Object.keys(this.reservations);\n const sum = keys.reduce((s, k) => s + (this.reservations[k] ?? 0), 0);\n if (sum <= this.limit) {\n const out: Record<string, number> = {};\n for (const k of keys) out[k] = this.reservations[k] ?? 0;\n return out;\n }\n const out: Record<string, number> = {};\n let remaining = this.limit;\n for (const k of keys) {\n const scaled = Math.floor(((this.reservations[k] ?? 0) / sum) * this.limit);\n const v = Math.max(0, Math.min(scaled, remaining));\n out[k] = v;\n remaining -= v;\n }\n return out;\n }\n\n /** Capacity available to intention I: limit minus reserved slots of other\n * intentions that have not yet been filled by active permits. */\n private capacity(intention: string, effRes: Record<string, number>): number {\n let reserved = 0;\n for (const k of Object.keys(effRes)) {\n if (k === intention) continue;\n const active = this.activeByIntention[k] ?? 0;\n const res = effRes[k] ?? 0;\n reserved += Math.max(0, res - active);\n }\n return this.limit - reserved;\n }\n\n private assertInvariants(): void {\n if (!(SCALE <= this.limit && this.limit <= this.hardCap)) {\n log.warn(\n `[gate.ts] invariant violation: SCALE(${SCALE}) <= limit(${this.limit}) <= hardCap(${this.hardCap})`,\n );\n }\n if (!Number.isInteger(this.active)) {\n log.warn(`[gate.ts] invariant warning: active(${this.active}) is not an integer`);\n }\n if (!Number.isInteger(this.limit)) {\n log.warn(`[gate.ts] invariant warning: limit(${this.limit}) is not an integer`);\n }\n if (!Number.isInteger(this.hardCap)) {\n log.warn(`[gate.ts] invariant warning: hardCap(${this.hardCap}) is not an integer`);\n }\n if (this.active > this.hardCap) {\n const excess = (this.active - this.hardCap) / SCALE;\n log.warn(\n `[gate.ts] invariant warning: active(${this.active}) > hardCap(${this.hardCap}); transient over-cap by ${excess} (in-flight permits will drain)`,\n );\n }\n if (this.active < 0) {\n log.warn(`[gate.ts] invariant violation: active(${this.active}) < 0`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// ConcurrencyGate — public API, composes Semaphore + CircuitBreaker.\n// ---------------------------------------------------------------------------\n\nexport class ConcurrencyGate {\n private readonly semaphore: Semaphore;\n private readonly breaker: CircuitBreaker;\n private onStatsCb: (() => void) | null = null;\n\n constructor(opts: ConcurrencyGateOptions) {\n this.breaker = new CircuitBreaker(\n opts.breakerThreshold,\n opts.breakerWindowMs,\n opts.breakerCooldownMs,\n );\n this.semaphore = new Semaphore(opts, () => this.emitStats());\n }\n\n /** Hot-reload reconfigurable parameters. Does NOT affect the soft limit\n * (which is driven by usage.onChange). Updates breaker/queue/cooldown. */\n reconfigure(opts: Partial<ConcurrencyGateOptions>): void {\n this.semaphore.reconfigure(opts);\n this.breaker.reconfigure({\n threshold: opts.breakerThreshold,\n windowMs: opts.breakerWindowMs,\n cooldownMs: opts.breakerCooldownMs,\n });\n this.emitStats();\n }\n\n onStatsChange(cb: () => void): void {\n this.onStatsCb = cb;\n }\n\n getLimit(): number {\n return this.semaphore.getLimit() / SCALE;\n }\n\n getIntentionActive(intention: string): number {\n return this.semaphore.getIntentionActive(intention);\n }\n\n getIntentionQueued(intention: string): number {\n return this.semaphore.getIntentionQueued(intention);\n }\n\n resize(newLimit: number): void {\n if (this.semaphore.resize(newLimit)) {\n this.emitStats();\n }\n }\n\n /** Update the hard cap (e.g. from /v1/usage reconciliation).\n * Clamps the soft limit down if it now exceeds the new hard cap.\n *\n * Policy: `hard_cap` is a hard grant boundary, NOT a hard real-time ceiling.\n * Lowering the cap does NOT evict active permits. Transient over-cap states\n * are expected until in-flight permits complete naturally. */\n setHardCap(newHardCap: number): void {\n if (this.semaphore.setHardCap(newHardCap)) {\n this.emitStats();\n }\n }\n\n /** Update the persisted soft limit (from /v1/usage). Does NOT change the\n * effective limit — that's driven by usage.onChange with priorityLow adjustment. */\n setSoftLimit(newSoftLimit: number): void {\n if (this.semaphore.setSoftLimit(newSoftLimit)) {\n this.emitStats();\n }\n }\n\n acquire(\n opts: {\n weight?: number;\n signal?: AbortSignal;\n onAcquire?: () => void;\n intention?: string;\n } = {},\n ): Promise<Permit> {\n const weight = Math.round((opts.weight ?? 1) * SCALE);\n if (weight <= 0) {\n throw new GateError(\"invalid_weight\", \"weight must be positive\");\n }\n const intention = opts.intention ?? \"main\";\n return new Promise((resolve, reject) => {\n // Circuit breaker check\n if (this.breaker.maybeHalfOpen() === \"open\") {\n reject(new GateError(\"circuit_open\", \"Circuit breaker open\"));\n return;\n }\n\n if (this.semaphore.canGrant(intention, weight)) {\n this.semaphore.grant(resolve, opts.onAcquire, weight, intention);\n return;\n }\n\n // Enqueue\n const queueTimeoutMs = this.semaphore.getQueueTimeoutMs();\n const timeout = setTimeout(() => {\n this.semaphore.removeWaiter(waiter);\n reject(new GateError(\"timeout\", \"Queue timeout exceeded\"));\n }, queueTimeoutMs);\n\n const waiter: Waiter = {\n resolve,\n reject,\n enqueuedAt: Date.now(),\n weight,\n intention,\n signal: opts.signal,\n onAcquire: opts.onAcquire,\n timeout,\n };\n\n if (!this.semaphore.enqueue(waiter)) {\n clearTimeout(timeout);\n reject(new GateError(\"queue_full\", \"Concurrency queue full\"));\n return;\n }\n\n this.emitStats();\n\n opts.signal?.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timeout);\n this.semaphore.removeWaiter(waiter);\n reject(new GateError(\"aborted\", \"Client disconnected while enqueued\"));\n },\n { once: true },\n );\n });\n }\n\n record429(type: \"concurrency\" | \"rate_limit\" | \"gateway\"): void {\n this.breaker.record429(type);\n if (this.breaker.getState() === \"open\") {\n this.emitStats();\n }\n }\n\n recordSuccess(): void {\n const wasHalfOpen = this.breaker.getState() === \"half_open\";\n this.breaker.recordSuccess();\n if (wasHalfOpen) {\n this.emitStats();\n }\n }\n\n getStats(snapshot: UsageSnapshot): GateStats {\n const now = Date.now();\n const boxed = snapshot.boxedUntil !== null && snapshot.boxedUntil > now;\n const activeByIntention: Record<string, number> = {};\n const semActive = this.semaphore.getActiveByIntention();\n for (const k of Object.keys(semActive)) {\n activeByIntention[k] = semActive[k] / SCALE;\n }\n const reservations: Record<string, number> = {};\n const semRes = this.semaphore.getReservations();\n for (const k of Object.keys(semRes)) {\n reservations[k] = semRes[k] / SCALE;\n }\n return {\n active: this.semaphore.getActive() / SCALE,\n queued: this.semaphore.getWaiterCount(),\n softLimit: this.semaphore.getSoftLimit() / SCALE,\n hardCap: this.semaphore.getHardCap() / SCALE,\n tier: snapshot.plan,\n breaker: this.breaker.getState(),\n boxed,\n boxedReason: snapshot.boxedReason,\n boxedUntil: snapshot.boxedUntil,\n priorityLow: snapshot.priorityLow,\n unitsDemoted: snapshot.unitsDemoted,\n demotedUntil: snapshot.demotedUntil,\n requestsRemaining: snapshot.requestsRemaining,\n requestsInWindow: snapshot.requestsInWindow,\n requestsLimit: snapshot.requestsLimit,\n windowSeconds: snapshot.requestsWindowSeconds,\n usageOk: snapshot.ok,\n lastUsageFetch: snapshot.fetchedAt || null,\n activeByIntention,\n queuedByIntention: { ...this.semaphore.getQueuedByIntention() },\n reservations,\n };\n }\n\n shutdown(): void {\n this.semaphore.shutdown();\n }\n\n private emitStats(): void {\n this.onStatsCb?.();\n }\n}\n","// Zero-dependency in-memory metrics registry.\n// Exposes counters and gauges in Prometheus text exposition format.\n// Designed for single-process Bun — no external collector required.\n\nexport type MetricType = \"counter\" | \"gauge\";\n\ninterface MetricEntry {\n type: MetricType;\n help: string;\n value: number;\n labels?: Record<string, string>;\n}\n\n/** In-memory metrics registry with Prometheus text format export. */\nexport class MetricsRegistry {\n private metrics = new Map<string, MetricEntry>();\n\n /** Increment a counter by n (default 1). Creates the metric if missing. */\n inc(name: string, n = 1, help?: string): void {\n const existing = this.metrics.get(name);\n if (existing) {\n existing.value += n;\n } else {\n this.metrics.set(name, {\n type: \"counter\",\n help: help ?? name,\n value: n,\n });\n }\n }\n\n /** Set a gauge to a specific value. Creates the metric if missing. */\n set(name: string, value: number, help?: string): void {\n const existing = this.metrics.get(name);\n if (existing) {\n existing.value = value;\n } else {\n this.metrics.set(name, {\n type: \"gauge\",\n help: help ?? name,\n value,\n });\n }\n }\n\n /** Get the current value of a metric. */\n get(name: string): number | undefined {\n return this.metrics.get(name)?.value;\n }\n\n /** Serialize all metrics to Prometheus text exposition format. */\n format(): string {\n const lines: string[] = [];\n const sorted = [...this.metrics.entries()].sort((a, b) => a[0].localeCompare(b[0]));\n for (const [name, entry] of sorted) {\n lines.push(`# HELP ${name} ${entry.help}`);\n lines.push(`# TYPE ${name} ${entry.type}`);\n lines.push(`${name} ${entry.value}`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n }\n\n /** Reset all counters (not gauges). Used for testing. */\n resetCounters(): void {\n for (const entry of this.metrics.values()) {\n if (entry.type === \"counter\") entry.value = 0;\n }\n }\n}\n\n/** Singleton registry for the process. */\nexport const metrics = new MetricsRegistry();\n","// Shared parser for the /v1/models/info upstream response.\n//\n// Both src/models.ts (rich ModelInfo) and src/vision/catalog.ts (subset\n// ModelInfo) consume the same JSON shape — a top-level object whose keys\n// are model ids and whose values carry capabilities/base_model metadata.\n// This module does the type-guarded extraction once into a normalized\n// ParsedModelInfo; each caller projects to its own interface.\n\n/** Tristate vision support as encoded by the upstream API. */\nexport type VisionSupport = boolean | \"via-handoff\";\n\n/**\n * Faithfully typed fields extracted from a single /v1/models/info entry.\n * Every field is populated; callers pick the subset they need.\n */\nexport interface ParsedModelInfo {\n name: string;\n display_name: string;\n description: string;\n base_model: {\n name: string;\n provider: string | undefined;\n family: string | undefined;\n oss_base: string | undefined;\n };\n capabilities: {\n max_completion_tokens: number;\n recommended_max_tokens: number;\n context_window: number;\n supports_vision: VisionSupport;\n supports_tools: boolean;\n reasoning: {\n supported: boolean;\n can_disable: boolean;\n levels: string[];\n default_level: string | null;\n };\n };\n benchmarks: Record<string, unknown>;\n weights: {\n precision: string | undefined;\n hf_url: string | undefined;\n };\n stage: string | undefined;\n lifecycle: { playground_start_date: string | undefined } | undefined;\n}\n\n/** Untyped shape of a single entry as received from upstream. */\ninterface RawModelInfo {\n name?: unknown;\n display_name?: unknown;\n description?: unknown;\n base_model?: {\n name?: unknown;\n provider?: unknown;\n family?: unknown;\n oss_base?: unknown;\n };\n capabilities?: {\n max_completion_tokens?: unknown;\n recommended_max_tokens?: unknown;\n context_window?: unknown;\n supports_vision?: unknown;\n supports_tools?: unknown;\n reasoning?: {\n supported?: unknown;\n can_disable?: unknown;\n levels?: unknown;\n default_level?: unknown;\n };\n };\n benchmarks?: Record<string, unknown>;\n weights?: {\n precision?: unknown;\n hf_url?: unknown;\n };\n stage?: unknown;\n lifecycle?: { playground_start_date?: unknown };\n}\n\n/**\n * Parse a raw /v1/models/info JSON body into a map keyed by model id.\n *\n * Entries whose value is not an object are skipped (matching prior behavior\n * in both src/models.ts and src/vision/catalog.ts). Every field is\n * type-guarded; missing or wrong-typed fields fall back to defaults\n * identical to the inline code this replaces.\n */\nexport function parseModelInfoResponse(body: unknown): Map<string, ParsedModelInfo> {\n const out = new Map<string, ParsedModelInfo>();\n if (typeof body !== \"object\" || body === null) return out;\n\n for (const [key, rawVal] of Object.entries(body as Record<string, unknown>)) {\n if (typeof rawVal !== \"object\" || rawVal === null) continue;\n const v = rawVal as RawModelInfo;\n const caps = v.capabilities ?? {};\n const bm = v.base_model ?? {};\n const w = v.weights ?? {};\n const sv = caps.supports_vision;\n const supportsVision: VisionSupport =\n sv === true ? true : sv === \"via-handoff\" ? \"via-handoff\" : false;\n const reasoning = caps.reasoning ?? {};\n const levels = Array.isArray(reasoning.levels)\n ? reasoning.levels.filter((l) => typeof l === \"string\")\n : [];\n out.set(key, {\n name: typeof v.name === \"string\" ? v.name : key,\n display_name: typeof v.display_name === \"string\" ? v.display_name : \"\",\n description: typeof v.description === \"string\" ? v.description : \"\",\n base_model: {\n name: typeof bm.name === \"string\" ? bm.name : \"\",\n provider: typeof bm.provider === \"string\" ? bm.provider : undefined,\n family: typeof bm.family === \"string\" ? bm.family : undefined,\n oss_base: typeof bm.oss_base === \"string\" ? bm.oss_base : undefined,\n },\n capabilities: {\n max_completion_tokens:\n typeof caps.max_completion_tokens === \"number\" ? caps.max_completion_tokens : 0,\n recommended_max_tokens:\n typeof caps.recommended_max_tokens === \"number\" ? caps.recommended_max_tokens : 0,\n context_window: typeof caps.context_window === \"number\" ? caps.context_window : 0,\n supports_vision: supportsVision,\n supports_tools: caps.supports_tools === true,\n reasoning: {\n supported: reasoning.supported === true,\n can_disable: reasoning.can_disable === true,\n levels,\n default_level:\n typeof reasoning.default_level === \"string\" ? reasoning.default_level : null,\n },\n },\n benchmarks: typeof v.benchmarks === \"object\" && v.benchmarks !== null ? v.benchmarks : {},\n weights: {\n precision: typeof w.precision === \"string\" ? w.precision : undefined,\n hf_url: typeof w.hf_url === \"string\" ? w.hf_url : undefined,\n },\n stage: typeof v.stage === \"string\" ? v.stage : undefined,\n lifecycle: v.lifecycle\n ? {\n playground_start_date:\n typeof v.lifecycle.playground_start_date === \"string\"\n ? v.lifecycle.playground_start_date\n : undefined,\n }\n : undefined,\n });\n }\n return out;\n}\n","// Shared model-info fetch utility.\n//\n// Both src/models.ts (ModelsClient) and src/vision/catalog.ts\n// (ModelInfoClient) fetch the /v1/models/info endpoint with the same\n// pattern: add a Bearer Authorization header, GET the JSON body, and\n// parse it via parseModelInfoResponse into a map keyed by model id.\n// This module extracts that shared fetch+parse pipeline so both\n// consumers share one implementation.\n\nimport { parseModelInfoResponse } from \"../model-info-parser.js\";\n\nexport type { ParsedModelInfo } from \"../model-info-parser.js\";\nimport type { ParsedModelInfo } from \"../model-info-parser.js\";\n\n/** Timeout for the upstream fetch (ms). */\nconst FETCH_TIMEOUT_MS = 15000;\n\n/**\n * Fetch /v1/models/info from `target` and return a map keyed by model id.\n *\n * Adds a `Bearer <apiKey>` Authorization header when `apiKey` is non-empty.\n * Throws on any failure — non-ok HTTP status, network error, or parse\n * error — so callers can preserve their own error-handling semantics.\n */\nexport async function fetchModelsInfo(\n target: string,\n apiKey: string | undefined,\n signal?: AbortSignal,\n): Promise<Map<string, ParsedModelInfo>> {\n const headers: Record<string, string> = {};\n if (apiKey) headers.authorization = `Bearer ${apiKey}`;\n\n const resp = await fetch(target, {\n method: \"GET\",\n headers,\n signal: signal ?? AbortSignal.timeout(FETCH_TIMEOUT_MS),\n });\n if (!resp.ok) {\n throw new Error(`HTTP ${resp.status} from ${target}`);\n }\n const parsed = await resp.json();\n return parseModelInfoResponse(parsed);\n}\n","// Upstream /v1/models client — fetches the model list and derives per-model\n// concurrency weights. Models with output pricing below the cheap-threshold\n// (default 2.0) get a reduced weight (default 0.5); all others default to 1.0.\n\nimport { createLogger } from \"./logger.js\";\nimport type { ParsedModelInfo, VisionSupport } from \"./model-info-parser.js\";\nimport { fetchModelsInfo } from \"./models/fetch-info.js\";\nimport type { VisionLookup, VisionTristate } from \"./vision/detect.js\";\n\nconst log = createLogger(\"models\");\n\nconst DEFAULT_MODELS_PATH = \"/v1/models\";\nconst DEFAULT_MODELS_INFO_PATH = \"/v1/models/info\";\n/** Pricing output threshold below which a model is considered \"cheap\". */\nconst CHEAP_OUTPUT_THRESHOLD = 2;\n/** Weight assigned to cheap models (output pricing < threshold). */\nconst CHEAP_MODEL_WEIGHT = 0.5;\n/** Default weight for models without cheap pricing. */\nconst DEFAULT_MODEL_WEIGHT = 1;\n\n/** Rich model info from /v1/models/info — faithfully typed from the upstream API. */\ninterface ModelInfo {\n name: string;\n display_name: string;\n description: string;\n base_model: {\n name: string;\n provider?: string;\n family?: string;\n oss_base?: string;\n };\n capabilities: {\n max_completion_tokens: number;\n recommended_max_tokens: number;\n context_window: number;\n supports_vision: VisionSupport;\n supports_tools: boolean;\n reasoning: {\n supported: boolean;\n can_disable: boolean;\n levels: string[];\n default_level: string | null;\n };\n };\n benchmarks: Record<string, unknown>;\n weights: {\n precision: string | undefined;\n hf_url: string | undefined;\n };\n stage?: string;\n lifecycle?: {\n playground_start_date?: string;\n };\n}\n\nexport interface ModelEntry {\n id: string;\n context_length: number;\n pricing: { input: number; output: number } | null;\n weight: number;\n info: ModelInfo | null;\n}\n\ninterface RawModel {\n id?: unknown;\n context_length?: unknown;\n pricing?: { input?: unknown; output?: unknown } | null;\n}\n\ninterface RawModelsResponse {\n data?: RawModel[];\n}\n\nexport interface ModelsClientOptions {\n target: string;\n apiKey?: string | null;\n refreshMs: number;\n /** Path appended to target for the model list. */\n path?: string;\n /** Path appended to target for the model info endpoint. */\n infoPath?: string;\n}\n\n/**\n * Fetches /v1/models on a timer, derives weights, and exposes synchronous\n * lookups. Failures are best-effort: last-known-good is served with ok=false.\n */\nexport class ModelsClient implements VisionLookup {\n private entries: Map<string, ModelEntry> = new Map();\n private fetchedAt = 0;\n private ok = false;\n private timer: ReturnType<typeof setInterval> | null = null;\n private fetching: Promise<boolean> | null = null;\n private onChangeCb: (() => void) | null = null;\n private readonly target: string;\n private readonly apiKey: string | null;\n private readonly refreshMs: number;\n private readonly path: string;\n private readonly infoPath: string;\n\n constructor(opts: ModelsClientOptions) {\n this.target = opts.target.replace(/\\/+$/, \"\");\n this.apiKey = opts.apiKey ?? null;\n this.refreshMs = opts.refreshMs;\n this.path = opts.path ?? DEFAULT_MODELS_PATH;\n this.infoPath = opts.infoPath ?? DEFAULT_MODELS_INFO_PATH;\n }\n\n start(): void {\n if (this.timer) return;\n void this.refresh().catch(() => {});\n this.timer = setInterval(() => {\n void this.refresh().catch(() => {});\n }, this.refreshMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Subscribe to catalog updates (fired after each successful or failed refresh). */\n onChange(cb: () => void): void {\n this.onChangeCb = cb;\n }\n\n /** True if the catalog has been successfully fetched at least once. */\n isReady(): boolean {\n return this.fetchedAt > 0;\n }\n\n /** Timestamp (ms) of the last successful fetch, or 0 if never. */\n lastFetchedAt(): number {\n return this.fetchedAt;\n }\n\n /** Current fetch health. */\n healthy(): boolean {\n return this.ok;\n }\n\n /**\n * Look up the derived weight for a model id.\n * Returns DEFAULT_MODEL_WEIGHT if the model is unknown or the catalog\n * has not been fetched yet.\n */\n getWeight(modelId: string): number {\n const entry = this.entries.get(modelId);\n return entry?.weight ?? DEFAULT_MODEL_WEIGHT;\n }\n\n getVisionSupport(modelName: string): VisionTristate | null {\n const entry = this.entries.get(modelName);\n if (!entry?.info) return null;\n return entry.info.capabilities.supports_vision;\n }\n\n /** Get a model entry, or null if unknown. */\n get(modelId: string): ModelEntry | null {\n return this.entries.get(modelId) ?? null;\n }\n\n /** Snapshot of all known models (sorted by id). */\n list(): ModelEntry[] {\n return [...this.entries.values()].sort((a, b) => a.id.localeCompare(b.id));\n }\n\n /**\n * Fetch the model list from the upstream API.\n * Deduplicates concurrent calls. Returns true on success, false on failure.\n */\n async refresh(): Promise<boolean> {\n if (this.fetching) return this.fetching;\n this.fetching = this.doFetch();\n try {\n return await this.fetching;\n } finally {\n this.fetching = null;\n }\n }\n\n private async doFetch(): Promise<boolean> {\n const url = `${this.target}${this.path}`;\n const infoUrl = `${this.target}${this.infoPath}`;\n const headers: Record<string, string> = {};\n if (this.apiKey) headers.authorization = `Bearer ${this.apiKey}`;\n const infoPromise = fetchModelsInfo(infoUrl, this.apiKey ?? undefined).then(\n (m) => m,\n (e: unknown) => {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.startsWith(\"HTTP \")) {\n log.warn(`info fetch failed: ${msg} from ${infoUrl}`);\n } else {\n log.warn(`info fetch parse failed: ${msg}`);\n }\n return null;\n },\n );\n try {\n const resp = await fetch(url, { method: \"GET\", headers, signal: AbortSignal.timeout(15000) });\n if (!resp.ok) {\n this.ok = false;\n log.error(`fetch failed: HTTP ${resp.status} from ${url}`);\n this.onChangeCb?.();\n return false;\n }\n const parsed = (await resp.json()) as RawModelsResponse;\n const data = parsed.data;\n if (!Array.isArray(data)) {\n this.ok = false;\n log.error(\"fetch failed: response has no data[] array\");\n this.onChangeCb?.();\n return false;\n }\n\n let infoMap: Map<string, ParsedModelInfo> | null = null;\n const infoResult = await infoPromise;\n if (infoResult) {\n infoMap = infoResult;\n }\n\n const next = new Map<string, ModelEntry>();\n for (const raw of data) {\n const id = typeof raw.id === \"string\" ? raw.id : null;\n if (!id) continue;\n const pricing =\n raw.pricing && typeof raw.pricing === \"object\"\n ? {\n input: typeof raw.pricing.input === \"number\" ? raw.pricing.input : 0,\n output: typeof raw.pricing.output === \"number\" ? raw.pricing.output : 0,\n }\n : null;\n const weight =\n pricing && pricing.output < CHEAP_OUTPUT_THRESHOLD\n ? CHEAP_MODEL_WEIGHT\n : DEFAULT_MODEL_WEIGHT;\n next.set(id, {\n id,\n context_length: typeof raw.context_length === \"number\" ? raw.context_length : 0,\n pricing,\n weight,\n info: infoMap?.get(id) ?? null,\n });\n }\n\n this.entries = next;\n this.fetchedAt = Date.now();\n this.ok = true;\n const cheap = [...next.values()].filter((m) => m.weight < DEFAULT_MODEL_WEIGHT).length;\n const withInfo = [...next.values()].filter((m) => m.info !== null).length;\n log.info(\n `fetched ${next.size} models from ${url} — cheap (weight<1): ${cheap}, with info: ${withInfo}`,\n );\n this.onChangeCb?.();\n return true;\n } catch (err) {\n this.ok = false;\n log.error(`fetch error: ${err instanceof Error ? err.message : String(err)}`);\n this.onChangeCb?.();\n return false;\n }\n }\n}\n","// Proxy handler — captures request/response, stamps TTL, forwards upstream.\n// Tee's response stream so the client gets data immediately while capture\n// accumulates in the background.\n\nimport type { CaptureDB } from \"./db.js\";\nimport {\n HOP,\n classify429,\n computeRequestWeight,\n decodeText,\n headersToObject,\n newSummary,\n redactHeaders,\n textDecoder,\n textEncoder,\n} from \"./helpers.js\";\nimport type { GateError } from \"./limiter/index.js\";\nimport type { ConcurrencyGate } from \"./limiter/index.js\";\nimport { createLogger } from \"./logger.js\";\n\nimport { STAMP_ANTHROPIC_BETA_HEADER } from \"./config.js\";\nimport { extractModelName } from \"./models/name.js\";\nimport type { WriteQueue } from \"./queue.js\";\nimport type { SlidingWindowRateLimiter } from \"./rate.js\";\nimport {\n CacheTtlStep,\n STAMP_PIPELINE,\n type StampContext,\n parseJsonBody,\n} from \"./stamp-pipeline.js\";\nimport type {\n CaptureConfig,\n GateConfig,\n ProtocolConfig,\n RequestMeta,\n ResponseMeta,\n StampConfig,\n} from \"./types.js\";\nimport type { UsageMetrics } from \"./usage-extract.js\";\n\nconst log = createLogger(\"proxy\");\nimport type { ModelsClient } from \"./models.js\";\nimport { extractUsage } from \"./usage-extract.js\";\nimport type { VisionHandoff } from \"./vision/handoff.js\";\nimport type { WsBroadcaster } from \"./ws.js\";\n\n// ─── Stamp pipeline (table-driven dispatch) ────────────────────────────────\n\n/**\n * Re-stamp cache_control TTL on the post-vision body.\n * Only CacheTtlStep runs here — thinking/maxTokens/outputConfig/reasoning\n * are NOT re-applied after vision injection.\n */\nfunction stampPostVision(\n body: unknown,\n ctx: StampContext,\n reqBuf: Uint8Array,\n): { reqBuf: Uint8Array; changed: boolean } {\n if (!CacheTtlStep.applies(ctx) || body === null || typeof body !== \"object\") {\n return { reqBuf, changed: false };\n }\n const changed = CacheTtlStep.apply(body, ctx);\n if (!changed) return { reqBuf, changed: false };\n return { reqBuf: textEncoder.encode(JSON.stringify(body)), changed: true };\n}\n\n/** Create the proxy request handler. */\nexport type RateLimiterRef = { current: SlidingWindowRateLimiter | null };\n\nexport function createProxyHandler(\n db: CaptureDB,\n ws: WsBroadcaster,\n queue: WriteQueue,\n config: StampConfig & CaptureConfig & GateConfig & ProtocolConfig,\n gate: ConcurrencyGate,\n rateRef: RateLimiterRef,\n vision: VisionHandoff | null,\n models: ModelsClient,\n onTraffic?: () => void,\n) {\n async function handleProxy(req: Request, url: URL): Promise<Response> {\n onTraffic?.();\n const startedAt = Date.now();\n const path = url.pathname + url.search;\n const targetUrl = config.target + path;\n\n const reqHeadersRaw = headersToObject(req.headers);\n const isOpenAi = url.pathname.includes(config.openaiPath);\n\n // Claude Code stamp: ensure ?beta=true on /v1/messages requests.\n const stampBeta = config.stampClaudeCode && !isOpenAi && url.pathname === \"/v1/messages\";\n const targetUrlObj = new URL(targetUrl);\n const finalTargetUrl =\n stampBeta && targetUrlObj.searchParams.get(\"beta\") !== \"true\"\n ? `${targetUrl}${targetUrl.includes(\"?\") ? \"&\" : \"?\"}beta=true`\n : targetUrl;\n\n // --- Request body capture ---\n let reqBuf: Uint8Array | null = null;\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n reqBuf = new Uint8Array(await req.arrayBuffer());\n }\n\n // Early exit if client already disconnected after sending the body.\n if (req.signal.aborted) {\n return new Response(null, { status: 499 });\n }\n\n // --- Stamp pipeline (TTL, AnthropicBody, OpenAiReasoning, TopK) ---\n // Non-critical: stamping is optimization, not correctness. If it fails,\n // forward the original body unchanged.\n let body: unknown = null;\n if (reqBuf && reqBuf.byteLength > 0) {\n try {\n const parsed = parseJsonBody(reqBuf, reqHeadersRaw);\n body = parsed.body;\n } catch (err) {\n log.warn(\"stamp pipeline failed, forwarding original body\", {\n error: (err as Error).message,\n path: url.pathname,\n });\n }\n }\n\n const reqModelName = body ? (extractModelName(body) ?? null) : null;\n\n if (body && typeof body === \"object\" && body !== null) {\n try {\n const stampCtx: StampContext = {\n config,\n isOpenAi,\n headers: reqHeadersRaw,\n url,\n method: req.method,\n modelName: reqModelName ?? undefined,\n };\n let stampChanged = false;\n for (const step of STAMP_PIPELINE) {\n if (!step.applies(stampCtx)) continue;\n if (step.apply(body, stampCtx)) stampChanged = true;\n }\n if (stampChanged) {\n reqBuf = textEncoder.encode(JSON.stringify(body));\n }\n } catch (err) {\n log.warn(\"stamp pipeline failed, forwarding original body\", {\n error: (err as Error).message,\n path: url.pathname,\n });\n }\n }\n\n // --- Insert capture row (early, so vision calls can link to it) ---\n let reqBodyText = reqBuf ? decodeText(reqBuf) : \"\";\n const reqMeta: RequestMeta = {\n method: req.method,\n path,\n request_size: reqBuf ? reqBuf.byteLength : 0,\n started_at: startedAt,\n };\n const capId = db.startCapture({\n $method: req.method,\n $path: path,\n $url: finalTargetUrl,\n $rh: JSON.stringify(redactHeaders(reqHeadersRaw)),\n $rb: reqBodyText,\n $rs: reqBuf ? reqBuf.byteLength : 0,\n $st: startedAt,\n $state: \"enqueued\",\n $inp: config.incomingProtocol,\n $outp: config.upstreamProtocol,\n });\n ws.broadcast({\n type: \"new\",\n capture: newSummary(capId, reqMeta, config, \"enqueued\", reqModelName),\n });\n\n // --- Vision handoff (image → text description) ---\n // Non-critical: vision is an optional feature. If it fails, forward\n // the original body unchanged.\n if (reqBuf && reqBuf.byteLength > 0 && vision) {\n try {\n if (!body) {\n try {\n const ct = reqHeadersRaw[\"content-type\"] ?? \"\";\n if (ct.includes(\"json\") || reqBuf[0] === 0x7b) {\n body = JSON.parse(textDecoder.decode(reqBuf));\n }\n } catch {\n body = null;\n }\n }\n if (body) {\n const apiKind = isOpenAi ? \"openai\" : \"anthropic\";\n const modelName = reqModelName ?? undefined;\n const result = config.backgroundVision\n ? await vision.processBodyCacheOnly(body, apiKind, modelName, capId, req.signal)\n : await vision.processBody(body, apiKind, modelName, capId, req.signal);\n if (result.changed) {\n reqBuf = textEncoder.encode(JSON.stringify(result.body));\n const postVisionCtx: StampContext = {\n config,\n isOpenAi,\n headers: reqHeadersRaw,\n url,\n method: req.method,\n modelName: extractModelName(result.body),\n };\n const stamped = stampPostVision(result.body, postVisionCtx, reqBuf);\n if (stamped.changed) {\n reqBuf = stamped.reqBuf;\n log.info(`post-handoff stamped (claude_code=${config.stampClaudeCode})`, {\n method: req.method,\n path: url.pathname,\n });\n }\n reqBodyText = decodeText(reqBuf);\n db.updateRequestBody(capId, reqBodyText, reqBuf.byteLength);\n reqMeta.request_size = reqBuf.byteLength;\n ws.broadcast({\n type: \"update\",\n capture: newSummary(capId, reqMeta, config, \"enqueued\", reqModelName),\n });\n log.info(\n `vision handoff: ${result.stats.handoffCount} images, ${result.stats.cacheHits} hits, ${result.stats.visionCalls} calls, active=${vision.visionActive} queued=${vision.visionQueued}`,\n {\n captureId: capId,\n method: req.method,\n path: url.pathname,\n },\n );\n }\n }\n } catch (err) {\n log.warn(\"vision handoff failed, forwarding original body\", {\n error: (err as Error).message,\n captureId: capId,\n });\n }\n }\n\n const reqSize = reqBuf ? reqBuf.byteLength : 0;\n reqMeta.request_size = reqSize;\n\n // --- Forwarded request headers: strip hop-by-hop + host ---\n const fwdHeaders: Record<string, string> = {};\n for (const [k, v] of Object.entries(reqHeadersRaw)) {\n if (HOP.has(k)) continue;\n fwdHeaders[k] = v;\n }\n\n // The proxy strips Content-Encoding from the upstream response and forwards\n // decoded bodies, so it must not advertise compression. Force identity on\n // every upstream request to stay responsible for the encoding contract.\n fwdHeaders[\"accept-encoding\"] = \"identity\";\n\n if (stampBeta) {\n fwdHeaders[\"anthropic-beta\"] = STAMP_ANTHROPIC_BETA_HEADER;\n }\n\n // --- Weighted rate limit check (pro tier only; rate is null when unlimited) ---\n const modelName = reqModelName ?? undefined;\n const weight = computeRequestWeight(modelName, models);\n const rate = rateRef.current;\n if (rate) {\n const rc = rate.check(weight);\n if (!rc.allowed) {\n queue.queueUpdate(capId, reqMeta, {\n $status: 429,\n $rh: JSON.stringify({ error: \"rate_limit_exceeded\" }),\n $rb: JSON.stringify({ error: \"rate_limit_exceeded\", retry_after: rc.retryAfterSeconds }),\n $rs: 0,\n $ct: \"application/json\",\n $sse: 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"gate\",\n $gate_reason: `Rate limit exceeded — retry after ${rc.retryAfterSeconds}s`,\n });\n return new Response(\n JSON.stringify({\n error: \"rate_limit_exceeded\",\n retry_after: rc.retryAfterSeconds,\n }),\n {\n status: 429,\n headers: {\n \"content-type\": \"application/json\",\n \"retry-after\": String(rc.retryAfterSeconds),\n },\n },\n );\n }\n }\n\n // --- Concurrency gate: acquire a permit (blocks if at cap) ---\n let permit: { release: () => void } | null = null;\n try {\n permit = await gate.acquire({\n weight,\n signal: req.signal,\n onAcquire: () => {\n db.setState(capId, \"streaming\");\n ws.broadcast({ type: \"state\", captureId: capId, state: \"streaming\" });\n },\n });\n } catch (e) {\n const err = e as GateError;\n const aborted = err.code === \"aborted\";\n const status = aborted\n ? 499\n : err.code === \"circuit_open\" || err.code === \"queue_full\" || err.code === \"timeout\"\n ? 503\n : 502;\n queue.queueUpdate(capId, reqMeta, {\n $status: status,\n $rh: JSON.stringify({ error: err.code }),\n $rb: aborted ? \"\" : err.message,\n $rs: 0,\n $ct: \"application/json\",\n $sse: 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"gate\",\n $gate_reason: aborted\n ? \"Client disconnected while enqueued\"\n : err.code === \"circuit_open\"\n ? \"Circuit breaker open — upstream concurrency 429s exceeded threshold\"\n : err.code === \"queue_full\"\n ? \"Concurrency queue full — too many requests waiting for a slot\"\n : err.code === \"timeout\"\n ? \"Queue timeout — request waited too long for a concurrency slot\"\n : err.code === \"invalid_weight\"\n ? \"Invalid weight — model weight must be positive\"\n : err.message,\n });\n if (aborted) return new Response(null, { status: 499 });\n return new Response(JSON.stringify({ error: err.code, message: err.message }), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n\n // --- Forward upstream ---\n let upstream: Response;\n try {\n const upstreamSignal = AbortSignal.any([\n req.signal,\n AbortSignal.timeout(config.upstreamTimeoutMs),\n ]);\n upstream = await fetch(finalTargetUrl, {\n method: req.method,\n headers: fwdHeaders,\n body: reqBuf && reqBuf.byteLength > 0 ? (reqBuf as BodyInit) : undefined,\n protocol: config.upstreamProtocol as unknown as never,\n signal: upstreamSignal,\n });\n } catch (e) {\n const err = e as Error;\n const clientAborted = err.name === \"AbortError\" && req.signal.aborted;\n const upstreamTimedOut =\n err.name === \"TimeoutError\" || (err.name === \"AbortError\" && !req.signal.aborted);\n const status = clientAborted ? 499 : upstreamTimedOut ? 504 : 502;\n queue.queueUpdate(capId, reqMeta, {\n $status: status,\n $rh: JSON.stringify({\n error: clientAborted\n ? \"client_disconnected\"\n : upstreamTimedOut\n ? \"upstream_timeout\"\n : String(err),\n }),\n $rb: clientAborted ? \"\" : `Upstream error: ${err.message}`,\n $rs: 0,\n $ct: \"text/plain\",\n $sse: 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"gate\",\n $gate_reason: clientAborted\n ? \"Client disconnected during upstream request\"\n : upstreamTimedOut\n ? \"Upstream inactivity timeout (300s)\"\n : `Upstream unreachable — ${err.message}`,\n });\n permit?.release();\n if (clientAborted) return new Response(null, { status: 499 });\n if (upstreamTimedOut) return new Response(`Gateway Timeout: ${err.message}`, { status: 504 });\n return new Response(`Bad Gateway: ${err.message}`, { status: 502 });\n }\n\n // --- Classify 429: only concurrency-429s trip the breaker ---\n try {\n if (upstream.status === 429) {\n gate.record429(classify429(upstream));\n } else if (upstream.status < 400) {\n gate.recordSuccess();\n }\n\n const resHeadersRaw = headersToObject(upstream.headers);\n const resHeadersJson = JSON.stringify(resHeadersRaw);\n const contentType = upstream.headers.get(\"content-type\") ?? \"\";\n const isSSE = contentType.includes(\"text/event-stream\");\n\n // Forwarded response headers: strip content-encoding/length + hop-by-hop.\n const outHeaders: Record<string, string> = {};\n for (const [k, v] of Object.entries(resHeadersRaw)) {\n if (HOP.has(k) || k === \"content-encoding\") continue;\n outHeaders[k] = v;\n }\n\n const doneRes = (): Omit<ResponseMeta, \"$rb\" | \"$rs\"> => ({\n $status: upstream.status,\n $rh: resHeadersJson,\n $ct: contentType,\n $sse: isSSE ? 1 : 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"upstream\",\n $gate_reason: null,\n });\n\n if (!upstream.body) {\n queue.queueUpdate(capId, reqMeta, { ...doneRes(), $rb: \"\", $rs: 0 });\n permit?.release();\n return new Response(null, { status: upstream.status, headers: outHeaders });\n }\n\n // Stream response to client while incrementally decoding for capture.\n // Decodes each chunk with stream:true to handle multi-byte sequences\n // spanning chunk boundaries, avoiding the combine() double-copy.\n // `captureBodyMaxBytes` caps in-memory buffer growth (0 = unlimited);\n // the stream to the client is never truncated.\n const cap = config.captureBodyMaxBytes;\n const parts: string[] = [];\n const timedChunks: { text: string; time: number }[] = [];\n let totalSize = 0;\n let flushed = false;\n let firstChunkSent = false;\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n const flushCapture = (): void => {\n if (flushed) return;\n flushed = true;\n if (cap === 0 || totalSize <= cap) {\n try {\n const tail = decoder.decode();\n if (tail) parts.push(tail);\n } catch {\n // Tail had invalid bytes — ignore, already captured above.\n }\n }\n const fullBody = parts.join(\"\");\n const isStream = isSSE;\n let usage: UsageMetrics | null = null;\n let model: string | null = null;\n try {\n const result = extractUsage({\n provider: isOpenAi ? \"openai\" : \"anthropic\",\n streaming: isStream,\n requestBody: reqBodyText,\n responseBody: fullBody,\n durationMs: Date.now() - startedAt,\n requestStartedAt: startedAt,\n chunks: isStream ? timedChunks : undefined,\n });\n model = result.model;\n usage = result.metrics;\n } catch {\n usage = null;\n model = null;\n }\n try {\n queue.queueUpdate(capId, reqMeta, {\n ...doneRes(),\n $rb: fullBody,\n $rs: totalSize,\n $usage: usage,\n $model: model,\n });\n } catch {\n // Non-critical: capture persistence failure must not block permit release\n }\n permit?.release();\n };\n const capture = new TransformStream({\n transform(chunk: Uint8Array, controller) {\n totalSize += chunk.byteLength;\n const capturing = cap === 0 || totalSize <= cap;\n const now = Date.now();\n if (capturing) {\n let decoded = \"\";\n try {\n decoded = decoder.decode(chunk, { stream: true });\n parts.push(decoded);\n } catch {\n parts.push(Buffer.from(chunk).toString(\"base64\"));\n }\n if (decoded) timedChunks.push({ text: decoded, time: now });\n }\n if (!firstChunkSent) {\n firstChunkSent = true;\n try {\n ws.broadcast({\n type: \"update\",\n capture: {\n ...newSummary(capId, reqMeta, config, \"streaming\", reqModelName),\n ttft_ms: now - startedAt,\n },\n });\n } catch {\n // Non-critical: dashboard update failure must not error the stream\n }\n }\n controller.enqueue(chunk);\n },\n flush() {\n flushCapture();\n },\n });\n\n // Client disconnected mid-stream — flush capture so it doesn't stay \"streaming\".\n if (req.signal) {\n if (req.signal.aborted) {\n flushCapture();\n } else {\n req.signal.addEventListener(\n \"abort\",\n () => {\n flushCapture();\n },\n { once: true },\n );\n }\n }\n\n const stream = upstream.body.pipeThrough(capture);\n return new Response(stream, {\n status: upstream.status,\n statusText: upstream.statusText,\n headers: outHeaders,\n });\n } catch (err) {\n log.error(\"post-fetch processing failed\", {\n error: (err as Error).message,\n captureId: capId,\n });\n queue.queueUpdate(capId, reqMeta, {\n $status: 500,\n $rh: JSON.stringify({ error: \"internal_error\" }),\n $rb: `Internal error: ${(err as Error).message}`,\n $rs: 0,\n $ct: \"application/json\",\n $sse: 0,\n $dur: Date.now() - startedAt,\n $fin: Date.now(),\n $status_source: \"gate\",\n $gate_reason: `Internal proxy error — ${(err as Error).message}`,\n });\n permit?.release();\n return new Response(\n JSON.stringify({ error: \"internal_error\", message: (err as Error).message }),\n { status: 500, headers: { \"content-type\": \"application/json\" } },\n );\n }\n }\n\n return { handleProxy };\n}\n","// Stamp pipeline — table-driven dispatch for request body mutation steps.\n// Extracted from proxy.ts to keep the proxy handler focused on transport\n// (capture, streaming, forwarding) while the pipeline is independently\n// testable and extensible.\n//\n// Dependency direction: stamp-pipeline → stamp* / types / config / helpers.\n// Never imports proxy.ts (no cycles).\n\nimport {\n STAMP_CACHE_TTL_VALUE,\n STAMP_CONTEXT_MANAGEMENT_VALUE,\n STAMP_OUTPUT_CONFIG_VALUE,\n STAMP_TEMPERATURE_VALUE,\n STAMP_THINKING_VALUE,\n STAMP_TOP_K_VALUE,\n} from \"./config.js\";\nimport { textDecoder } from \"./helpers.js\";\nimport { createLogger } from \"./logger.js\";\nimport { isGlmModel, resolveEffortForModel } from \"./model-policy.js\";\nimport { stampReasoning } from \"./stamp-reasoning.js\";\nimport { stampTemperature } from \"./stamp-temperature.js\";\nimport { stampThinking } from \"./stamp-thinking.js\";\nimport { stampTopK } from \"./stamp-topk.js\";\nimport { stampCacheTtl } from \"./stamp.js\";\nimport type {\n AnthropicBody,\n CaptureConfig,\n GateConfig,\n OpenAiBody,\n ProtocolConfig,\n StampConfig,\n} from \"./types.js\";\n\nconst log = createLogger(\"stamp-pipeline\");\n\n// ─── Pipeline types ───────────────────────────────────────────────────────\n\n/** Resolved config subset available to every stamp step. */\ntype StampPipelineConfig = StampConfig & CaptureConfig & GateConfig & ProtocolConfig;\n\n/** Everything a stamp step needs to decide applicability and mutate the body. */\nexport interface StampContext {\n config: StampPipelineConfig;\n isOpenAi: boolean;\n headers: Record<string, string>;\n url: URL;\n method: string;\n modelName: string | undefined;\n}\n\n/** A single mutation step in the stamp pipeline. */\nexport interface StampStep {\n readonly label: string;\n applies(ctx: StampContext): boolean;\n apply(body: unknown, ctx: StampContext): boolean;\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\n/** Parse a request buffer as JSON using the content-type / first-byte heuristic. */\nexport function parseJsonBody(\n reqBuf: Uint8Array,\n headers: Record<string, string>,\n): { body: unknown; ok: boolean } {\n const ct = headers[\"content-type\"] ?? \"\";\n if (!ct.includes(\"json\") && reqBuf[0] !== 0x7b) return { body: null, ok: false };\n try {\n return { body: JSON.parse(textDecoder.decode(reqBuf)), ok: true };\n } catch {\n return { body: null, ok: false };\n }\n}\n\n// ─── Concrete steps ───────────────────────────────────────────────────────\n\nexport const CacheTtlStep: StampStep = {\n label: \"ttl\",\n applies(ctx) {\n return ctx.config.stampClaudeCode && !ctx.isOpenAi;\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const n = stampCacheTtl(body as AnthropicBody, STAMP_CACHE_TTL_VALUE);\n if (n > 0) {\n log.info(`stamped ttl=\"${STAMP_CACHE_TTL_VALUE}\" on ${n} block(s)`, {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\nexport const AnthropicBodyStep: StampStep = {\n label: \"anthropic-body\",\n applies(ctx) {\n return ctx.config.stampClaudeCode && !ctx.isOpenAi;\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const effort = resolveEffortForModel(ctx.modelName, true);\n const outputConfigValue = effort !== undefined ? { effort } : STAMP_OUTPUT_CONFIG_VALUE;\n const changed = stampThinking(body as AnthropicBody, {\n maxTokens: true,\n thinking: STAMP_THINKING_VALUE,\n outputConfig: outputConfigValue,\n });\n if (changed) {\n log.info(\"stamped anthropic body fields\", {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\nexport const ContextManagementStep: StampStep = {\n label: \"context-management\",\n applies(ctx) {\n return (\n ctx.config.stampClaudeCode &&\n !ctx.isOpenAi &&\n ctx.headers[\"anthropic-version\"] === \"2023-06-01\"\n );\n },\n apply(body) {\n if (body === null || typeof body !== \"object\") return false;\n const b = body as AnthropicBody;\n b.context_management = {\n edits: STAMP_CONTEXT_MANAGEMENT_VALUE.edits.map((e) => ({ ...e })),\n };\n log.info(\"stamped context_management\");\n return true;\n },\n};\n\nexport const OpenAiReasoningStep: StampStep = {\n label: \"openai-reasoning\",\n applies(ctx) {\n return ctx.isOpenAi && ctx.config.stampReasoningEffort !== null;\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const reasoningEffort =\n resolveEffortForModel(ctx.modelName, ctx.config.stampReasoningEffort !== null) ??\n (ctx.config.stampReasoningEffort as \"high\" | \"max\");\n const changed = stampReasoning(body as OpenAiBody, { reasoningEffort });\n if (changed) {\n log.info(\"stamped openai body reasoning_effort\", {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\nexport const TopKStep: StampStep = {\n label: \"top-k\",\n applies(ctx) {\n return ctx.config.stampClaudeCode && isGlmModel(ctx.modelName);\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const changed = stampTopK(body, STAMP_TOP_K_VALUE);\n if (changed) {\n log.info(`stamped top_k=${STAMP_TOP_K_VALUE}`, {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\nexport const TemperatureStep: StampStep = {\n label: \"temperature\",\n applies(ctx) {\n return ctx.config.stampClaudeCode;\n },\n apply(body, ctx) {\n if (body === null || typeof body !== \"object\") return false;\n const changed = stampTemperature(body, STAMP_TEMPERATURE_VALUE);\n if (changed) {\n log.info(`stamped temperature=${STAMP_TEMPERATURE_VALUE}`, {\n method: ctx.method,\n path: ctx.url.pathname,\n });\n return true;\n }\n return false;\n },\n};\n\n// ─── Pipeline ─────────────────────────────────────────────────────────────\n\n/** Ordered stamp steps applied to every request body before forwarding. */\nexport const STAMP_PIPELINE: StampStep[] = [\n CacheTtlStep,\n AnthropicBodyStep,\n ContextManagementStep,\n OpenAiReasoningStep,\n TopKStep,\n TemperatureStep,\n];\n","import { STAMP_REASONING_EFFORT_GLM_VALUE, STAMP_REASONING_EFFORT_VALUE } from \"./config.js\";\n\n/** Returns true if the model name belongs to the umans-glm family. */\nexport function isGlmModel(modelName: string | undefined): boolean {\n return typeof modelName === \"string\" && modelName.startsWith(\"umans-glm\");\n}\n\n/**\n * Resolve the effort level for a model.\n *\n * GLM models get \"max\"; all others get \"high\". When `enabled` is false,\n * returns undefined (stamping disabled). This is the single source of truth\n * for all GLM effort-policy decisions across the stamp pipeline.\n */\nexport function resolveEffortForModel(modelName: string | undefined, enabled: true): \"high\" | \"max\";\nexport function resolveEffortForModel(modelName: string | undefined, enabled: false): undefined;\nexport function resolveEffortForModel(\n modelName: string | undefined,\n enabled: boolean,\n): \"high\" | \"max\" | undefined;\nexport function resolveEffortForModel(\n modelName: string | undefined,\n enabled: boolean,\n): \"high\" | \"max\" | undefined {\n if (!enabled) return undefined;\n return isGlmModel(modelName) ? STAMP_REASONING_EFFORT_GLM_VALUE : STAMP_REASONING_EFFORT_VALUE;\n}\n","import type { OpenAiBody } from \"./types.js\";\n\nexport interface StampReasoningOptions {\n reasoningEffort: \"high\" | \"max\" | null | undefined;\n}\n\n/**\n * Stamp OpenAI-compatible request body with reasoning_effort and strip any\n * conflicting max_tokens/thinking fields. Mutates the body in place.\n * Returns true if the body was changed.\n *\n * When reasoning_effort is disabled (null/undefined), the key is removed from\n * the body rather than left as an explicit undefined value.\n */\nexport function stampReasoning(body: OpenAiBody, options: StampReasoningOptions): boolean {\n let changed = false;\n\n if (body.max_tokens !== undefined) {\n body.max_tokens = undefined;\n changed = true;\n }\n if (body.thinking !== undefined) {\n body.thinking = undefined;\n changed = true;\n }\n\n if (options.reasoningEffort === null || options.reasoningEffort === undefined) {\n if (Object.prototype.hasOwnProperty.call(body, \"reasoning_effort\")) {\n Reflect.deleteProperty(body, \"reasoning_effort\");\n changed = true;\n }\n } else if (body.reasoning_effort !== options.reasoningEffort) {\n body.reasoning_effort = options.reasoningEffort;\n changed = true;\n }\n\n return changed;\n}\n","// Force `temperature` onto LLM request bodies before forwarding upstream.\n// Applies to both OpenAI-compatible and Anthropic routes.\n// Overwrites any existing `temperature` value already set by the caller.\n\ninterface RequestBody {\n temperature?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Force `temperature` onto a parsed request body, overwriting any existing value.\n * Mutates the body in place. Returns true if the body was changed.\n */\nexport function stampTemperature(body: unknown, temperature: number): boolean {\n if (typeof body !== \"object\" || body === null || Array.isArray(body)) return false;\n const b = body as RequestBody;\n if (b.temperature === temperature) return false;\n b.temperature = temperature;\n return true;\n}\n","import {\n STAMP_MAX_TOKENS_GLM_VALUE,\n STAMP_MAX_TOKENS_VALUE,\n STAMP_OUTPUT_CONFIG_GLM_VALUE,\n STAMP_OUTPUT_CONFIG_VALUE,\n} from \"./config.js\";\nimport { isGlmModel } from \"./model-policy.js\";\nimport { extractModelName } from \"./models/name.js\";\nimport type { AnthropicBody, OutputConfig, ThinkingConfig } from \"./types.js\";\n\nconst DEFAULT_THINKING: ThinkingConfig = {\n type: \"adaptive\",\n};\n\nexport interface StampOptions {\n /** Inject `max_tokens` resolved from model (131071 for GLM, 32767 for others) when true. */\n maxTokens?: boolean;\n /** Inject `thinking` block when true. */\n thinking?: ThinkingConfig | boolean;\n /** Inject `output_config` block when true. */\n outputConfig?: OutputConfig | boolean;\n}\n\nfunction modelMatchesThinkingPattern(model: unknown): boolean {\n if (typeof model !== \"string\") return false;\n if (model === \"umans-coder\" || model === \"umans-flash\") return true;\n return model.startsWith(\"umans-kimi\") || model.startsWith(\"umans-qwen\");\n}\n\nfunction resolveOutputConfig(model: unknown, outputConfig: OutputConfig | boolean): OutputConfig {\n if (typeof outputConfig === \"object\" && outputConfig !== null) return outputConfig;\n if (typeof model === \"string\" && isGlmModel(model)) return STAMP_OUTPUT_CONFIG_GLM_VALUE;\n return STAMP_OUTPUT_CONFIG_VALUE;\n}\n\nfunction resolveMaxTokens(model: unknown): number {\n if (typeof model === \"string\" && isGlmModel(model)) return STAMP_MAX_TOKENS_GLM_VALUE;\n return STAMP_MAX_TOKENS_VALUE;\n}\n\n/**\n * Stamp Anthropic request body fields based on enabled toggles. Overwrites any\n * existing values. Mutates the body in place. Returns true if the body was changed.\n */\nexport function stampThinking(body: AnthropicBody, options: StampOptions): boolean {\n const model = extractModelName(body);\n let changed = false;\n\n if (options.maxTokens) {\n body.max_tokens = resolveMaxTokens(model);\n changed = true;\n }\n\n if (options.thinking) {\n if (modelMatchesThinkingPattern(model)) {\n body.thinking = typeof options.thinking === \"object\" ? options.thinking : DEFAULT_THINKING;\n changed = true;\n }\n }\n\n if (options.outputConfig) {\n body.output_config = resolveOutputConfig(model, options.outputConfig);\n changed = true;\n }\n\n return changed;\n}\n","// Inject `top_k` into LLM request bodies before forwarding upstream.\n// Applies to both OpenAI-compatible and Anthropic routes.\n// Places `top_k` immediately after `model` for consistent wire ordering.\n// Preserves any existing `top_k` value already set by the caller.\n\nimport { isGlmModel } from \"./model-policy.js\";\nimport { extractModelName } from \"./models/name.js\";\n\n/**\n * Body shape we mutate — has a `model` and optional `top_k` at top level.\n * The rest is opaque (OpenAI/Anthropic bodies differ in structure).\n */\ninterface RequestBody {\n model?: unknown;\n top_k?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Inject `top_k` into a parsed request body, placing it right after `model`.\n * If `top_k` already exists on the body, it is preserved (not overwritten).\n * Mutates the body in place. Returns true if the body was changed.\n *\n * Key ordering: JSON.stringify emits string keys in insertion order. To place\n * `top_k` immediately after `model`, we rebuild the object as a new record\n * with model first, then top_k, then the remaining keys — guaranteeing wire\n * ordering matches the UMANS API convention and the benchmark harness.\n */\nexport function stampTopK(body: unknown, topK: number): boolean {\n if (typeof body !== \"object\" || body === null || Array.isArray(body)) return false;\n const model = extractModelName(body);\n if (model === undefined) return false;\n if (!isGlmModel(model)) return false; // defense in depth — guard also in TopKStep.applies()\n const b = body as RequestBody;\n if (b.top_k !== undefined) return false; // caller already set — respect it\n\n // Rebuild with desired key order: model, top_k, then the rest.\n const rebuilt: Record<string, unknown> = {};\n rebuilt.model = model;\n rebuilt.top_k = topK;\n for (const k of Object.keys(b)) {\n if (k !== \"model\" && k !== \"top_k\") rebuilt[k] = b[k];\n }\n // Mutate original in place: clear then copy back.\n for (const k of Object.keys(b)) Reflect.deleteProperty(b, k);\n Object.assign(b, rebuilt);\n return true;\n}\n","// Anthropic cache_control TTL stamping.\n// Walks the request body's system + messages content arrays, stamping\n// `ttl` onto any `cache_control: {type:\"ephemeral\"}` block that lacks one.\n\nimport type { AnthropicBody, ContentBlock } from \"./types.js\";\n\n/**\n * Stamp `ttl` onto Anthropic-style cache_control ephemeral blocks that lack one.\n * Mutates the body in place. Returns the count of blocks stamped (caller\n * re-serializes only when > 0). Non-array system (e.g. a plain string) is\n * skipped safely.\n */\nexport function stampCacheTtl(body: AnthropicBody, ttl: string): number {\n let n = 0;\n\n const stamp = (blocks: unknown) => {\n if (!Array.isArray(blocks)) return;\n for (const b of blocks as ContentBlock[]) {\n const cc = b?.cache_control;\n if (cc?.type === \"ephemeral\" && !cc.ttl) {\n cc.ttl = ttl;\n n++;\n }\n }\n };\n\n stamp(body?.system);\n for (const m of body?.messages ?? []) {\n stamp(m.content);\n }\n\n return n;\n}\n","// Write-behind queue for capture updates.\n// Batches database writes to reduce SQLite contention during streaming.\n\nimport type { UpdateParams } from \"./db.js\";\nimport { buildSummary } from \"./helpers.js\";\nimport { createLogger } from \"./logger.js\";\nimport type { ProtocolConfig, QueueConfig, RequestMeta, ResponseMeta, WsMessage } from \"./types.js\";\n\nconst logger = createLogger(\"queue\");\n\ninterface QueuedUpdate {\n id: number;\n reqMeta: RequestMeta;\n res: ResponseMeta;\n}\n\n/** Abstraction over the capture persistence layer used by WriteQueue. */\nexport interface CaptureStore {\n updateCapture(params: UpdateParams): void;\n batchUpdate(items: Array<{ id: number; res: Omit<UpdateParams, \"$id\"> }>): Promise<void>;\n}\n\n/**\n * Batches response metadata updates and flushes them to the database\n * in a single transaction. Broadcasts WebSocket updates after each flush\n * via the optional onFlush callback.\n */\nexport class WriteQueue {\n private queue: QueuedUpdate[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly flushIntervalMs: number;\n private readonly flushBatch: number;\n private readonly queueMaxDepth: number;\n private store: CaptureStore;\n private onFlush?: (messages: WsMessage[]) => void;\n private config: QueueConfig & ProtocolConfig;\n droppedCount = 0;\n\n constructor(\n store: CaptureStore,\n config: QueueConfig & ProtocolConfig,\n onFlush?: (messages: WsMessage[]) => void,\n ) {\n this.store = store;\n this.config = config;\n this.onFlush = onFlush;\n this.flushIntervalMs = config.flushIntervalMs;\n this.flushBatch = config.flushBatch;\n this.queueMaxDepth = config.queueMaxDepth;\n }\n\n /** Queue a response metadata update for a capture. */\n queueUpdate(id: number, reqMeta: RequestMeta, res: ResponseMeta): void {\n this.queue.push({ id, reqMeta, res });\n if (this.queue.length >= this.flushBatch) {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n void this.flushNow().catch((err) => {\n logger.error(\"WriteQueue flush failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n } else if (!this.flushTimer) {\n this.flushTimer = setTimeout(() => {\n void this.flushNow().catch((err) => {\n logger.error(\"WriteQueue flush failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }, this.flushIntervalMs);\n }\n if (this.queue.length >= this.queueMaxDepth) {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n void this.flushNow().catch((err) => {\n logger.error(\"WriteQueue flush failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n if (this.queue.length >= this.queueMaxDepth) {\n const dropped = this.queue.shift();\n this.droppedCount++;\n logger.warn(\"WriteQueue overflow: dropped oldest entry\", {\n captureId: dropped?.id,\n depth: this.queue.length,\n maxDepth: this.queueMaxDepth,\n totalDropped: this.droppedCount,\n });\n }\n }\n }\n\n get length(): number {\n return this.queue.length;\n }\n\n get hasTimer(): boolean {\n return this.flushTimer !== null;\n }\n\n /** Flush all queued updates to the database immediately.\n * On batchUpdate failure, re-queues the batch at the front so items are\n * not permanently lost. The drop path in queueUpdate() will activate\n * if the queue overflows after a failed flush. */\n async flushNow(): Promise<void> {\n this.flushTimer = null;\n if (this.queue.length === 0) return;\n const batch = this.queue.splice(0, this.queue.length);\n try {\n await this.store.batchUpdate(batch.map((it) => ({ id: it.id, res: it.res })));\n } catch (err) {\n this.queue.unshift(...batch);\n logger.error(\"WriteQueue flush failed, re-queued batch\", {\n batchSize: batch.length,\n depth: this.queue.length,\n error: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n if (this.onFlush) {\n const messages: WsMessage[] = batch.map((it) => ({\n type: \"update\" as const,\n capture: buildSummary(it.id, it.reqMeta, it.res, this.config),\n }));\n this.onFlush(messages);\n }\n }\n}\n","// Sliding-window rate limiter for the pro-tier request limit.\n// Tracks weighted entries: each request consumes `weight` units of the budget.\n// Binary-search pruning; check() records, peek() does not.\n\nexport interface RateLimitConfig {\n limit: number;\n windowSeconds: number;\n}\n\nexport interface RateCheckResult {\n allowed: boolean;\n remaining: number;\n retryAfterSeconds: number | null;\n}\n\ninterface Entry {\n time: number;\n weight: number;\n}\n\nexport class SlidingWindowRateLimiter {\n private entries: Entry[] = [];\n private runningSum = 0;\n private readonly limit;\n private readonly windowMs;\n\n constructor(config: RateLimitConfig) {\n this.limit = config.limit;\n this.windowMs = config.windowSeconds * 1000;\n }\n\n /** Record a request with the given weight. Returns whether it was allowed. */\n check(weight = 1, now: number = Date.now()): RateCheckResult {\n this.prune(now);\n const currentWeight = this.runningSum;\n if (currentWeight + weight > this.limit) {\n const oldest = this.entries[0];\n const retryAfter = oldest ? Math.ceil((oldest.time + this.windowMs - now) / 1000) : 1;\n return {\n allowed: false,\n remaining: Math.max(0, this.limit - currentWeight),\n retryAfterSeconds: Math.max(1, retryAfter),\n };\n }\n this.entries.push({ time: now, weight });\n this.runningSum += weight;\n return {\n allowed: true,\n remaining: Math.max(0, this.limit - currentWeight - weight),\n retryAfterSeconds: null,\n };\n }\n\n /** Check whether a request with the given weight would be allowed, without recording. */\n peek(weight = 1, now: number = Date.now()): RateCheckResult {\n this.prune(now);\n const currentWeight = this.runningSum;\n if (currentWeight + weight > this.limit) {\n const oldest = this.entries[0];\n const retryAfter = oldest ? Math.ceil((oldest.time + this.windowMs - now) / 1000) : 1;\n return {\n allowed: false,\n remaining: Math.max(0, this.limit - currentWeight),\n retryAfterSeconds: Math.max(1, retryAfter),\n };\n }\n return {\n allowed: true,\n remaining: Math.max(0, this.limit - currentWeight - weight),\n retryAfterSeconds: null,\n };\n }\n\n private prune(now: number): void {\n const cutoff = now - this.windowMs;\n let lo = 0;\n let hi = this.entries.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (this.entries[mid].time < cutoff) lo = mid + 1;\n else hi = mid;\n }\n if (lo > 0) {\n for (let i = 0; i < lo; i++) this.runningSum -= this.entries[i].weight;\n this.entries.splice(0, lo);\n }\n }\n\n /** Total weight of entries currently in the window. */\n count(now: number = Date.now()): number {\n this.prune(now);\n return this.runningSum;\n }\n}\n","// Shared /v1/usage fetch helper.\n// Consolidates duplicated fetch+parse+error handling between reconciler and aggregator.\n\nimport type { RawUsage } from \"./parser.js\";\n\nconst USAGE_PATH = \"/v1/usage\";\n\nexport type { RawUsage };\n\nexport type FetchUsageResult = { ok: true; data: RawUsage } | { ok: false; error: string };\n\n/** Shared raw fetch of the upstream /v1/usage endpoint.\n * Preserves the exact request shape used by callers: GET, no query params,\n * and an `authorization: Bearer <apiKey>` header when an API key is provided. */\nexport async function fetchUsageRaw(\n target: string,\n apiKey: string | null,\n signal?: AbortSignal,\n): Promise<FetchUsageResult> {\n if (!apiKey) return { ok: false, error: \"umans_api_key not set\" };\n try {\n const res = await fetch(`${target}${USAGE_PATH}`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${apiKey}` },\n signal,\n });\n if (!res.ok) return { ok: false, error: `HTTP ${res.status}` };\n const data = (await res.json()) as RawUsage;\n return { ok: true, data };\n } catch (e) {\n return { ok: false, error: e instanceof Error ? e.message : String(e) };\n }\n}\n","// Raw upstream /v1/usage response parsing + snapshot construction.\n// Pure functions: no I/O, no mutation. Reusable by aggregator + reconciler.\n\nimport type { UsageSnapshot } from \"../types.js\";\n\n/** Raw shape of the upstream /v1/usage response. */\nexport interface RawUsage {\n plan?: { display_name?: string };\n limits?: {\n requests?: { limit?: number; hard_cap?: number; burst_pct?: number; window_seconds?: number };\n concurrency?: { limit?: number; hard_cap?: number; burst_pct?: number };\n };\n usage?: {\n requests_in_window?: number;\n remaining_requests?: number;\n concurrent_sessions?: number;\n tokens_in?: number;\n tokens_out?: number;\n priority?: {\n low?: boolean;\n boxed_until?: number | null;\n reason?: string | null;\n units_demoted?: boolean;\n demoted_until?: number | null;\n };\n };\n}\n\n/** Classify a plan display name into one of the known tiers.\n * Uses substring matching so variants like \"Code Max (Founding Seat)\"\n * still resolve to \"Code Max\". */\nfunction classifyPlan(name: string | undefined): \"Code Pro\" | \"Code Max\" | \"unknown\" {\n if (!name) return \"unknown\";\n const lower = name.toLowerCase();\n if (lower.includes(\"code max\")) return \"Code Max\";\n if (lower.includes(\"code pro\")) return \"Code Pro\";\n return \"unknown\";\n}\n\n/** Build a UsageSnapshot from a parsed raw response.\n * Carries forward last-known concurrency limits when the upstream omits them. */\nexport function buildSnapshot(\n raw: RawUsage,\n ok: boolean,\n lastConcurrencyHardCap: number,\n lastConcurrencySoftLimit: number,\n): UsageSnapshot {\n const planName = raw.plan?.display_name;\n const plan = classifyPlan(planName);\n return {\n ok,\n fetchedAt: Date.now(),\n plan,\n requestsLimit: raw.limits?.requests?.limit ?? null,\n requestsHardCap: raw.limits?.requests?.hard_cap ?? null,\n requestsWindowSeconds: raw.limits?.requests?.window_seconds ?? null,\n concurrencySoftLimit: raw.limits?.concurrency?.limit ?? lastConcurrencySoftLimit,\n concurrencyHardCap: raw.limits?.concurrency?.hard_cap ?? lastConcurrencyHardCap,\n requestsInWindow: raw.usage?.requests_in_window ?? 0,\n requestsRemaining: raw.usage?.remaining_requests ?? null,\n concurrentSessions: raw.usage?.concurrent_sessions ?? 0,\n priorityLow: raw.usage?.priority?.low ?? false,\n boxedUntil: raw.usage?.priority?.boxed_until ?? null,\n boxedReason: raw.usage?.priority?.reason ?? null,\n unitsDemoted: raw.usage?.priority?.units_demoted ?? false,\n demotedUntil: raw.usage?.priority?.demoted_until ?? null,\n };\n}\n\n/** Fail-safe worst-case snapshot used when no data is available.\n * Assumes at hard cap, priority low — the most conservative posture. */\nexport function failSafeSnapshot(): UsageSnapshot {\n return {\n ok: false,\n fetchedAt: 0,\n plan: \"unknown\",\n requestsLimit: null,\n requestsHardCap: null,\n requestsWindowSeconds: null,\n concurrencySoftLimit: 1,\n concurrencyHardCap: 1,\n requestsInWindow: 0,\n requestsRemaining: null,\n concurrentSessions: 0,\n priorityLow: true,\n boxedUntil: null,\n boxedReason: null,\n unitsDemoted: false,\n demotedUntil: null,\n };\n}\n","// One-shot /v1/usage fetches for limit reconciliation.\n// Used by proxy startup + config validation to compare source-of-truth limits\n// against cached/captured values. Does NOT touch live snapshot state.\n\nimport { fetchUsageRaw } from \"./fetch-usage.js\";\n\nexport type ConcurrencyLimitResult =\n | { ok: true; hardCap: number; softLimit: number }\n | { ok: false; error: string };\n\nexport type RequestsLimitResult =\n | { ok: true; limit: number | null; hardCap: number | null; windowSeconds: number | null }\n | { ok: false; error: string };\n\n/** One-shot fetch of /v1/usage to extract concurrency hard_cap + soft_limit.\n * Does NOT update live snapshot. */\nexport async function fetchConcurrencyLimits(\n target: string,\n apiKey: string | null,\n): Promise<ConcurrencyLimitResult> {\n const result = await fetchUsageRaw(target, apiKey);\n if (!result.ok) return { ok: false, error: result.error };\n const hardCap = Math.max(1, Math.floor(Number(result.data.limits?.concurrency?.hard_cap ?? 1)));\n const softLimit = Math.max(1, Math.floor(Number(result.data.limits?.concurrency?.limit ?? 1)));\n return { ok: true, hardCap, softLimit };\n}\n\n/** One-shot fetch of /v1/usage to extract requests limits for rate_limit_requests validation.\n * Returns {limit, hardCap, windowSeconds} or null if not set (unlimited). */\nexport async function fetchRequestsLimits(\n target: string,\n apiKey: string | null,\n): Promise<RequestsLimitResult> {\n const result = await fetchUsageRaw(target, apiKey);\n if (!result.ok) return { ok: false, error: result.error };\n return {\n ok: true,\n limit: result.data.limits?.requests?.limit ?? null,\n hardCap: result.data.limits?.requests?.hard_cap ?? null,\n windowSeconds: result.data.limits?.requests?.window_seconds ?? null,\n };\n}\n","// UmansUsageClient — accumulates /v1/usage snapshots with periodic refresh.\n// Owns mutable state (snapshot, timer, fetching flag) and the onChange callback.\n// Delegates parsing to ./parser.js and one-shot limit fetches to ./reconciler.js.\n\nimport { createLogger } from \"../logger.js\";\nimport type { ProxyConfig, UsageSnapshot } from \"../types.js\";\nimport { type RawUsage, fetchUsageRaw } from \"./fetch-usage.js\";\nimport { buildSnapshot, failSafeSnapshot } from \"./parser.js\";\nimport { fetchConcurrencyLimits, fetchRequestsLimits } from \"./reconciler.js\";\n\nconst log = createLogger(\"usage\");\n\nexport class UmansUsageClient {\n private snapshot: UsageSnapshot | null = null;\n private timer: ReturnType<typeof setInterval> | null = null;\n private fetching = false;\n private readonly target: string;\n private readonly apiKey: string | null;\n private readonly refreshMs: number;\n private onChangeCb: ((s: UsageSnapshot) => void) | null = null;\n\n constructor(config: Pick<ProxyConfig, \"target\" | \"umansApiKey\" | \"usageRefreshMs\">) {\n this.target = config.target.replace(/\\/+$/, \"\");\n this.apiKey = config.umansApiKey;\n this.refreshMs = config.usageRefreshMs;\n }\n\n onChange(cb: (s: UsageSnapshot) => void): void {\n this.onChangeCb = cb;\n }\n\n start(): void {\n if (!this.apiKey || this.timer) return;\n void this.refresh();\n this.timer = setInterval(() => void this.refresh(), this.refreshMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n getSnapshot(): UsageSnapshot {\n if (this.snapshot) return this.snapshot;\n return failSafeSnapshot();\n }\n\n async refresh(): Promise<void> {\n if (this.fetching || !this.apiKey) return;\n this.fetching = true;\n try {\n const result = await fetchUsageRaw(this.target, this.apiKey);\n if (!result.ok) {\n this.applyFailedSnapshot(result.error);\n return;\n }\n this.applySnapshot(result.data, true);\n } finally {\n this.fetching = false;\n }\n }\n\n /** One-shot fetch of /v1/usage to extract concurrency hard_cap + soft_limit.\n * Does NOT update live snapshot. */\n async fetchLimitsFromSource(): Promise<\n { ok: true; hardCap: number; softLimit: number } | { ok: false; error: string }\n > {\n return fetchConcurrencyLimits(this.target, this.apiKey);\n }\n\n /** One-shot fetch of /v1/usage to extract requests limits for rate_limit_requests validation.\n * Returns {limit, hardCap, windowSeconds} or null if not set (unlimited). */\n async fetchRequestsLimit(): Promise<\n | { ok: true; limit: number | null; hardCap: number | null; windowSeconds: number | null }\n | { ok: false; error: string }\n > {\n return fetchRequestsLimits(this.target, this.apiKey);\n }\n\n private applySnapshot(raw: RawUsage, ok: boolean): void {\n const lastHardCap = this.snapshot?.concurrencyHardCap ?? 1;\n const lastSoftLimit = this.snapshot?.concurrencySoftLimit ?? 1;\n const snap = buildSnapshot(raw, ok, lastHardCap, lastSoftLimit);\n this.snapshot = snap;\n this.onChangeCb?.(snap);\n }\n\n private applyFailedSnapshot(reason: string): void {\n if (this.snapshot) {\n this.snapshot = { ...this.snapshot, ok: false, fetchedAt: Date.now() };\n } else {\n this.snapshot = this.getSnapshot();\n this.snapshot.ok = false;\n }\n this.onChangeCb?.(this.snapshot);\n log.error(`fetch failed: ${reason}`);\n }\n}\n","// Viewer router — serves the inspector dashboard and REST API.\n// All routes are under the /dashboard prefix.\n//\n// Asset resolution order:\n// 1. dashboard/dist/ (production build from Vite)\n// 2. 404\n\nimport {\n type ReloadResult,\n readConfigFile,\n resetConfig,\n saveConfig,\n validateConfig,\n} from \"./config.js\";\nimport type { RawConfigInput } from \"./config.js\";\nimport type { CaptureDB } from \"./db.js\";\nimport {\n getAvailableMonths,\n getDailyUsage,\n getMonthSummary,\n getPricingTable,\n} from \"./economics.js\";\nimport { summary } from \"./helpers.js\";\nimport type { ConcurrencyGate } from \"./limiter/index.js\";\nimport type { ModelsClient } from \"./models.js\";\nimport type { ProxyConfig } from \"./types.js\";\nimport type { UmansUsageClient } from \"./usage.js\";\nimport type { VisionHandoff } from \"./vision/handoff.js\";\nimport type { WsBroadcaster } from \"./ws.js\";\n\n// Embedded assets: in compiled executables, Bun.embeddedFiles exposes imported\n// `with { type: \"file\" }` assets as Blobs. In dev mode, it's an empty array.\n// This allows the dashboard to work inside standalone compiled executables.\nimport { embeddedFiles } from \"bun\";\nimport { EMBEDDED_ASSET_PATHS } from \"./embedded-assets.js\";\n\ninterface NamedBlob extends Blob {\n name: string;\n}\n\n// Prevent tree-shaking of the embedded asset imports.\n// EMBEDDED_ASSET_PATHS is referenced here so the bundler keeps the side-effect imports.\nvoid EMBEDDED_ASSET_PATHS;\n\n// Module-level: populated only in compiled executables.\n// In dev mode, Bun.embeddedFiles is [] (empty array), so this stays null.\nconst EMBEDDED_ASSETS: Map<string, Blob> | null = (() => {\n try {\n if (!embeddedFiles || embeddedFiles.length === 0) return null;\n const map = new Map<string, Blob>();\n for (const blob of embeddedFiles as NamedBlob[]) {\n // blob.name is an internal Bun virtual FS path like /$bunfs/root/dashboard/dist/index.html\n // Strip the prefix to get the relative path within dashboard/dist/\n const name = blob.name.replace(/^.*\\/dashboard\\/dist\\//, \"\");\n if (name) {\n // Also store a de-hashed version (Bun adds hash suffix: index-a1b2c3d4.html → index.html)\n const dehashed = name.replace(/-[a-f0-9]{6,}(\\.[^.]+)$/, \"$1\");\n map.set(name, blob);\n if (dehashed !== name) map.set(dehashed, blob);\n }\n }\n return map.size > 0 ? map : null;\n } catch {\n return null;\n }\n})();\n\nexport interface CreateViewerRouterOptions {\n db: CaptureDB;\n ws: WsBroadcaster;\n config: ProxyConfig;\n gate: ConcurrencyGate;\n usage: UmansUsageClient;\n vision: VisionHandoff | null;\n models: ModelsClient | null;\n reloadConfig?: () => ReloadResult;\n refreshLimits?: () => Promise<\n | {\n ok: true;\n hardCap: number;\n softLimit: number;\n requestsLimit: number | null;\n requestsHardCap: number | null;\n requestsWindowSeconds: number | null;\n }\n | { ok: false; error: string }\n >;\n restart?: () => void;\n}\n\n/** Content type map for common static file extensions. */\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"application/javascript; charset=utf-8\",\n \".mjs\": \"application/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\n/** Resolve content type from file path. */\nfunction contentTypeFor(path: string): string {\n for (const [ext, ct] of Object.entries(CONTENT_TYPES)) {\n if (path.endsWith(ext)) return ct;\n }\n return \"application/octet-stream\";\n}\n\n/**\n * Try to resolve a static file from multiple candidate directories.\n * Returns the Bun file if found, null otherwise.\n *\n * Security: after resolving the relative path against each candidate base,\n * verify the resolved URL remains inside the base directory. This is an\n * explicit containment check that does not rely on URL-parser normalization\n * or Bun.file's own file-URL validation to prevent path traversal.\n */\nasync function resolveStaticFile(\n relativePath: string,\n candidates: URL[],\n): Promise<Response | null> {\n if (EMBEDDED_ASSETS) {\n const dehashed = relativePath.replace(/-[a-f0-9]{6,}(\\.[^.]+)$/, \"$1\");\n const blob = EMBEDDED_ASSETS.get(relativePath) ?? EMBEDDED_ASSETS.get(dehashed);\n if (blob) {\n return new Response(blob, {\n headers: { \"content-type\": contentTypeFor(relativePath) },\n });\n }\n }\n for (const base of candidates) {\n const fileUrl = new URL(relativePath, base);\n // Reject any resolved path that escapes the base directory.\n // base.href always ends with \"/\" (constructed with trailing slash),\n // so a contained fileUrl must start with base.href.\n if (!fileUrl.href.startsWith(base.href)) continue;\n try {\n const file = Bun.file(fileUrl);\n if (await file.exists()) {\n return new Response(file, {\n headers: { \"content-type\": contentTypeFor(relativePath) },\n });\n }\n } catch {\n // Bun.file rejects malformed file URLs (e.g. encoded path\n // separators like %2f). Treat as \"not found\" and continue.\n }\n }\n return null;\n}\n\n/** Result of matching a RegExp route pattern; `match[1]` is the capture id. */\ntype RegExpMatch = RegExpMatchArray;\n\n/** All dependencies a route handler needs from the viewer router closure. */\ninterface ViewerRouteContext {\n url: URL;\n req: Request;\n pathname: string;\n /** Present only when the matched route's pattern is a RegExp. */\n match: RegExpMatch | null;\n db: CaptureDB;\n ws: WsBroadcaster;\n config: ProxyConfig;\n gate: ConcurrencyGate;\n usage: UmansUsageClient;\n vision: VisionHandoff | null;\n models: ModelsClient | null;\n reloadConfig: (() => ReloadResult) | null;\n refreshLimits:\n | (() => Promise<\n | {\n ok: true;\n hardCap: number;\n softLimit: number;\n requestsLimit: number | null;\n requestsHardCap: number | null;\n requestsWindowSeconds: number | null;\n }\n | { ok: false; error: string }\n >)\n | null;\n restart: (() => void) | null;\n}\n\n/** A single route in the viewer dispatch table. */\ninterface ViewerRoute {\n method: string;\n pattern: string | RegExp;\n handler: (ctx: ViewerRouteContext) => Response | Promise<Response>;\n}\n\n/**\n * Create the viewer router that handles static asset serving and the REST API.\n * Returns null if the request is not a viewer route (caller should proxy it).\n */\nexport function createViewerRouter(options: CreateViewerRouterOptions) {\n const { db, ws, config, gate, usage, vision, models, reloadConfig, refreshLimits, restart } =\n options;\n const VIEWER = config.viewerPrefix;\n const DETAIL_RE = new RegExp(`^${VIEWER}/api/captures/(\\\\d+)$`);\n\n // Candidate directories for static assets, in priority order.\n // When running from src/ (dev): dashboard/dist/ is in project root.\n // When running from dist/ (built): dashboard/dist/ is a sibling to dist/.\n const assetBases: URL[] = [\n // dashboard/dist/ relative to src/ (dev mode)\n new URL(\"../../dashboard/dist/\", import.meta.url),\n // dashboard/dist/ relative to dist/ (built mode)\n new URL(\"../dashboard/dist/\", import.meta.url),\n ];\n\n // Route table — exact priority order of the former if-chain.\n // API routes first, then the DETAIL_RE regex route, then static-file/SPA\n // fallback is handled after the loop (see handleViewer).\n const ROUTES: ViewerRoute[] = [\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/captures`,\n handler: (ctx) => {\n const limit = Math.min(Number(ctx.url.searchParams.get(\"limit\") ?? 200), 1000);\n const rows = ctx.db.list(limit);\n return Response.json(rows.map(summary));\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/clear`,\n handler: (ctx) => {\n ctx.db.clear();\n ctx.vision?.clearRecords();\n ctx.ws.broadcast({ type: \"clear\" });\n return Response.json({ ok: true });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/gate`,\n handler: (ctx) => Response.json(ctx.gate.getStats(ctx.usage.getSnapshot())),\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/usage`,\n handler: (ctx) => Response.json(ctx.usage.getSnapshot()),\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/models`,\n handler: (ctx) => {\n if (!ctx.models) return Response.json({ models: [], fetched_at: 0, ok: false });\n return Response.json({\n models: ctx.models.list(),\n fetched_at: ctx.models.lastFetchedAt(),\n ok: ctx.models.healthy(),\n });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/performance`,\n handler: (ctx) => Response.json(ctx.db.getPerformanceStats()),\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/vision-calls`,\n handler: (ctx) => {\n const limit = Math.min(Number(ctx.url.searchParams.get(\"limit\") ?? 100), 500);\n return Response.json(ctx.db.getVisionCallRecords(limit));\n },\n },\n {\n method: \"DELETE\",\n pattern: `${VIEWER}/api/vision-calls`,\n handler: (ctx) => {\n ctx.vision?.clearRecords();\n ctx.ws.broadcast({ type: \"vision-clear\" });\n return Response.json({ ok: true });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/vision-cache-stats`,\n handler: (ctx) => Response.json(ctx.vision?.getCacheStats() ?? null),\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/config`,\n handler: (ctx) => {\n const raw = readConfigFile();\n const { umans_api_key: _omitted, ...safe } = raw;\n // Use the resolved config's umansApiKey (env > file) so has_api_key\n // is true even when the key is set via UMANS_API_KEY env var only.\n return Response.json({ ...safe, has_api_key: Boolean(ctx.config.umansApiKey) });\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/config`,\n handler: async (ctx) => {\n try {\n const body = (await ctx.req.json()) as RawConfigInput;\n const result = saveConfig(body);\n return Response.json(result, { status: result.ok ? 200 : 400 });\n } catch (e) {\n return Response.json(\n {\n ok: false,\n errors: [`Invalid JSON body: ${e instanceof Error ? e.message : String(e)}`],\n warnings: [],\n written: null,\n },\n { status: 400 },\n );\n }\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/config/reset`,\n handler: async (ctx) => {\n const result = resetConfig();\n if (!result.ok) {\n return Response.json(result, { status: 500 });\n }\n if (ctx.refreshLimits) {\n const limits = await ctx.refreshLimits();\n return Response.json({\n ...result,\n limits: limits.ok\n ? {\n hardCap: limits.hardCap,\n softLimit: limits.softLimit,\n requestsLimit: limits.requestsLimit ?? null,\n requestsHardCap: limits.requestsHardCap ?? null,\n requestsWindowSeconds: limits.requestsWindowSeconds ?? null,\n }\n : null,\n });\n }\n return Response.json(result);\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/config/validate`,\n handler: async (ctx) => {\n try {\n const body = (await ctx.req.json()) as RawConfigInput;\n const result = validateConfig(body);\n return Response.json({ ok: result.ok, errors: result.errors, warnings: result.warnings });\n } catch (e) {\n return Response.json(\n {\n ok: false,\n errors: [`Invalid JSON body: ${e instanceof Error ? e.message : String(e)}`],\n warnings: [],\n },\n { status: 400 },\n );\n }\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/config/reload`,\n handler: (ctx) => {\n if (!ctx.reloadConfig) {\n return Response.json(\n {\n ok: false,\n errors: [\"Reload not available\"],\n warnings: [],\n applied: [],\n restartRequired: [],\n configPath: \"\",\n },\n { status: 501 },\n );\n }\n const result = ctx.reloadConfig();\n return Response.json(result);\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/usage/refresh-source`,\n handler: async (ctx) => {\n if (!ctx.refreshLimits) {\n return Response.json({ ok: false, error: \"Refresh not available\" }, { status: 501 });\n }\n const result = await ctx.refreshLimits();\n return Response.json(result);\n },\n },\n {\n method: \"POST\",\n pattern: `${VIEWER}/api/restart`,\n handler: (ctx) => {\n const restart = ctx.restart;\n if (!restart) {\n return Response.json({ ok: false, error: \"Restart not available\" }, { status: 501 });\n }\n setTimeout(() => restart(), 100);\n return Response.json({\n ok: true,\n message:\n \"Server is restarting. Requires a process manager (bun --watch, systemd, pm2) to auto-restart — otherwise the server exits and stays down.\",\n });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/economics/summary`,\n handler: (ctx) => {\n const year = Number(ctx.url.searchParams.get(\"year\") ?? new Date().getFullYear());\n const month = Number(ctx.url.searchParams.get(\"month\") ?? new Date().getMonth() + 1);\n const summaryData = getMonthSummary(ctx.db.rawDb, year, month);\n const months = getAvailableMonths(ctx.db.rawDb);\n const pricing = getPricingTable(ctx.db.rawDb);\n return Response.json({ summary: summaryData, months, pricing });\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/economics/daily`,\n handler: (ctx) => {\n const limit = Math.min(Number(ctx.url.searchParams.get(\"limit\") ?? 90), 365);\n const rows = getDailyUsage(ctx.db.rawDb, limit);\n return Response.json(rows);\n },\n },\n {\n method: \"GET\",\n pattern: `${VIEWER}/api/economics/pricing`,\n handler: (ctx) => Response.json(getPricingTable(ctx.db.rawDb)),\n },\n // DETAIL_RE regex route — must come after all exact-match API routes and\n // before static file fallback. Uses the capture id from match[1].\n {\n method: \"GET\",\n pattern: DETAIL_RE,\n handler: (ctx) => {\n const m = ctx.match;\n if (!m) return new Response(\"not found\", { status: 404 });\n const row = ctx.db.get(Number(m[1]));\n if (!row) return new Response(\"not found\", { status: 404 });\n return Response.json(row);\n },\n },\n ];\n\n /**\n * Handle a viewer request. Returns null for non-viewer paths (caller proxies).\n */\n async function handleViewer(url: URL, req: Request): Promise<Response | null> {\n const p = url.pathname;\n\n // WebSocket upgrade is handled by the server, not here.\n if (p === `${VIEWER}/ws`) return null;\n\n // Build context once for all routes.\n const ctx: ViewerRouteContext = {\n url,\n req,\n pathname: p,\n match: null,\n db,\n ws,\n config,\n gate,\n usage,\n vision,\n models,\n reloadConfig: reloadConfig ?? null,\n refreshLimits: refreshLimits ?? null,\n restart: restart ?? null,\n };\n\n // Dispatch: first matching route (method + pattern) wins.\n for (const route of ROUTES) {\n if (route.method !== req.method) continue;\n\n if (route.pattern instanceof RegExp) {\n const m = p.match(route.pattern);\n if (m) {\n ctx.match = m;\n return route.handler(ctx);\n }\n } else if (p === route.pattern) {\n return route.handler(ctx);\n }\n }\n\n // Static assets: serve from dashboard/dist/\n // Map /dashboard/ → index.html, /dashboard/assets/* → assets/*\n if (p === VIEWER || p === `${VIEWER}/`) {\n const resp = await resolveStaticFile(\"index.html\", assetBases);\n if (resp) {\n resp.headers.set(\"cache-control\", \"no-cache, no-store, must-revalidate\");\n return resp;\n }\n return new Response(\"dashboard not built. Run: cd dashboard && bun run build\", {\n status: 404,\n });\n }\n\n // Other static asset paths under /dashboard/\n if (p.startsWith(`${VIEWER}/`)) {\n const relativePath = p.slice(`${VIEWER}/`.length);\n\n // Try to serve from asset bases\n const resp = await resolveStaticFile(relativePath, assetBases);\n if (resp) return resp;\n\n // SPA fallback: if no asset matches, serve index.html (for client-side routing)\n // But only for non-API, non-asset paths\n if (!relativePath.startsWith(\"api/\") && !relativePath.includes(\".\")) {\n const spaResp = await resolveStaticFile(\"index.html\", assetBases);\n if (spaResp) {\n spaResp.headers.set(\"cache-control\", \"no-cache, no-store, must-revalidate\");\n return spaResp;\n }\n }\n\n return new Response(\"not found\", { status: 404 });\n }\n\n return new Response(\"not found\", { status: 404 });\n }\n\n return { handleViewer, VIEWER };\n}\n","// Description LRU cache + SHA-256 key generation for vision handoff.\n// In-memory only; entries are lost on restart. Backed by lru-cache v11.\n\nimport { LRUCache } from \"lru-cache\";\nimport type { PersistentDescriptionStore } from \"./persistent-cache.js\";\n\nexport interface CompressionRecipe {\n format: string;\n quality: number;\n max_dimension: number;\n}\n\n/**\n * Stable image-hash key over (original_bytes || canonical_settings || encoder_version).\n * Uses Bun's built-in CryptoHasher — faster than node:crypto.\n */\nexport function imageCacheKey(\n bytes: Buffer | Uint8Array,\n recipe: CompressionRecipe,\n encoderVersion: string,\n): string {\n const h = new Bun.CryptoHasher(\"sha256\");\n h.update(bytes);\n h.update(JSON.stringify(recipe));\n h.update(encoderVersion);\n return h.digest(\"hex\");\n}\n\n/**\n * Description-cache key over (image_hash, prompt_version, model_id).\n * Description text is a VALUE, not part of the KEY.\n */\nexport function descriptionCacheKey(\n bytes: Buffer | Uint8Array,\n recipe: CompressionRecipe,\n encoderVersion: string,\n modelId: string,\n promptVersion: number,\n): string {\n const base = imageCacheKey(bytes, recipe, encoderVersion);\n const h = new Bun.CryptoHasher(\"sha256\");\n h.update(base);\n h.update(`|pv=${promptVersion}|model=${modelId}`);\n return h.digest(\"hex\");\n}\n\n/** Stats surfaced by {@link DescriptionCache}. */\nexport interface DescriptionCacheStats {\n hits: number;\n misses: number;\n evictions: number;\n size: number;\n}\n\n/**\n * Synchronous LRU description cache. Lets the VisionHandoff orchestrator\n * check for a hit before issuing an async vision-model call.\n *\n * `getOrCompute` is intentionally SYNCHRONOUS: JS event loop is single\n * threaded, so no locking is needed; the orchestrator performs async work\n * OUTSIDE the cache and stores the result via a second call on hit-less\n * paths (or by supplying a `compute` that has already resolved).\n */\nexport class DescriptionCache {\n private readonly cache: LRUCache<string, string>;\n private readonly persistent: PersistentDescriptionStore | null;\n private hits = 0;\n private misses = 0;\n private evictions = 0;\n private persistentHits = 0;\n private persistentWrites = 0;\n\n constructor(maxSize: number, ttlMs: number, persistent?: PersistentDescriptionStore | null) {\n this.persistent = persistent ?? null;\n this.cache = new LRUCache<string, string>({\n max: maxSize,\n ttl: ttlMs,\n dispose: (_value, _key, reason) => {\n if (reason === \"evict\" || reason === \"expire\") this.evictions++;\n },\n });\n }\n\n get size(): number {\n return this.cache.size;\n }\n\n get stats(): DescriptionCacheStats {\n return {\n hits: this.hits,\n misses: this.misses,\n evictions: this.evictions,\n size: this.cache.size,\n };\n }\n\n get persistentStats(): { hits: number; writes: number } {\n return { hits: this.persistentHits, writes: this.persistentWrites };\n }\n\n warm(key: string, description: string): void {\n this.cache.set(key, description);\n }\n\n getOrCompute(\n bytes: Buffer | Uint8Array,\n recipe: CompressionRecipe,\n encoderVersion: string,\n modelId: string,\n promptVersion: number,\n compute: () => string,\n ): string {\n const key = descriptionCacheKey(bytes, recipe, encoderVersion, modelId, promptVersion);\n if (this.cache.has(key)) {\n const v = this.cache.get(key);\n if (v !== undefined) {\n this.hits++;\n return v;\n }\n }\n\n if (this.persistent) {\n const persisted = this.persistent.get(key);\n if (persisted !== null) {\n this.persistentHits++;\n this.cache.set(key, persisted);\n this.hits++;\n return persisted;\n }\n }\n\n const value = compute();\n this.cache.set(key, value);\n this.misses++;\n\n if (this.persistent) {\n const imageHash = imageCacheKey(bytes, recipe, encoderVersion);\n this.persistent.set({\n key,\n description: value,\n imageHash,\n model: modelId,\n promptVersion,\n });\n this.persistentWrites++;\n }\n return value;\n }\n}\n","// Image block detection + byte scan for LLM request bodies.\n// Walks OpenAI image_url parts and Anthropic image blocks, plus a cheap\n// regex fast-path that avoids full JSON deserialization when no image is present.\n\n/** Detected image part, normalized across OpenAI and Anthropic shapes. */\nexport interface ImagePart {\n mediaType: string | null;\n encoding: \"base64\" | \"url\";\n data: string;\n}\n\n/** Tristate capability: true = supports vision, false = does not, \"via-handoff\" = only via handoff. */\nexport type VisionTristate = true | false | \"via-handoff\";\n\n/** Lookup interface for vision-capability queries. Implemented by ModelsClient + ModelInfoClient. */\nexport interface VisionLookup {\n getVisionSupport(modelName: string): VisionTristate | null;\n}\n\n/** Which API shape the body follows. */\nexport type ApiKind = \"openai\" | \"anthropic\";\n\n/** Rewrite strategy from config. */\nexport type RewriteStrategy = \"never\" | \"catalog\" | \"always\";\n\n/** OpenAI content[] walker: collects every image_url part. */\nexport function findOpenAIImageParts(body: unknown): ImagePart[] {\n const out: ImagePart[] = [];\n if (typeof body !== \"object\" || body === null) return out;\n const messages = (body as { messages?: unknown }).messages;\n if (!Array.isArray(messages)) return out;\n for (const msg of messages) {\n if (typeof msg !== \"object\" || msg === null) continue;\n const content = (msg as { content?: unknown }).content;\n if (!Array.isArray(content)) continue;\n for (const part of content) {\n if (typeof part !== \"object\" || part === null) continue;\n if ((part as { type?: unknown }).type !== \"image_url\") continue;\n const imageUrl = (part as { image_url?: { url?: unknown } }).image_url;\n const url = typeof imageUrl?.url === \"string\" ? imageUrl.url : \"\";\n if (!url) continue;\n const m = /^data:([^;]+);base64,(.+)$/.exec(url);\n if (m) {\n out.push({ mediaType: m[1], encoding: \"base64\", data: m[2] });\n } else {\n out.push({ mediaType: inferExtFromUrl(url), encoding: \"url\", data: url });\n }\n }\n }\n return out;\n}\n\n/** Anthropic content[] walker: recurses into messages[].content[] and tool_result.content[]. */\nexport function findAnthropicImageParts(body: unknown): ImagePart[] {\n const out: ImagePart[] = [];\n if (typeof body !== \"object\" || body === null) return out;\n const messages = (body as { messages?: unknown }).messages;\n if (!Array.isArray(messages)) return out;\n\n const walk = (arr: unknown[]) => {\n for (const part of arr) {\n if (typeof part !== \"object\" || part === null) continue;\n const p = part as { type?: unknown; source?: unknown; content?: unknown };\n if (p.type === \"image\" && p.source && typeof p.source === \"object\") {\n const src = p.source as {\n type?: unknown;\n media_type?: unknown;\n data?: unknown;\n url?: unknown;\n };\n if (src.type === \"base64\") {\n out.push({\n mediaType: typeof src.media_type === \"string\" ? src.media_type : null,\n encoding: \"base64\",\n data: typeof src.data === \"string\" ? src.data : \"\",\n });\n } else if (src.type === \"url\") {\n const url = typeof src.url === \"string\" ? src.url : \"\";\n out.push({\n mediaType: inferExtFromUrl(url),\n encoding: \"url\",\n data: url,\n });\n }\n } else if (p.type === \"tool_result\" && Array.isArray(p.content)) {\n walk(p.content);\n }\n }\n };\n\n for (const msg of messages) {\n if (typeof msg !== \"object\" || msg === null) continue;\n const content = (msg as { content?: unknown }).content;\n if (Array.isArray(content)) walk(content);\n }\n return out;\n}\n\n/**\n * Cheap signal: does the stringified body contain an image block?\n * Mirrors a memchr scan for `\"type\":\"image_url\"` or `\"type\":\"image\"` without\n * fully deserialising. The Rust equivalent would use `memchr::memmem::find`.\n */\nexport function cheapImageSignal(bodyStr: string): boolean {\n return (\n /\"type\"\\s*:\\s*\"image_url\"/.test(bodyStr) || /\"type\"\\s*:\\s*\"image\"\\s*,\\s*\"source\"/.test(bodyStr)\n );\n}\n\n/** Infer a media type from a URL's extension; null if unknown. */\nexport function inferExtFromUrl(url: string): string | null {\n const m = /\\.(png|jpe?g|webp|gif|bmp|tiff?|heic|heif|svg)$/i.exec(url);\n if (!m) return null;\n const ext = m[1].toLowerCase();\n if (ext === \"jpg\") return \"image/jpeg\";\n if (ext === \"tif\" || ext === \"tiff\") return \"image/tiff\";\n return `image/${ext}`;\n}\n\n/**\n * Tristate capability resolution. Decides whether the proxy should rewrite\n * an image-bearing request into a vision handoff.\n *\n * - `never` → never rewrite\n * - `always` → always rewrite\n * - `catalog` → consult the model's vision capability:\n * - supports_vision: false → rewrite (model can't see images)\n * - supports_vision: \"via-handoff\" → rewrite OpenAI only (server handles Anthropic)\n * - supports_vision: true → pass through, UNLESS forceInterceptCapable is set\n * - unknown (null) → pass through (fail-safe: don't intercept if uncertain)\n */\nexport function shouldRewrite(\n strategy: RewriteStrategy,\n supports: VisionTristate | null,\n apiKind: ApiKind,\n forceInterceptCapable = false,\n): boolean {\n if (strategy === \"never\") return false;\n if (strategy === \"always\") return true;\n // catalog\n if (supports === null) return false;\n if (supports === false) return true;\n if (supports === \"via-handoff\") return apiKind === \"openai\";\n // supports === true\n return forceInterceptCapable;\n}\n","// Image transcode: decode → resize → JPEG re-encode via Bun.Image.\n// Returns bytes + SHA-256 hash + output dimensions for cache keying.\n\n/** Options for {@link transcodeImage}. */\nexport interface TranscodeOptions {\n /** Longest side after resize; aspect ratio is preserved (fit: \"inside\"). */\n maxDimension: number;\n /** JPEG quality 1–100 (used only when format is \"jpeg\"). */\n quality: number;\n /** Output format: \"jpeg\" (lossy) or \"png\" (lossless). Default \"jpeg\". */\n format?: \"jpeg\" | \"png\";\n}\n\n/** Result of a successful transcode. */\nexport interface TranscodeResult {\n bytes: Uint8Array;\n /** SHA-256 hex of the encoded JPEG bytes (not the source). */\n hash: string;\n width: number;\n height: number;\n format: string;\n}\n\n/** Error codes callers can branch on for fail-open behaviour. */\nexport type TranscodeErrorCode = \"unsupported\" | \"decode_failed\" | \"encode_failed\" | \"too_large\";\n\n/**\n * Typed error thrown by {@link transcodeImage}. Callers should catch this\n * and treat the image as un-transcodable (fail-open) rather than crashing.\n */\nexport class TranscodeError extends Error {\n readonly code: TranscodeErrorCode;\n constructor(\n code: TranscodeErrorCode,\n message: string,\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"TranscodeError\";\n this.code = code;\n }\n}\n\n/**\n * Map a `Bun.Image` rejection's `error.code` to a {@link TranscodeErrorCode}.\n * Returns `null` for codes we don't classify (caller rethrows).\n */\nfunction classifyBunImageError(err: unknown): TranscodeErrorCode | null {\n if (typeof err !== \"object\" || err === null) return null;\n const code = (err as { code?: unknown }).code;\n switch (code) {\n case \"ERR_IMAGE_FORMAT_UNSUPPORTED\":\n case \"ERR_IMAGE_UNKNOWN_FORMAT\":\n return \"unsupported\";\n case \"ERR_IMAGE_DECODE_FAILED\":\n return \"decode_failed\";\n case \"ERR_IMAGE_ENCODE_FAILED\":\n return \"encode_failed\";\n case \"ERR_IMAGE_TOO_MANY_PIXELS\":\n return \"too_large\";\n default:\n return null;\n }\n}\n\n/**\n * Decode image bytes, resize to fit within `maxDimension` (preserving aspect\n * ratio), and re-encode as progressive JPEG quality `quality`. Returns the\n * encoded bytes, their SHA-256 hash, and the output dimensions.\n *\n * Errors from `Bun.Image` terminals are converted to {@link TranscodeError}\n * so the caller can fail-open without a `try/catch` wrapping every call.\n */\nexport async function transcodeImage(\n imageBytes: Uint8Array,\n opts: TranscodeOptions = { maxDimension: 1024, quality: 85 },\n): Promise<TranscodeResult> {\n const img = new Bun.Image(imageBytes, { maxPixels: 50_000_000 });\n try {\n await img.metadata();\n } catch (err) {\n const code = classifyBunImageError(err);\n if (code) {\n throw new TranscodeError(code, `image decode failed: ${code}`, err);\n }\n throw err;\n }\n\n img.resize(opts.maxDimension, opts.maxDimension, { fit: \"inside\", filter: \"lanczos3\" });\n\n const format = opts.format ?? \"jpeg\";\n let encoded: Uint8Array;\n try {\n if (format === \"png\") {\n encoded = await img.png().bytes();\n } else {\n encoded = await img.jpeg({ quality: opts.quality, progressive: true }).bytes();\n }\n } catch (err) {\n const code = classifyBunImageError(err);\n if (code) {\n throw new TranscodeError(code, `image encode failed: ${code}`, err);\n }\n throw err;\n }\n\n const hash = new Bun.CryptoHasher(\"sha256\").update(encoded).digest(\"hex\");\n return { bytes: encoded, hash, width: img.width, height: img.height, format };\n}\n","// Deterministic replacement-text wrapper + failure placeholders + format policies.\n// Mirrors umans-dash's label format so upstream KV-cache prefixes remain stable.\n// All output text is byte-identical for identical inputs — no dynamic metadata.\n\nimport type { ImagePart } from \"./detect.js\";\n\n/** Reason a vision analysis failed. Constrained to safe, non-leaky tokens. */\nexport type FailureReason = \"timeout\" | \"http_status\" | \"parse\" | \"empty\" | \"generic\" | \"aborted\";\n\n/** What to do with a given image format. */\nexport type FormatPolicy = \"pass\" | \"transcode\" | \"reject\";\n\n/**\n * Deterministic wrapper. Mirrors umans-dash's label format.\n *\n * The label is byte-identical for identical `desc` inputs — it never includes\n * timestamps, request IDs, image position, or any runtime metadata. The\n * active-model caveat is fixed text so upstream KV-cache prefixes stay stable.\n */\nexport function wrapDescription(desc: string): string {\n return `[Image content — analyzed by vision module, shown as text because the active model cannot see images:]\\n${desc}`;\n}\n\n/**\n * Failure placeholder. Always starts with `[Image analysis failed:` and ends\n * with a fixed model-caveat suffix. `detail` is interpolated only for the\n * safe-reason tokens (`timeout`, `http_status`, `generic`) — it must be a\n * sanitized scalar, never an upstream body or stack frame.\n */\nexport function failurePlaceholder(reason: FailureReason, detail: string): string {\n const reasonText: Record<FailureReason, string> = {\n timeout: `vision model timed out after ${detail}`,\n http_status: `vision model returned HTTP ${detail}`,\n parse: \"vision model returned an unparseable response\",\n empty: \"vision model returned an empty description\",\n generic: detail,\n aborted: \"client disconnected before vision model responded\",\n };\n return `[Image analysis failed: ${reasonText[reason]}. The active model cannot see this image.]`;\n}\n\n/**\n * Format policy: pass-through natively supported formats, transcode\n * bitmaps/heic/svg, reject anything else. Extension is lower-cased; mediaType\n * is accepted but the extension wins (mirrors PoC at L668-675).\n */\nexport function formatPolicy(ext: string, _mediaType: string): FormatPolicy {\n const supported = new Set([\"png\", \"jpg\", \"jpeg\", \"webp\", \"gif\"]);\n const transcodable = new Set([\"bmp\", \"tiff\", \"tif\", \"heic\", \"heif\", \"svg\"]);\n const e = ext.toLowerCase();\n if (supported.has(e)) return \"pass\";\n if (transcodable.has(e)) return \"transcode\";\n return \"reject\";\n}\n\n/**\n * Apply the max-images overflow policy.\n *\n * Image parts beyond `maxImages` are dropped from the `kept` set and each is\n * replaced with a deterministic placeholder in `overflow`. The placeholder\n * tells the model not to describe or reference the omitted image, preventing\n * hallucination about content the model never received.\n */\nexport function applyMaxImagesPolicy(\n parts: ImagePart[],\n maxImages: number,\n): { kept: ImagePart[]; overflow: string[] } {\n const kept = parts.slice(0, maxImages);\n const overflow = parts\n .slice(maxImages)\n .map(\n (_, i) =>\n `[Image omitted: not delivered to model — do not describe or reference it. (overflow ${i + 1})]`,\n );\n return { kept, overflow };\n}\n","// Vision handoff orchestrator.\n// Drives the full pipeline: detect images → check cache → transcode → call\n// vision model → wrap descriptions → replace image blocks in body.\n//\n// The orchestrator never holds the vision permit and the main proxy permit\n// at the same time: the caller (proxy.ts) acquires the vision permit, invokes\n// `processBody`, and only after it returns acquires the main upstream permit.\n// All I/O here is async; the synchronous `DescriptionCache` is consulted\n// before any network call so cache hits never touch the network.\n\nimport { flattenUsage } from \"../db.js\";\nimport type { CaptureDB } from \"../db.js\";\nimport { headersToObject, redactHeaders } from \"../helpers.js\";\nimport { ConcurrencyGate } from \"../limiter/index.js\";\nimport { createLogger } from \"../logger.js\";\nimport type { CaptureState, ProtocolConfig } from \"../types.js\";\nimport { extractOpenAiNonStreaming } from \"../usage/extract.js\";\nimport type { UsageMetrics } from \"../usage/types.js\";\nimport type { CompressionRecipe, DescriptionCache } from \"./cache.js\";\nimport { descriptionCacheKey } from \"./cache.js\";\nimport type { ApiKind, ImagePart, VisionLookup, VisionTristate } from \"./detect.js\";\nimport {\n cheapImageSignal,\n findAnthropicImageParts,\n findOpenAIImageParts,\n shouldRewrite,\n} from \"./detect.js\";\nimport type { VisionHttpExchange, VisionRecordSink } from \"./sink.js\";\nimport { TranscodeError, transcodeImage } from \"./transcode.js\";\nimport { applyMaxImagesPolicy, failurePlaceholder, wrapDescription } from \"./wrapper.js\";\n\nconst log = createLogger(\"vision\");\n\n/** Strategy for when to rewrite image-bearing requests. */\ntype VisionStrategy = \"never\" | \"catalog\" | \"always\";\n\nconst DEFAULT_VISION_BREAKER_THRESHOLD = 100;\nconst DEFAULT_VISION_BREAKER_WINDOW_MS = 5000;\nconst DEFAULT_VISION_BREAKER_COOLDOWN_MS = 1000;\nconst DEFAULT_VISION_MAX_QUEUE_DEPTH = 256;\nconst DEFAULT_VISION_QUEUE_TIMEOUT_MS = 30000;\n\n/**\n * Vision-handoff configuration extracted from {@link ProxyConfig} fields.\n * `target` / `model` / `apiKey` are nullable so the caller can disable\n * handoff by leaving them unset without constructing an invalid config.\n */\nexport interface VisionConfig {\n strategy: VisionStrategy;\n target: string | null;\n model: string | null;\n prompt: string;\n promptVersion: number;\n maxImages: number;\n maxDescriptionTokens: number;\n reasoningEffort: \"none\" | \"low\" | \"medium\" | \"high\" | null;\n timeoutMs: number;\n cacheSize: number;\n cacheTtlMs: number;\n cacheMaxRows: number;\n persistentCache: boolean;\n apiKey: string | null;\n forceInterceptCapable: boolean;\n concurrency: number;\n maxDimension: number;\n jpegQuality: number;\n imageFormat: \"jpeg\" | \"png\";\n imageDetail: \"auto\" | \"low\" | \"high\";\n /** Weight used when acquiring a vision permit from the shared concurrency gate. */\n visionWeight: number;\n /** Circuit-breaker failure threshold for the fallback concurrency gate. */\n breakerThreshold?: number;\n /** Window over which circuit-breaker failures are counted. */\n breakerWindowMs?: number;\n /** Cooldown before the circuit breaker allows traffic again. */\n breakerCooldownMs?: number;\n /** Maximum queued permits for the fallback concurrency gate. */\n maxQueueDepth?: number;\n /** Timeout for queued permits waiting for a free slot. */\n queueTimeoutMs?: number;\n /** When true, cache misses forward the original body immediately and process vision in the background. */\n backgroundVision: boolean;\n}\n\n/** Per-call statistics surfaced back to the caller. */\ninterface VisionStats {\n handoffCount: number;\n cacheHits: number;\n cacheMisses: number;\n visionCalls: number;\n latencyMs: number[];\n}\n\n/** A recorded vision call for debugging and dashboard visibility. */\nexport interface VisionCallRecord {\n id: number;\n timestamp: number;\n captureId: number | null;\n model: string;\n target: string;\n imageSize: number;\n imageHash: string | null;\n status:\n | \"ok\"\n | \"cache_hit\"\n | \"http_error\"\n | \"timeout\"\n | \"fetch_error\"\n | \"parse_error\"\n | \"empty\"\n | \"skipped\"\n | \"aborted\";\n httpStatus: number | null;\n latencyMs: number;\n description: string;\n error: string | null;\n incomingProtocol: string;\n upstreamProtocol: string;\n state: CaptureState;\n}\n\n/** Result of {@link processBody}. */\nexport interface ProcessBodyResult {\n body: unknown;\n changed: boolean;\n stats: VisionStats;\n}\n\n/** Per-image result from processImage — assembled by processBody into stats. */\ninterface ImageProcessResult {\n description: string;\n cacheHit: boolean;\n cacheMiss: boolean;\n visionCall: boolean;\n latencyMs: number;\n}\n\n/** Encoder version tag mixed into the cache key. Bump when transcode output bytes change. */\nconst ENCODER_VERSION = \"bun-image-v2\";\n\n/**\n * Sentinel value returned (via throw) from {@link DescriptionCache.getOrCompute}\n * when a vision description is not found in either the LRU or persistent cache.\n *\n * Callers should treat this as a cache miss and proceed to invoke the upstream\n * vision model. The value is always empty (`\"\"`) after the miss is caught.\n *\n * This is a unique `Symbol` rather than `undefined` or `null` so it can never\n * collide with a legitimate cached string (including empty descriptions) and\n * remains detectable across the `getOrCompute` callback boundary.\n */\nconst CACHE_MISS = Symbol(\"cache-miss\");\n\nfunction recipeFromConfig(cfg: VisionConfig): CompressionRecipe {\n return {\n format: cfg.imageFormat,\n quality: cfg.jpegQuality,\n max_dimension: cfg.maxDimension,\n };\n}\n\n/**\n * Orchestrates the vision handoff pipeline for a single request body.\n *\n * One instance is cheap to construct and holds no per-call state beyond the\n * constructor-supplied config and cache, so it can be reused across requests\n * or created per-request. The cache is shared by reference.\n */\nexport class VisionHandoff {\n private records: VisionCallRecord[] = [];\n private nextRecordId = 1;\n private readonly maxRecords: number;\n private readonly gate: ConcurrencyGate;\n private readonly inflight = new Map<string, Promise<string>>();\n\n constructor(\n private readonly config: VisionConfig,\n private readonly cache: DescriptionCache,\n private readonly catalog: VisionLookup | null = null,\n gate?: ConcurrencyGate,\n private readonly db?: CaptureDB,\n private readonly sink?: VisionRecordSink,\n private readonly protocolConfig: ProtocolConfig = {\n incomingProtocol: \"http1.1\",\n upstreamProtocol: \"http1.1\",\n upstreamTimeoutMs: 300000,\n },\n ) {\n this.maxRecords = Math.max(config.cacheSize, 200);\n this.gate =\n gate ??\n new ConcurrencyGate({\n hardCap: Math.max(1, config.concurrency),\n softLimit: Math.max(1, config.concurrency),\n releaseCooldownMs: 0,\n breakerThreshold: config.breakerThreshold ?? DEFAULT_VISION_BREAKER_THRESHOLD,\n breakerWindowMs: config.breakerWindowMs ?? DEFAULT_VISION_BREAKER_WINDOW_MS,\n breakerCooldownMs: config.breakerCooldownMs ?? DEFAULT_VISION_BREAKER_COOLDOWN_MS,\n maxQueueDepth: config.maxQueueDepth ?? DEFAULT_VISION_MAX_QUEUE_DEPTH,\n queueTimeoutMs: config.queueTimeoutMs ?? DEFAULT_VISION_QUEUE_TIMEOUT_MS,\n intentions: { vision: config.concurrency },\n });\n }\n\n get visionActive(): number {\n return this.gate.getIntentionActive(\"vision\");\n }\n\n get visionQueued(): number {\n return this.gate.getIntentionQueued(\"vision\");\n }\n\n getCacheStats(): {\n lru: { hits: number; misses: number; evictions: number; size: number };\n persistent: { hits: number; writes: number };\n } {\n return {\n lru: this.cache.stats,\n persistent: this.cache.persistentStats,\n };\n }\n\n /** Return recent vision call records (newest first). */\n getRecords(limit = 100): VisionCallRecord[] {\n return this.records.slice(-limit).reverse();\n }\n\n /** Clear all stored vision call records. */\n clearRecords(): void {\n this.records = [];\n this.nextRecordId = 1;\n this.db?.clearVisionCaptures();\n }\n\n private addRecord(\n rec: Omit<\n VisionCallRecord,\n \"id\" | \"timestamp\" | \"incomingProtocol\" | \"upstreamProtocol\" | \"state\"\n >,\n httpExchange?: VisionHttpExchange,\n usage: UsageMetrics | null = null,\n dbId?: number,\n ): void {\n const now = Date.now();\n const full: VisionCallRecord = {\n ...rec,\n id: this.nextRecordId++,\n timestamp: now,\n incomingProtocol: this.protocolConfig.incomingProtocol,\n upstreamProtocol: this.protocolConfig.upstreamProtocol,\n state: \"done\",\n };\n this.records.push(full);\n if (this.records.length > this.maxRecords) {\n this.records.splice(0, this.records.length - this.maxRecords);\n }\n\n this.sink?.record({ rec: full, httpExchange, usage, dbId });\n }\n\n /**\n * Process a request body: detect images, transcode + describe each via the\n * vision model (or serve from cache), and replace image blocks in the body\n * with text blocks containing the wrapped descriptions.\n *\n * The original `body` is never mutated — a deep clone is produced first.\n * Fail-open: on any per-image error (transcode, timeout, HTTP, parse), the\n * image is replaced with a deterministic failure placeholder rather than\n * aborting the whole request.\n *\n * If a model name and catalog are provided, the catalog strategy consults\n * the model's vision capability to decide whether to intercept.\n */\n async processBody(\n body: unknown,\n apiKind: ApiKind,\n modelName?: string,\n captureId?: number,\n signal?: AbortSignal,\n ): Promise<ProcessBodyResult> {\n const stats: VisionStats = {\n handoffCount: 0,\n cacheHits: 0,\n cacheMisses: 0,\n visionCalls: 0,\n latencyMs: [],\n };\n\n // 0. Catalog-aware gate: if we know the model and it supports vision,\n // decide whether to intercept based on strategy + forceInterceptCapable.\n if (this.config.strategy === \"catalog\" && modelName && this.catalog) {\n const supports: VisionTristate | null = this.catalog.getVisionSupport(modelName);\n if (\n !shouldRewrite(this.config.strategy, supports, apiKind, this.config.forceInterceptCapable)\n ) {\n return { body, changed: false, stats };\n }\n }\n\n // 1. Cheap signal: skip full parse when no image block can be present.\n const bodyStr = typeof body === \"string\" ? body : JSON.stringify(body);\n if (!cheapImageSignal(bodyStr)) {\n return { body, changed: false, stats };\n }\n\n // 2. Extract image parts per API shape.\n const parts =\n apiKind === \"anthropic\" ? findAnthropicImageParts(body) : findOpenAIImageParts(body);\n if (parts.length === 0) {\n return { body, changed: false, stats };\n }\n\n // 3. Cap image count; overflow becomes deterministic placeholders.\n const { kept, overflow } = applyMaxImagesPolicy(parts, this.config.maxImages);\n stats.handoffCount = kept.length;\n\n // 4. Clone the body so the original is never mutated.\n const mutated = cloneBody(body);\n\n // 5. For each kept image: transcode → cache lookup → vision call → wrap.\n // Processed in parallel; the concurrency gate serializes vision calls.\n const wrappedDescriptions: string[] = [];\n const results = await Promise.allSettled(\n kept.map((part) => this.processImage(part, captureId, signal)),\n );\n for (const result of results) {\n const r: ImageProcessResult =\n result.status === \"fulfilled\"\n ? result.value\n : {\n description: failurePlaceholder(\"generic\", \"unexpected error\"),\n cacheHit: false,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n wrappedDescriptions.push(r.description);\n if (r.cacheHit) stats.cacheHits++;\n if (r.cacheMiss) stats.cacheMisses++;\n if (r.visionCall) {\n stats.visionCalls++;\n stats.latencyMs.push(r.latencyMs);\n }\n }\n\n // 6. Replace image blocks in the cloned body with text blocks.\n replaceImageBlocks(mutated, apiKind, wrappedDescriptions, overflow);\n\n return {\n body: mutated,\n changed: true,\n stats,\n };\n }\n\n /**\n * Cache-only version of {@link processBody}. Checks the LRU + persistent\n * cache for every image part. On any cache miss, the original body is\n * forwarded unchanged and a background `processBody` call is enqueued to\n * populate the cache for future requests.\n *\n * Returns `{ changed: true }` only when every image was a cache hit.\n */\n async processBodyCacheOnly(\n body: unknown,\n apiKind: ApiKind,\n modelName?: string,\n captureId?: number,\n signal?: AbortSignal,\n ): Promise<ProcessBodyResult> {\n const stats: VisionStats = {\n handoffCount: 0,\n cacheHits: 0,\n cacheMisses: 0,\n visionCalls: 0,\n latencyMs: [],\n };\n\n if (this.config.strategy === \"catalog\" && modelName && this.catalog) {\n const supports: VisionTristate | null = this.catalog.getVisionSupport(modelName);\n if (\n !shouldRewrite(this.config.strategy, supports, apiKind, this.config.forceInterceptCapable)\n ) {\n return { body, changed: false, stats };\n }\n }\n\n const bodyStr = typeof body === \"string\" ? body : JSON.stringify(body);\n if (!cheapImageSignal(bodyStr)) {\n return { body, changed: false, stats };\n }\n\n const parts =\n apiKind === \"anthropic\" ? findAnthropicImageParts(body) : findOpenAIImageParts(body);\n if (parts.length === 0) {\n return { body, changed: false, stats };\n }\n\n const { kept } = applyMaxImagesPolicy(parts, this.config.maxImages);\n stats.handoffCount = kept.length;\n\n const recipe = recipeFromConfig(this.config);\n const descriptions: string[] = [];\n let allHit = true;\n\n for (const part of kept) {\n if (part.encoding === \"url\") {\n allHit = false;\n break;\n }\n const decoded = decodeBase64(part.data);\n if (decoded === null) {\n allHit = false;\n break;\n }\n let cacheBytes: Uint8Array;\n try {\n const result = await transcodeImage(decoded, {\n maxDimension: recipe.max_dimension,\n quality: recipe.quality,\n format: this.config.imageFormat,\n });\n cacheBytes = result.bytes;\n } catch {\n allHit = false;\n break;\n }\n let cached = \"\";\n try {\n cached = this.cache.getOrCompute(\n cacheBytes,\n recipe,\n ENCODER_VERSION,\n this.config.model ?? \"\",\n this.config.promptVersion,\n () => {\n throw CACHE_MISS;\n },\n );\n } catch (err) {\n if (err !== CACHE_MISS) throw err;\n }\n if (cached !== \"\") {\n descriptions.push(wrapDescription(cached));\n stats.cacheHits++;\n } else {\n allHit = false;\n break;\n }\n }\n\n if (!allHit) {\n this.enqueueBackgroundVision(body, apiKind, modelName, captureId, signal);\n return { body, changed: false, stats };\n }\n\n const mutated = cloneBody(body);\n replaceImageBlocks(mutated, apiKind, descriptions, []);\n return { body: mutated, changed: true, stats };\n }\n\n private enqueueBackgroundVision(\n body: unknown,\n apiKind: ApiKind,\n modelName: string | undefined,\n captureId: number | undefined,\n signal: AbortSignal | undefined,\n ): void {\n const bgSignal = signal ? AbortSignal.any([signal]) : undefined;\n this.processBody(body, apiKind, modelName, captureId, bgSignal).catch((err) => {\n log.warn(\"background vision processing failed\", {\n error: (err as Error).message,\n captureId,\n });\n });\n }\n\n /**\n * Process a single image part: decode → transcode → cache check → vision call → record.\n * Returns a result object that the caller assembles into stats + wrappedDescriptions.\n * Never rejects — errors produce a failure placeholder.\n */\n private async processImage(\n part: ImagePart,\n captureId: number | undefined,\n signal: AbortSignal | undefined,\n ): Promise<ImageProcessResult> {\n if (part.encoding === \"url\") {\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: 0,\n imageHash: null,\n status: \"skipped\",\n httpStatus: null,\n latencyMs: 0,\n description: \"\",\n error: \"url images unsupported in v1\",\n });\n return {\n description: failurePlaceholder(\"generic\", \"url images unsupported in v1\"),\n cacheHit: false,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const decoded = decodeBase64(part.data);\n if (decoded === null) {\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: 0,\n imageHash: null,\n status: \"parse_error\",\n httpStatus: null,\n latencyMs: 0,\n description: \"\",\n error: \"invalid base64 image data\",\n });\n return {\n description: failurePlaceholder(\"parse\", \"invalid base64 image data\"),\n cacheHit: false,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const recipe = recipeFromConfig(this.config);\n let cacheBytes: Uint8Array;\n try {\n const result = await transcodeImage(decoded, {\n maxDimension: recipe.max_dimension,\n quality: recipe.quality,\n format: this.config.imageFormat,\n });\n cacheBytes = result.bytes;\n } catch (err) {\n const errMsg = err instanceof TranscodeError ? `transcode ${err.code}` : \"transcode failed\";\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: decoded.byteLength,\n imageHash: null,\n status: \"fetch_error\",\n httpStatus: null,\n latencyMs: 0,\n description: \"\",\n error: errMsg,\n });\n return {\n description: failurePlaceholder(\"generic\", errMsg),\n cacheHit: false,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const imageHash = simpleHash(cacheBytes);\n\n let cached: string;\n try {\n cached = this.cache.getOrCompute(\n cacheBytes,\n recipe,\n ENCODER_VERSION,\n this.config.model ?? \"\",\n this.config.promptVersion,\n () => {\n throw CACHE_MISS;\n },\n );\n } catch (err) {\n if (err !== CACHE_MISS) throw err;\n cached = \"\";\n }\n if (cached !== \"\") {\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: cacheBytes.byteLength,\n imageHash,\n status: \"cache_hit\",\n httpStatus: null,\n latencyMs: 0,\n description: cached,\n error: null,\n });\n return {\n description: wrapDescription(cached),\n cacheHit: true,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const cacheKey = descriptionCacheKey(\n cacheBytes,\n recipe,\n ENCODER_VERSION,\n this.config.model ?? \"\",\n this.config.promptVersion,\n );\n\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n const desc = await existing;\n this.addRecord({\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: cacheBytes.byteLength,\n imageHash,\n status: \"cache_hit\",\n httpStatus: null,\n latencyMs: 0,\n description: desc,\n error: null,\n });\n return {\n description: wrapDescription(desc),\n cacheHit: true,\n cacheMiss: false,\n visionCall: false,\n latencyMs: 0,\n };\n }\n\n const startedAt = Date.now();\n const visionDbId = this.db?.insertVisionCapture({\n $method: \"POST\",\n $path: \"/v1/chat/completions\",\n $url: this.config.target ?? \"\",\n $rh: \"{}\",\n $rb: \"{}\",\n $rs: 0,\n $status: null,\n $rh2: \"{}\",\n $rb2: \"\",\n $rs2: 0,\n $ct: \"application/json\",\n $dur: 0,\n $state: \"enqueued\",\n $started_at: startedAt,\n $finished_at: 0,\n $inp: this.protocolConfig.incomingProtocol,\n $outp: this.protocolConfig.upstreamProtocol,\n $model: this.config.model ?? \"\",\n $parent_capture_id: captureId ?? null,\n $vision_meta: null,\n ...flattenUsage(null),\n });\n if (visionDbId) this.db?.setState(visionDbId, \"streaming\");\n\n const start = Date.now();\n const visionPromise = (async () => {\n const result = await this.callVisionRecorded(cacheBytes, signal);\n const stored = this.cache.getOrCompute(\n cacheBytes,\n recipe,\n ENCODER_VERSION,\n this.config.model ?? \"\",\n this.config.promptVersion,\n () => result.description,\n );\n return { result, stored };\n })();\n this.inflight.set(\n cacheKey,\n visionPromise.then((r) => r.stored),\n );\n\n let visionResult: Awaited<ReturnType<typeof this.callVisionRecorded>>;\n let stored: string;\n try {\n const r = await visionPromise;\n visionResult = r.result;\n stored = r.stored;\n } finally {\n this.inflight.delete(cacheKey);\n }\n const elapsed = Date.now() - start;\n\n if (visionDbId) {\n const finishedAt = Date.now();\n const metaJson = JSON.stringify({\n status: visionResult.status,\n httpStatus: visionResult.httpStatus,\n latencyMs: elapsed,\n description: visionResult.description,\n error: visionResult.error,\n imageHash,\n imageSize: cacheBytes.byteLength,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n });\n this.db?.updateVisionCapture({\n $id: visionDbId,\n $status:\n visionResult.status === \"ok\" || visionResult.status === \"cache_hit\"\n ? 200\n : (visionResult.httpStatus ?? null),\n $rh: visionResult.responseHeaders,\n $rb: visionResult.responseBody,\n $rs: Buffer.byteLength(visionResult.responseBody),\n $ct: \"application/json\",\n $sse: 0,\n $dur: elapsed,\n $fin: finishedAt,\n $status_source: \"upstream\",\n $gate_reason: null,\n $vision_meta: metaJson,\n $model: this.config.model ?? null,\n ...flattenUsage(visionResult.usage),\n });\n }\n\n this.addRecord(\n {\n captureId: captureId ?? null,\n model: this.config.model ?? \"\",\n target: this.config.target ?? \"\",\n imageSize: cacheBytes.byteLength,\n imageHash,\n status: visionResult.status,\n httpStatus: visionResult.httpStatus,\n latencyMs: elapsed,\n description: visionResult.description,\n error: visionResult.error,\n },\n {\n requestBody: visionResult.requestBody,\n requestHeaders: visionResult.requestHeaders,\n responseBody: visionResult.responseBody,\n responseHeaders: visionResult.responseHeaders,\n },\n visionResult.usage,\n visionDbId,\n );\n\n return {\n description: wrapDescription(stored),\n cacheHit: false,\n cacheMiss: true,\n visionCall: true,\n latencyMs: elapsed,\n };\n }\n\n /**\n * Call the vision model with a single JPEG image. Returns the description\n * text or a deterministic failure placeholder on any error (fail-open).\n *\n * Request shape follows the OpenAI chat-completions format so a single\n * vision endpoint can describe images regardless of the upstream API kind.\n */\n private async callVisionRecorded(\n imageBytes: Uint8Array,\n signal?: AbortSignal,\n ): Promise<{\n description: string;\n status: VisionCallRecord[\"status\"];\n httpStatus: number | null;\n error: string | null;\n requestBody: string;\n requestHeaders: string;\n responseBody: string;\n responseHeaders: string;\n usage: UsageMetrics | null;\n }> {\n const { target, model, prompt, reasoningEffort, imageFormat, imageDetail, apiKey } =\n this.config;\n if (!target || !model) {\n log.warn(\"callVision skipped: target or model not configured\");\n return {\n description: failurePlaceholder(\"generic\", \"vision target or model not configured\"),\n status: \"skipped\",\n httpStatus: null,\n error: \"target or model not configured\",\n requestBody: \"\",\n requestHeaders: \"{}\",\n responseBody: \"\",\n responseHeaders: \"{}\",\n usage: null,\n };\n }\n\n const authHeader = apiKey ? `Bearer ${apiKey}` : \"\";\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (apiKey) headers.Authorization = authHeader;\n const requestHeaders = JSON.stringify(redactHeaders(headers));\n\n const base64 = encodeBase64(imageBytes);\n const mediaType = imageFormat === \"png\" ? \"image/png\" : \"image/jpeg\";\n\n const buildRequestBody = (): string => {\n const reqBody: Record<string, unknown> = {\n model,\n messages: [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: prompt },\n {\n type: \"image_url\",\n image_url: {\n url: `data:${mediaType};base64,${base64}`,\n detail: imageDetail,\n },\n },\n ],\n },\n ],\n };\n if (reasoningEffort !== null) {\n reqBody.reasoning_effort = reasoningEffort;\n }\n return JSON.stringify(reqBody);\n };\n\n const requestBody = buildRequestBody();\n const visionStart = Date.now();\n\n let permit: { release: () => void } | null = null;\n let response: Response;\n try {\n permit = await this.gate.acquire({\n intention: \"vision\",\n weight: this.config.visionWeight,\n signal,\n });\n response = await fetch(target, {\n method: \"POST\",\n headers,\n body: requestBody,\n signal,\n });\n } catch (err) {\n if (permit) permit.release();\n const elapsed = Date.now() - visionStart;\n if (err instanceof DOMException && err.name === \"AbortError\") {\n log.warn(`callVision ABORTED after ${elapsed}ms (client disconnect)`, {\n model,\n target,\n imgSize: imageBytes.byteLength,\n });\n return {\n description: failurePlaceholder(\"aborted\", \"client disconnected\"),\n status: \"aborted\",\n httpStatus: null,\n error: \"client disconnected\",\n requestBody,\n requestHeaders,\n responseBody: \"\",\n responseHeaders: \"{}\",\n usage: null,\n };\n }\n const errMsg = err instanceof Error ? err.message : String(err);\n log.error(`callVision FETCH ERROR after ${elapsed}ms: ${errMsg}`, { model, target });\n return {\n description: failurePlaceholder(\"generic\", \"vision fetch failed\"),\n status: \"fetch_error\",\n httpStatus: null,\n error: errMsg,\n requestBody,\n requestHeaders,\n responseBody: \"\",\n responseHeaders: \"{}\",\n usage: null,\n };\n }\n permit.release();\n\n const elapsed = Date.now() - visionStart;\n const resHeadersJson = JSON.stringify(headersToObject(response.headers));\n const rawBody = await response.text().catch(() => \"\");\n if (!response.ok) {\n log.error(`callVision HTTP ${response.status} after ${elapsed}ms`, {\n model,\n target,\n imgSize: imageBytes.byteLength,\n body: rawBody.slice(0, 300),\n });\n return {\n description: failurePlaceholder(\"http_status\", String(response.status)),\n status: \"http_error\",\n httpStatus: response.status,\n error: rawBody.slice(0, 500) || `HTTP ${response.status}`,\n requestBody,\n requestHeaders,\n responseBody: rawBody,\n responseHeaders: resHeadersJson,\n usage: null,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawBody);\n } catch {\n log.error(`callVision PARSE ERROR after ${elapsed}ms — response not valid JSON`, { model });\n return {\n description: failurePlaceholder(\"parse\", \"\"),\n status: \"parse_error\",\n httpStatus: response.status,\n error: \"response not valid JSON\",\n requestBody,\n requestHeaders,\n responseBody: rawBody,\n responseHeaders: resHeadersJson,\n usage: null,\n };\n }\n\n const text = extractOpenAIContent(parsed);\n if (!text) {\n log.error(\n `callVision EMPTY response after ${elapsed}ms — choices[0].message.content missing or empty`,\n { model },\n );\n return {\n description: failurePlaceholder(\"empty\", \"\"),\n status: \"empty\",\n httpStatus: response.status,\n error: \"choices[0].message.content missing or empty\",\n requestBody,\n requestHeaders,\n responseBody: rawBody,\n responseHeaders: resHeadersJson,\n usage: null,\n };\n }\n log.info(`callVision OK in ${elapsed}ms`, {\n model,\n imgSize: imageBytes.byteLength,\n descLen: text.length,\n });\n const usage = extractOpenAiNonStreaming(parsed, elapsed);\n return {\n description: text,\n status: \"ok\",\n httpStatus: response.status,\n error: null,\n requestBody,\n requestHeaders,\n responseBody: rawBody,\n responseHeaders: resHeadersJson,\n usage,\n };\n }\n}\n\n/**\n * Replace image blocks in `body` (already cloned) with text blocks carrying\n * the wrapped descriptions. OpenAI image_url parts and Anthropic image blocks\n * are both replaced in-place; overflow placeholders are appended after the\n * last replaced block of the relevant message so the model still sees them\n * in conversation order.\n */\nfunction replaceImageBlocks(\n body: unknown,\n apiKind: ApiKind,\n descriptions: string[],\n overflow: string[],\n): void {\n if (typeof body !== \"object\" || body === null) return;\n const messages = (body as { messages?: unknown }).messages;\n if (!Array.isArray(messages)) return;\n\n const cursor = { descIdx: 0, overflowIdx: 0 };\n const overflowText = overflow.length > 0 ? overflow.join(\"\\n\") : \"\";\n\n for (const msg of messages) {\n if (typeof msg !== \"object\" || msg === null) continue;\n const content = (msg as { content?: unknown }).content;\n if (!Array.isArray(content)) continue;\n replaceInContentArray(content, apiKind, descriptions, overflow, cursor);\n\n if (\n overflowText &&\n cursor.descIdx >= descriptions.length &&\n cursor.overflowIdx < overflow.length\n ) {\n content.push({ type: \"text\", text: overflowText });\n cursor.overflowIdx = overflow.length;\n }\n }\n}\n\nfunction replaceInContentArray(\n content: unknown[],\n apiKind: ApiKind,\n descriptions: string[],\n overflow: string[],\n cursor: { descIdx: number; overflowIdx: number },\n): void {\n for (let i = 0; i < content.length; i++) {\n const part = content[i];\n if (typeof part !== \"object\" || part === null) continue;\n const p = part as { type?: unknown; content?: unknown };\n\n if (apiKind === \"openai\" && p.type === \"image_url\") {\n if (cursor.descIdx < descriptions.length) {\n content[i] = { type: \"text\", text: descriptions[cursor.descIdx] };\n cursor.descIdx++;\n }\n } else if (apiKind === \"anthropic\" && p.type === \"image\") {\n if (cursor.descIdx < descriptions.length) {\n content[i] = {\n type: \"text\",\n text: descriptions[cursor.descIdx],\n cache_control: { type: \"ephemeral\" },\n };\n cursor.descIdx++;\n }\n } else if (apiKind === \"anthropic\" && p.type === \"tool_result\" && Array.isArray(p.content)) {\n replaceInContentArray(p.content, apiKind, descriptions, overflow, cursor);\n }\n }\n}\n\n/** Deep-clone a body via JSON round-trip. Falls back to the original on failure. */\nfunction cloneBody(body: unknown): unknown {\n try {\n return JSON.parse(JSON.stringify(body));\n } catch {\n return body;\n }\n}\n\n/**\n * Decode a base64 string to bytes. Returns null on invalid input rather than\n * throwing — callers fail-open with a placeholder.\n */\nfunction decodeBase64(data: string): Uint8Array | null {\n try {\n return new Uint8Array(Buffer.from(data, \"base64\"));\n } catch {\n return null;\n }\n}\n\n/** Encode bytes to a base64 string. */\nfunction encodeBase64(bytes: Uint8Array): string {\n return Buffer.from(bytes).toString(\"base64\");\n}\n\n/** Fast non-cryptographic hash for dedup identification in vision call records. */\nfunction simpleHash(bytes: Uint8Array): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < bytes.length; i++) {\n h ^= bytes[i];\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Extract the assistant message text from an OpenAI chat-completions response.\n * Returns the empty string when the shape is unexpected.\n */\nfunction extractOpenAIContent(parsed: unknown): string {\n if (typeof parsed !== \"object\" || parsed === null) return \"\";\n const choices = (parsed as { choices?: unknown }).choices;\n if (!Array.isArray(choices) || choices.length === 0) return \"\";\n const first = choices[0];\n if (typeof first !== \"object\" || first === null) return \"\";\n const message = (first as { message?: { content?: unknown } }).message;\n if (!message) return \"\";\n const content = message.content;\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n for (const part of content) {\n if (typeof part !== \"object\" || part === null) continue;\n const t = (part as { type?: unknown; text?: unknown }).type;\n const txt = (part as { text?: unknown }).text;\n if (t === \"text\" && typeof txt === \"string\") return txt;\n }\n }\n // Never fall back to reasoning_content — it is the model's internal chain-of-thought,\n // not the actual image description. An empty return triggers a failure placeholder.\n return \"\";\n}\n","import type { CaptureDB } from \"../db.js\";\n\nexport interface PersistentDescriptionEntry {\n key: string;\n description: string;\n imageHash: string;\n model: string;\n promptVersion: number;\n}\n\ninterface PendingWrite {\n entry: PersistentDescriptionEntry;\n now: number;\n}\n\n/**\n * SQLite-backed description store with write-behind batching.\n *\n * Writes are buffered and flushed in batches on a timer (same pattern as\n * `queue.ts` WriteQueue) to avoid blocking the request path with synchronous\n * SQLite writes. Reads check the pending buffer first, then SQLite.\n *\n * WAL mode + single-threaded JS event loop means no lock contention between\n * reads and batched writes — the write-behind queue exists to keep the\n * request path fast, not to avoid lock contention (WAL already handles that).\n */\nexport class PersistentDescriptionStore {\n private readonly db: CaptureDB;\n private readonly ttlMs: number;\n private readonly maxRows: number;\n private lastEvictionAt = 0;\n private static readonly EVICTION_INTERVAL_MS = 60_000;\n\n private readonly pendingWrites: PendingWrite[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly flushIntervalMs: number;\n private readonly flushBatch: number;\n private closed = false;\n\n constructor(db: CaptureDB, ttlMs: number, maxRows: number, flushBatch = 50) {\n this.db = db;\n this.ttlMs = ttlMs;\n this.maxRows = maxRows;\n this.flushIntervalMs = 500;\n this.flushBatch = flushBatch;\n }\n\n get(key: string): string | null {\n for (let i = this.pendingWrites.length - 1; i >= 0; i--) {\n if (this.pendingWrites[i].entry.key === key) {\n const pw = this.pendingWrites[i];\n if (Date.now() - pw.now > this.ttlMs) return null;\n return pw.entry.description;\n }\n }\n const row = this.db.getVisionDescription(key);\n if (!row) return null;\n const now = Date.now();\n if (now - row.created_at > this.ttlMs) {\n this.db.deleteVisionDescription(key);\n return null;\n }\n return row.description;\n }\n\n set(entry: PersistentDescriptionEntry): void {\n const now = Date.now();\n this.pendingWrites.push({ entry, now });\n if (this.pendingWrites.length >= this.flushBatch) {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n this.flushNow();\n } else if (!this.flushTimer) {\n this.flushTimer = setTimeout(() => {\n try {\n this.flushNow();\n } catch {\n // DB may be closed during shutdown; pending writes are lost\n }\n }, this.flushIntervalMs);\n }\n this.maybeEvict(now);\n }\n\n flushNow(): void {\n this.flushTimer = null;\n if (this.closed || this.pendingWrites.length === 0) return;\n const batch = this.pendingWrites.splice(0, this.pendingWrites.length);\n for (const pw of batch) {\n this.db.upsertVisionDescription({\n $key: pw.entry.key,\n $image_hash: pw.entry.imageHash,\n $model: pw.entry.model,\n $prompt_version: pw.entry.promptVersion,\n $description: pw.entry.description,\n $now: pw.now,\n });\n }\n }\n\n close(): void {\n this.closed = true;\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n this.flushNow();\n }\n\n warmIntoCache(onEntry: (key: string, description: string) => void, limit: number): number {\n this.flushNow();\n const cutoff = Date.now() - this.ttlMs;\n const rows = this.db.listVisionDescriptionsForWarming(limit, cutoff);\n let count = 0;\n for (const row of rows) {\n onEntry(row.key, row.description);\n count++;\n }\n return count;\n }\n\n private maybeEvict(now: number): void {\n if (now - this.lastEvictionAt < PersistentDescriptionStore.EVICTION_INTERVAL_MS) return;\n this.lastEvictionAt = now;\n const cutoff = now - this.ttlMs;\n this.db.evictVisionDescriptions(cutoff, this.maxRows);\n }\n}\n","// Vision record sinks.\n//\n// Decouples VisionHandoff.addRecord() from the concrete persistence (CaptureDB)\n// and broadcast (WsBroadcaster) side effects. A sink receives a fully-built\n// VisionRecord and performs its side effect; error isolation is the\n// CompositeVisionSink's responsibility so a DB failure never prevents a WS\n// broadcast (and vice versa).\n\nimport { type CaptureDB, flattenUsage } from \"../db.js\";\nimport type { CaptureSummary, ProtocolConfig, WsMessage } from \"../types.js\";\nimport type { UsageMetrics } from \"../usage/types.js\";\nimport type { WsBroadcaster } from \"../ws.js\";\nimport type { VisionCallRecord } from \"./handoff.js\";\n\ninterface SinkRecordParts {\n metaJson: string;\n reqBody: string;\n resBody: string;\n reqHeaders: string;\n resHeaders: string;\n path: string;\n url: string;\n status: number | null;\n startedAt: number;\n}\n\nfunction buildSinkRecord(record: VisionRecord, opts?: { includeUrl?: boolean }): SinkRecordParts {\n const { rec, httpExchange } = record;\n const metaJson = JSON.stringify({\n status: rec.status,\n httpStatus: rec.httpStatus,\n latencyMs: rec.latencyMs,\n description: rec.description,\n error: rec.error,\n imageHash: rec.imageHash,\n imageSize: rec.imageSize,\n model: rec.model,\n target: rec.target,\n });\n const reqBody =\n httpExchange?.requestBody ??\n JSON.stringify({\n model: rec.model,\n target: rec.target,\n imageSize: rec.imageSize,\n imageHash: rec.imageHash,\n });\n const resBody = httpExchange?.responseBody ?? metaJson;\n const reqHeaders = httpExchange?.requestHeaders ?? \"{}\";\n const resHeaders = httpExchange?.responseHeaders ?? \"{}\";\n\n let path = \"/v1/chat/completions\";\n let url = \"\";\n if (rec.target) {\n try {\n const u = new URL(rec.target);\n path = u.pathname + u.search;\n if (opts?.includeUrl) {\n url = rec.target;\n }\n } catch {\n path = rec.target;\n if (opts?.includeUrl) {\n url = rec.target;\n }\n }\n }\n\n const status = rec.status === \"ok\" || rec.status === \"cache_hit\" ? 200 : (rec.httpStatus ?? null);\n const startedAt = rec.timestamp - Math.max(0, rec.latencyMs);\n\n return {\n metaJson,\n reqBody,\n resBody,\n reqHeaders,\n resHeaders,\n path,\n url,\n status,\n startedAt,\n };\n}\n\n/** Raw HTTP exchange captured alongside a vision call (bodies + headers). */\nexport interface VisionHttpExchange {\n requestBody: string;\n requestHeaders: string;\n responseBody: string;\n responseHeaders: string;\n}\n\n/**\n * Everything a sink needs to persist a vision capture and broadcast it.\n * Bundles the call record, the optional HTTP exchange, and parsed usage.\n */\nexport interface VisionRecord {\n /** The assigned vision call record (id + timestamp included). */\n rec: VisionCallRecord;\n /** Raw HTTP exchange if available; sinks fall back to synthesized bodies. */\n httpExchange?: VisionHttpExchange;\n /** Parsed usage metrics for token accounting (null if absent). */\n usage: UsageMetrics | null;\n /** CaptureDB row id when the row has already been inserted (lifecycle path). */\n dbId?: number;\n}\n\n/**\n * Receives a completed vision record for persistence and/or broadcast.\n * Implementations must not throw — wrap errors internally if they cannot\n * propagate cleanly, because {@link CompositeVisionSink} relies on the\n * \"one sink throwing must not break the others\" contract at the composite\n * boundary (it also isolates, but sinks shouldn't depend on that).\n */\nexport interface VisionRecordSink {\n record(record: VisionRecord): void;\n}\n\n/**\n * Fan-out sink: forwards the record to each child sink, isolating errors so\n * a throw in one sink never prevents the remaining sinks from running.\n *\n * This is the default sink wired in src/index.ts: CompositeVisionSink([\n * new DbVisionSink(db),\n * new WsBroadcastVisionSink(ws),\n * ]).\n */\nexport class CompositeVisionSink implements VisionRecordSink {\n private readonly sinks: readonly VisionRecordSink[];\n\n constructor(sinks: readonly VisionRecordSink[]) {\n this.sinks = sinks;\n }\n\n record(record: VisionRecord): void {\n for (const sink of this.sinks) {\n try {\n sink.record(record);\n } catch {\n // Error isolation: one sink failing must not break the others.\n // Intentionally swallowed — each sink owns its own logging if needed.\n }\n }\n }\n}\n\n/**\n * Persists a vision record into the captures table via CaptureDB.\n * Reproduces the exact insert params the fused VisionHandoff.addRecord()\n * previously built inline.\n */\nexport class DbVisionSink implements VisionRecordSink {\n constructor(\n private readonly db: CaptureDB,\n private readonly protocolConfig: ProtocolConfig,\n ) {}\n\n record(record: VisionRecord): void {\n if (record.dbId !== undefined) return;\n const { rec, usage } = record;\n const parts = buildSinkRecord(record, { includeUrl: true });\n\n record.dbId = this.db.insertVisionCapture({\n $method: \"POST\",\n $path: parts.path,\n $url: parts.url,\n $rh: parts.reqHeaders,\n $rb: parts.reqBody,\n $rs: parts.reqBody.length,\n $status: parts.status,\n $rh2: parts.resHeaders,\n $rb2: parts.resBody,\n $rs2: parts.resBody.length,\n $ct: \"application/json\",\n $dur: rec.latencyMs,\n $state: rec.state,\n $started_at: parts.startedAt,\n $finished_at: rec.timestamp,\n $inp: this.protocolConfig.incomingProtocol,\n $outp: this.protocolConfig.upstreamProtocol,\n $model: rec.model,\n $parent_capture_id: rec.captureId ?? null,\n $vision_meta: parts.metaJson,\n ...flattenUsage(usage),\n });\n }\n}\n\n/**\n * Broadcasts a vision record as a \"new capture\" WS message.\n * Reproduces the exact CaptureSummary the fused VisionHandoff.addRecord()\n * previously built inline.\n */\nexport class WsBroadcastVisionSink implements VisionRecordSink {\n constructor(\n private readonly ws: WsBroadcaster,\n private readonly protocolConfig: ProtocolConfig,\n ) {}\n\n record(record: VisionRecord): void {\n const { rec, usage } = record;\n const parts = buildSinkRecord(record);\n const msg: WsMessage = {\n type: record.dbId ? \"update\" : \"new\",\n capture: {\n id: record.dbId ?? rec.id,\n method: \"POST\",\n path: parts.path,\n response_status: parts.status,\n is_sse: false,\n content_type: \"application/json\",\n request_size: parts.reqBody.length,\n response_size: parts.resBody.length,\n duration_ms: rec.latencyMs,\n state: rec.state,\n started_at: parts.startedAt,\n finished_at: rec.timestamp,\n incoming_protocol: this.protocolConfig.incomingProtocol,\n upstream_protocol: this.protocolConfig.upstreamProtocol,\n model: rec.model,\n usage_missing: usage ? usage.usage_missing : null,\n ttft_ms: usage?.ttft_ms ?? null,\n tps: usage?.tps ?? null,\n cache_creation_tokens: usage?.cache_creation_tokens ?? null,\n cache_read_tokens: usage?.cache_read_tokens ?? null,\n total_input_tokens: usage?.total_input_tokens ?? null,\n output_tokens: usage?.output_tokens ?? null,\n total_output_tokens: usage?.total_output_tokens ?? null,\n is_vision: true,\n status_source: \"upstream\",\n gate_reason: null,\n } satisfies CaptureSummary,\n };\n this.ws.broadcast(msg);\n }\n}\n","// Upstream connection warmer — prevents TLS handshake overhead on the first\n// request after startup or extended idle (saves ~750ms cold-start latency).\n// Pings a lightweight upstream endpoint on a fixed interval, but skips\n// the ping whenever the proxy handled real traffic in the last interval.\n\nimport { createLogger } from \"./logger.js\";\nimport type { ProxyConfig, UpstreamProtocol } from \"./types.js\";\n\nconst log = createLogger(\"warmer\");\n\nexport class ConnectionWarmer {\n private readonly target: string;\n private readonly intervalMs: number;\n private readonly path: string;\n private readonly upstreamProtocol: UpstreamProtocol;\n private timer: ReturnType<typeof setInterval> | null = null;\n private lastTrafficAt = 0;\n private warmedOnce = false;\n\n constructor(\n config: Pick<ProxyConfig, \"target\" | \"warmerIntervalMs\" | \"warmerPath\" | \"upstreamProtocol\">,\n ) {\n this.target = config.target;\n this.intervalMs = config.warmerIntervalMs;\n this.path = config.warmerPath;\n this.upstreamProtocol = config.upstreamProtocol;\n }\n\n start(): void {\n if (this.timer || this.intervalMs <= 0) return;\n void this.ping();\n this.timer = setInterval(() => void this.ping(), this.intervalMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Called by the proxy on every upstream request — connection is already warm. */\n notifyTraffic(): void {\n this.lastTrafficAt = Date.now();\n }\n\n private async ping(): Promise<void> {\n if (Date.now() - this.lastTrafficAt < this.intervalMs) return;\n const url = `${this.target}${this.path}`;\n try {\n const res = await fetch(url, {\n method: \"GET\",\n headers: { \"accept-encoding\": \"identity\" },\n protocol: this.upstreamProtocol as unknown as never,\n });\n if (!this.warmedOnce && res.ok) {\n this.warmedOnce = true;\n log.info(`upstream warm: ${url} (${res.status})`);\n }\n } catch {\n // Silent — warmer is best-effort. Next interval will retry.\n }\n }\n}\n","import type { UpdateParams } from \"../db.js\";\nimport { createLogger } from \"../logger.js\";\nimport type { CaptureStore } from \"../queue.js\";\n\nconst logger = createLogger(\"worker-store\");\n\nconst ACK_TIMEOUT_MS = 10_000;\n\ninterface PendingBatch {\n resolve: () => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n itemCount: number;\n}\n\nexport class WorkerCaptureStore implements CaptureStore {\n private worker: Worker;\n private dbPath: string;\n private compressionEnabled: boolean;\n private nextBatchId = 1;\n private pending = new Map<number, PendingBatch>();\n\n constructor(dbPath: string, compressionEnabled: boolean) {\n this.dbPath = dbPath;\n this.compressionEnabled = compressionEnabled;\n this.worker = new Worker(new URL(\"./write-worker.ts\", import.meta.url));\n this.worker.onmessage = (e: MessageEvent) => {\n const msg = e.data as { type: string; batchId?: number; count?: number; error?: string };\n if (!msg.batchId) return;\n const pending = this.pending.get(msg.batchId);\n if (!pending) return;\n clearTimeout(pending.timer);\n this.pending.delete(msg.batchId);\n if (msg.type === \"error\") {\n logger.error(\"worker batch write failed\", { error: msg.error, count: msg.count });\n pending.reject(new Error(msg.error ?? \"unknown worker error\"));\n } else {\n pending.resolve();\n }\n };\n this.worker.onerror = (e: ErrorEvent) => {\n logger.error(\"worker error\", { message: e.message });\n for (const [id, pending] of this.pending) {\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.reject(new Error(`worker crashed: ${e.message}`));\n }\n };\n }\n\n updateCapture(_params: UpdateParams): void {\n throw new Error(\n \"updateCapture should not be called on WorkerCaptureStore — use the main db for single updates\",\n );\n }\n\n batchUpdate(items: Array<{ id: number; res: Omit<UpdateParams, \"$id\"> }>): Promise<void> {\n const batchId = this.nextBatchId++;\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(batchId);\n logger.error(\"worker ack timeout\", { batchId, count: items.length });\n reject(new Error(`worker ack timeout for batch ${batchId}`));\n }, ACK_TIMEOUT_MS);\n this.pending.set(batchId, { resolve, reject, timer, itemCount: items.length });\n this.worker.postMessage({\n type: \"batch\",\n batchId,\n dbPath: this.dbPath,\n items,\n compressionEnabled: this.compressionEnabled,\n });\n });\n }\n\n async close(): Promise<void> {\n this.worker.postMessage({ type: \"close\" });\n const drainDeadline = Date.now() + ACK_TIMEOUT_MS;\n while (this.pending.size > 0 && Date.now() < drainDeadline) {\n await Bun.sleep(50);\n }\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timer);\n pending.reject(new Error(\"worker closed before ack\"));\n }\n this.pending.clear();\n this.worker.terminate();\n }\n}\n","// WebSocket broadcast manager.\n// Tracks connected inspector clients and fans out capture updates.\n\nimport type { WsMessage } from \"./types.js\";\n\nexport type { WsMessage };\n\n/** A Bun WebSocket server instance. */\nexport interface BunServerWebSocket {\n send(data: string): number;\n close(code?: number, reason?: string): void;\n readyState: number;\n}\n\n/**\n * Manages the set of connected WebSocket clients and broadcasts messages.\n * Decoupled from the server lifecycle so tests can stub it.\n */\nexport class WsBroadcaster {\n private clients = new Set<BunServerWebSocket>();\n\n /** Add a client to the broadcast set. */\n add(ws: BunServerWebSocket): void {\n this.clients.add(ws);\n }\n\n /** Remove a client from the broadcast set. */\n remove(ws: BunServerWebSocket): void {\n this.clients.delete(ws);\n }\n\n /** Broadcast a message to all connected clients. Swallows per-client errors. */\n broadcast(msg: WsMessage): void {\n const s = JSON.stringify(msg);\n const dead: BunServerWebSocket[] = [];\n for (const ws of this.clients) {\n try {\n const sent = ws.send(s);\n if (sent === 0) {\n dead.push(ws);\n try {\n ws.close(1013, \"backpressure\");\n } catch {\n // Ignore close failures on already-closed sockets.\n }\n }\n } catch {\n dead.push(ws);\n }\n }\n for (const ws of dead) {\n this.clients.delete(ws);\n }\n }\n\n /** Current number of connected clients. */\n get size(): number {\n return this.clients.size;\n }\n}\n"],"mappings":"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","names":["existsSync","execSync","existsSync","dirname","join","isCompiledExecutable","isNpmGlobal","log","evType","num","num","metrics","log","out","log","log","log","modelName","log","restart","log","stored","elapsed","log","logger","log","fetch","checkForUpdate","performUpdate","uninstall"]}