theokit 0.59.0 → 0.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/{actions-virtual-module-L6SCJLDI.js → actions-virtual-module-DNOALUTX.js} +5 -5
  2. package/dist/{actions-virtual-module-YQBEDNHN.js → actions-virtual-module-UE5H42GP.js} +4 -4
  3. package/dist/adapters/agent-mount.d.ts +1 -1
  4. package/dist/adapters/agent-mount.js +2 -2
  5. package/dist/adapters/security-headers.d.ts +2 -2
  6. package/dist/adapters/security-headers.js +1 -1
  7. package/dist/{agent-X7RDENTI.js → agent-VWPSEDXD.js} +3 -3
  8. package/dist/{app-typed-client-BE4LZPK2.js → app-typed-client-4KLNKET6.js} +4 -4
  9. package/dist/{app-typed-client-F3YLMV4N.js → app-typed-client-MKTSLES3.js} +5 -5
  10. package/dist/{aws-lambda-VKMYUMP2.js → aws-lambda-2R77AV5F.js} +3 -3
  11. package/dist/{build-H2UDKBV7.js → build-RG54DH3F.js} +5 -5
  12. package/dist/{bun-POLTY2HK.js → bun-MAAG4MKW.js} +3 -3
  13. package/dist/{chunk-CF2DNN5R.js → chunk-45XETXSG.js} +10 -2
  14. package/dist/chunk-45XETXSG.js.map +1 -0
  15. package/dist/{chunk-IRQGAC4L.js → chunk-5GZROLDV.js} +2 -2
  16. package/dist/{chunk-IRQGAC4L.js.map → chunk-5GZROLDV.js.map} +1 -1
  17. package/dist/{chunk-M357ILK5.js → chunk-DMODPX7P.js} +2 -2
  18. package/dist/{chunk-6YRZWT5X.js → chunk-ETKLM6UN.js} +9 -9
  19. package/dist/{chunk-BPYMC7SU.js → chunk-EYRWD2AB.js} +2 -2
  20. package/dist/{chunk-HA35FS7U.js → chunk-FFS7IRZF.js} +2 -2
  21. package/dist/{chunk-IF7CZXPW.js → chunk-GZ5YHD5H.js} +3 -3
  22. package/dist/{chunk-MWPC7EOR.js → chunk-IZTOGKLS.js} +5 -5
  23. package/dist/{chunk-YGL3H3UW.js → chunk-K3DUUEQ5.js} +31 -8
  24. package/dist/chunk-K3DUUEQ5.js.map +1 -0
  25. package/dist/{chunk-GO7UWJ47.js → chunk-KPZ2JSAU.js} +53 -26
  26. package/dist/chunk-KPZ2JSAU.js.map +1 -0
  27. package/dist/{chunk-IJER2SYS.js → chunk-OH32SN46.js} +20 -1
  28. package/dist/{chunk-IJER2SYS.js.map → chunk-OH32SN46.js.map} +1 -1
  29. package/dist/{chunk-4OTIXAUY.js → chunk-TMQ52AZR.js} +24 -1
  30. package/dist/chunk-TMQ52AZR.js.map +1 -0
  31. package/dist/{chunk-E43VJ3E5.js → chunk-VPX3MS5D.js} +2 -2
  32. package/dist/{chunk-RPET332G.js → chunk-VV2VU3LJ.js} +2 -2
  33. package/dist/chunk-VV2VU3LJ.js.map +1 -0
  34. package/dist/{chunk-35QIWOHT.js → chunk-WFIAYPBH.js} +10 -2
  35. package/dist/chunk-WFIAYPBH.js.map +1 -0
  36. package/dist/{chunk-VJRPOUKM.js → chunk-XES7ZE2K.js} +4 -4
  37. package/dist/cli/index.js +9 -9
  38. package/dist/{cloudflare-SIG42OIW.js → cloudflare-F5DNYH4B.js} +3 -3
  39. package/dist/{deno-deploy-LQAYOOPW.js → deno-deploy-47E7GIME.js} +3 -3
  40. package/dist/{dev-SJE2GSAC.js → dev-5ETOFKOP.js} +22 -17
  41. package/dist/dev-5ETOFKOP.js.map +1 -0
  42. package/dist/index.d.ts +7 -0
  43. package/dist/index.js +6 -5
  44. package/dist/index.js.map +1 -1
  45. package/dist/{info-6WJ3JRNE.js → info-2H7I55YN.js} +3 -3
  46. package/dist/{internal-api-W5RURFU7.js → internal-api-3YDGG5FF.js} +5 -5
  47. package/dist/{internal-api-4FBFRQ4G.js → internal-api-UM77XDXN.js} +4 -4
  48. package/dist/{load-config-BKUAMGO5.js → load-config-VMKOV44Y.js} +3 -3
  49. package/dist/{mcp-I5HCW4QF.js → mcp-B42ISIJG.js} +4 -4
  50. package/dist/{netlify-C2IRCZKI.js → netlify-BJQGPMGY.js} +3 -3
  51. package/dist/{openapi-WDX3XX2J.js → openapi-SKKXWFX4.js} +3 -3
  52. package/dist/{preview-PWGO5LUE.js → preview-XP77EPTL.js} +3 -3
  53. package/dist/{provider-resolver-VS61zqsw.d.ts → provider-resolver-1aWi_WSz.d.ts} +17 -0
  54. package/dist/{registry-3LUHO4IB.js → registry-APCN3PSJ.js} +7 -7
  55. package/dist/{routes-A2YLFEYC.js → routes-ECCDRHXZ.js} +3 -3
  56. package/dist/{schema-WKTNTGX6.js → schema-ND7GHPQK.js} +2 -2
  57. package/dist/{security-headers-Ccj7ncNx.d.ts → security-headers-C2cByfJn.d.ts} +5 -0
  58. package/dist/server/agent/index.d.ts +17 -2
  59. package/dist/server/agent/index.js +2 -2
  60. package/dist/server/index.d.ts +3 -3
  61. package/dist/server/index.js +10 -6
  62. package/dist/server/index.js.map +1 -1
  63. package/dist/server/security/index.d.ts +2 -2
  64. package/dist/server/security/index.js +2 -2
  65. package/dist/server/webhook/index.d.ts +9 -4
  66. package/dist/server/webhook/index.js +7 -3
  67. package/dist/{server-boundary-WVWEJNUJ.js → server-boundary-JG53VI6L.js} +5 -5
  68. package/dist/{server-boundary-CJIXOPCF.js → server-boundary-X52AMCKI.js} +4 -4
  69. package/dist/{start-KEWSYVVU.js → start-3FYIYTM7.js} +8 -8
  70. package/dist/{vercel-BMCQKCWE.js → vercel-BEDGUVXX.js} +3 -3
  71. package/dist/vite-plugin/index.js +5 -5
  72. package/dist/{vite-plugin-TU3HQA43.js → vite-plugin-BG5K3L46.js} +8 -8
  73. package/dist/whatsapp-D_Fnl1LX.d.ts +85 -0
  74. package/package.json +3 -3
  75. package/dist/chunk-35QIWOHT.js.map +0 -1
  76. package/dist/chunk-4OTIXAUY.js.map +0 -1
  77. package/dist/chunk-CF2DNN5R.js.map +0 -1
  78. package/dist/chunk-GO7UWJ47.js.map +0 -1
  79. package/dist/chunk-RPET332G.js.map +0 -1
  80. package/dist/chunk-YGL3H3UW.js.map +0 -1
  81. package/dist/dev-SJE2GSAC.js.map +0 -1
  82. package/dist/webhook-types-DGIjjcaI.d.ts +0 -43
  83. /package/dist/{actions-virtual-module-L6SCJLDI.js.map → actions-virtual-module-DNOALUTX.js.map} +0 -0
  84. /package/dist/{actions-virtual-module-YQBEDNHN.js.map → actions-virtual-module-UE5H42GP.js.map} +0 -0
  85. /package/dist/{agent-X7RDENTI.js.map → agent-VWPSEDXD.js.map} +0 -0
  86. /package/dist/{app-typed-client-BE4LZPK2.js.map → app-typed-client-4KLNKET6.js.map} +0 -0
  87. /package/dist/{app-typed-client-F3YLMV4N.js.map → app-typed-client-MKTSLES3.js.map} +0 -0
  88. /package/dist/{aws-lambda-VKMYUMP2.js.map → aws-lambda-2R77AV5F.js.map} +0 -0
  89. /package/dist/{build-H2UDKBV7.js.map → build-RG54DH3F.js.map} +0 -0
  90. /package/dist/{bun-POLTY2HK.js.map → bun-MAAG4MKW.js.map} +0 -0
  91. /package/dist/{chunk-M357ILK5.js.map → chunk-DMODPX7P.js.map} +0 -0
  92. /package/dist/{chunk-6YRZWT5X.js.map → chunk-ETKLM6UN.js.map} +0 -0
  93. /package/dist/{chunk-BPYMC7SU.js.map → chunk-EYRWD2AB.js.map} +0 -0
  94. /package/dist/{chunk-HA35FS7U.js.map → chunk-FFS7IRZF.js.map} +0 -0
  95. /package/dist/{chunk-IF7CZXPW.js.map → chunk-GZ5YHD5H.js.map} +0 -0
  96. /package/dist/{chunk-MWPC7EOR.js.map → chunk-IZTOGKLS.js.map} +0 -0
  97. /package/dist/{chunk-E43VJ3E5.js.map → chunk-VPX3MS5D.js.map} +0 -0
  98. /package/dist/{chunk-VJRPOUKM.js.map → chunk-XES7ZE2K.js.map} +0 -0
  99. /package/dist/{cloudflare-SIG42OIW.js.map → cloudflare-F5DNYH4B.js.map} +0 -0
  100. /package/dist/{deno-deploy-LQAYOOPW.js.map → deno-deploy-47E7GIME.js.map} +0 -0
  101. /package/dist/{info-6WJ3JRNE.js.map → info-2H7I55YN.js.map} +0 -0
  102. /package/dist/{internal-api-W5RURFU7.js.map → internal-api-3YDGG5FF.js.map} +0 -0
  103. /package/dist/{internal-api-4FBFRQ4G.js.map → internal-api-UM77XDXN.js.map} +0 -0
  104. /package/dist/{load-config-BKUAMGO5.js.map → load-config-VMKOV44Y.js.map} +0 -0
  105. /package/dist/{mcp-I5HCW4QF.js.map → mcp-B42ISIJG.js.map} +0 -0
  106. /package/dist/{netlify-C2IRCZKI.js.map → netlify-BJQGPMGY.js.map} +0 -0
  107. /package/dist/{openapi-WDX3XX2J.js.map → openapi-SKKXWFX4.js.map} +0 -0
  108. /package/dist/{preview-PWGO5LUE.js.map → preview-XP77EPTL.js.map} +0 -0
  109. /package/dist/{registry-3LUHO4IB.js.map → registry-APCN3PSJ.js.map} +0 -0
  110. /package/dist/{routes-A2YLFEYC.js.map → routes-ECCDRHXZ.js.map} +0 -0
  111. /package/dist/{schema-WKTNTGX6.js.map → schema-ND7GHPQK.js.map} +0 -0
  112. /package/dist/{server-boundary-WVWEJNUJ.js.map → server-boundary-JG53VI6L.js.map} +0 -0
  113. /package/dist/{server-boundary-CJIXOPCF.js.map → server-boundary-X52AMCKI.js.map} +0 -0
  114. /package/dist/{start-KEWSYVVU.js.map → start-3FYIYTM7.js.map} +0 -0
  115. /package/dist/{vercel-BMCQKCWE.js.map → vercel-BEDGUVXX.js.map} +0 -0
  116. /package/dist/{vite-plugin-TU3HQA43.js.map → vite-plugin-BG5K3L46.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/_internal/process-singleton.ts","../src/server/agent/provider-resolver.ts","../src/server/agent/approval-registry.ts"],"sourcesContent":["/**\n * One object per PROCESS, whatever the bundler did to the module holding it.\n *\n * ## The defect this exists to remove\n *\n * A module-level `let x` gives one instance per **module instance**, and a bundler is free to place\n * the same source in more than one chunk. When it does, each chunk gets its own `x` and any state\n * written through one is invisible through the other.\n *\n * Measured in this repository's own `dist` (usetheokit/theokit#401) by grepping the built chunks for\n * each module's marker: `provider-resolver` lands in two chunks, and `approval-registry` in two.\n * Neither copy is redundant — one of the provider chunks is tree-shaken to `resolveProvider` and\n * does not carry `resetProviderRegistry` at all, which is precisely how the halves diverge: an\n * application calling `registerProvider` — public API, with a documented self-hosting example —\n * mutated the array in one chunk while `theokit start` resolved against the array in the other.\n *\n * The second is worse in kind, because `approval-registry.ts` states the requirement in its own\n * source: *\"the approval a request awaits and the approval the route resolves MUST be the same\n * object, so a single instance per process is not a convenience but a correctness requirement.\"* A\n * chunk boundary can break that silently, and the symptom would be a HITL pause that never resumes.\n *\n * ## Why `globalThis` and not better chunking\n *\n * Chunking is a bundler heuristic that changes between versions and with the shape of the import\n * graph. A fix that depends on tsup continuing to place this module in exactly one chunk is a fix\n * that holds until someone adds an import. `Symbol.for` gives a key that is equal across every\n * module instance in the realm, so identity stops depending on the build at all.\n *\n * @internal\n */\n\n/** Namespaced so a key cannot collide with an unrelated global. */\nconst NAMESPACE = 'theokit.singleton.'\n\n/**\n * The object registered under `key`, creating it with `factory` the first time.\n *\n * `factory` runs at most once per process. A later call with the same key returns what the first\n * call built and never replaces it — replacing it is the same defect wearing a different hat, since\n * two callers would again hold different objects.\n */\nexport function processSingleton<T extends object>(key: string, factory: () => T): T {\n const slot = Symbol.for(NAMESPACE + key)\n const host = globalThis as unknown as Record<symbol, T | undefined>\n\n const existing = host[slot]\n if (existing !== undefined) return existing\n\n const created = factory()\n host[slot] = created\n return created\n}\n","import { processSingleton } from '../_internal/process-singleton.js'\n\n/**\n * Provider Resolver — Strategy + Registry pattern (FAANG-grade).\n *\n * Inspiration: Dapr Conversation Registry (`dapr/pkg/components/conversation/registry.go`)\n * + Encore Manager provider array (`encore/runtimes/go/pubsub/manager_internal.go`).\n *\n * Principle: provider routing is the FRAMEWORK's responsibility, not the consumer's — but the\n * consumer still gets to SAY which provider it wants, and it says so in the model id.\n *\n * `provider/model` is the convention every agent in this ecosystem already writes\n * (`anthropic/claude-sonnet-4-6`, `openrouter/anthropic/claude-haiku-4.5`), and it is the one the\n * SDK routes on. Until theokit#326 this resolver ignored it entirely and picked by env-var\n * priority, so an agent that declared `anthropic/...` was handed an OpenRouter key whenever one\n * happened to be present — and every turn failed with `auth_failed (HTTP 401)` against a provider\n * nobody asked for. The declared provider now wins; priority is the fallback for a bare model id.\n *\n * Wire protocol: OpenAI Chat Completions (universal — implemented by every\n * os providers: OpenRouter, Groq, Mistral, Together, Anthropic via proxy, etc).\n *\n * Resolution by priority (FIRST match wins):\n * 1. OPENROUTER_API_KEY → baseUrl=openrouter.ai (multi-model gateway)\n * 2. OPENAI_API_KEY → baseUrl=api.openai.com\n * 3. ANTHROPIC_API_KEY → direct Anthropic (Messages API, not OpenAI-compat)\n *\n * Escape hatch: an explicit `options.apiKey` OVERRIDES auto-resolution\n * (the consumer can force a specific provider if it wants to).\n */\n\n/**\n * Provider configuration descriptor — Registry entry shape.\n *\n * @public\n */\nexport interface ProviderDescriptor {\n /** Stable name used internally — not exposed on the wire. */\n name: string\n /**\n * Environment variable that holds the API key for this provider.\n *\n * **Absent means the provider needs no credential** — a model running on the developer's own\n * machine has nothing to authenticate against. Such a provider is reachable ONLY when a model id\n * names it (`ollama/llama3.2`); it never participates in the priority walk, because the walk\n * reads \"this variable is set\" as \"a human configured this provider\", and a keyless entry offers\n * no equivalent signal. Including it would route every bare model id to localhost the moment no\n * cloud key was set (usetheokit/theokit#407).\n */\n envKey?: string\n /** Base URL for the provider's OpenAI-compatible (or native) API. */\n baseUrl: string\n /**\n * Environment variable that OVERRIDES {@link baseUrl} when set.\n *\n * Scoped honestly: every caller inside this framework takes `.apiKey` from the resolved provider\n * and discards `.baseUrl` (`cli/commands/start/handlers.ts`, `vite-plugin/api-middleware.ts`,\n * `cli/commands/agent.ts`), so the endpoint a request actually reaches is chosen by the SDK, from\n * its own profile — which reads `OLLAMA_HOST` itself. This field therefore does not redirect\n * traffic today.\n *\n * It exists so that the `baseUrl` this function REPORTS agrees with where the request goes, for\n * the consumers that read it — `resolveProvider` is public API, and a reported endpoint that\n * contradicts the real one is a debugging trap rather than a harmless inaccuracy.\n */\n baseUrlEnv?: string\n /**\n * True when this provider is a GATEWAY — it serves other providers' catalogs under their own\n * model ids, reachable by prefixing its name (`openrouter/openai/gpt-4o-mini`).\n *\n * It changes nothing about resolution. It exists so the refusal below can name a model id that\n * would work instead of only the variable that is missing (#554): a scaffold shipping\n * `openai/gpt-4o-mini` and an `.env.example` asking for `OPENROUTER_API_KEY` produced an error\n * naming `OPENAI_API_KEY` — the one key the reader was never told to get — while the key they had\n * serves that exact id through the gateway.\n *\n * Deliberately NOT a licence to substitute. Every caller here takes `.apiKey` and discards\n * `.baseUrl`; the SDK picks the endpoint from the model id (measured: `openai/gpt-4o-mini` →\n * `api.openai.com`, `openrouter/openai/gpt-4o-mini` → `openrouter.ai`). Handing an OpenRouter key\n * to a request bound for OpenAI is #326 exactly, so the caller changes the id and the routing\n * follows — the framework does not change it for them.\n */\n gateway?: boolean\n /** Resolution priority (lower = higher priority). FIRST match wins. */\n priority: number\n}\n\n/**\n * Resolved provider configuration — output of `resolveProvider()`.\n *\n * @public\n */\nexport interface ResolvedProvider {\n name: string\n apiKey: string\n baseUrl: string\n}\n\n/**\n * Default provider registry. Order = priority (first = highest).\n *\n * Adding a new provider:\n * 1. Append entry below (or register via `registerProvider()`).\n * 2. Set `envKey` matching the user's env var convention.\n * 3. Set `baseUrl` to the OpenAI-compat endpoint (or native if not compat).\n * 4. Provider name used in telemetry/logs only — never wire-exposed.\n */\nconst DEFAULT_REGISTRY: ProviderDescriptor[] = [\n {\n name: 'openrouter',\n envKey: 'OPENROUTER_API_KEY',\n baseUrl: 'https://openrouter.ai/api/v1',\n gateway: true,\n priority: 1,\n },\n {\n name: 'openai',\n envKey: 'OPENAI_API_KEY',\n baseUrl: 'https://api.openai.com/v1',\n priority: 2,\n },\n {\n name: 'anthropic',\n envKey: 'ANTHROPIC_API_KEY',\n baseUrl: 'https://api.anthropic.com',\n priority: 3,\n },\n {\n // Mirrors the profile `@theokit/sdk` already ships for this provider — same default host, same\n // override variable — so the two cannot disagree about where ollama is listening. The SDK is\n // what dials it (see `baseUrlEnv`); this entry's job is to stop the resolver from rejecting the\n // model before the SDK is ever reached. No `envKey`: see that field's doc comment for why the\n // absence also keeps this entry out of the priority walk.\n name: 'ollama',\n baseUrl: 'http://localhost:11434',\n baseUrlEnv: 'OLLAMA_HOST',\n priority: 100,\n },\n]\n\n/**\n * Runtime registry — copy of DEFAULT_REGISTRY mutable via registerProvider().\n * Sorted by priority on every resolve (stable, O(n log n) — n <= ~10 providers).\n *\n * Held per PROCESS rather than per module instance (usetheokit/theokit#401). As a module-level\n * `const` it was one array per chunk, and the bundler emits this module into two of them: an\n * application calling `registerProvider` mutated one array while `theokit start` resolved against\n * another, so the call had no effect and the resulting error listed the defaults while the\n * registered provider sat in an object nobody read.\n */\nfunction registryOf(): ProviderDescriptor[] {\n return processSingleton('provider-registry', () => [...DEFAULT_REGISTRY])\n}\n\n/**\n * Providers already announced, so a boot line does not become a per-request line.\n *\n * theokit#326 — resolution was silent on success. `resolveProvider` returns `{ name, apiKey,\n * baseUrl }` and every call site consumes only the key, so an operator could only learn which\n * provider was selected from an error message: exactly when it has already failed, and never in\n * the case that hurts most — a stale key that resolves cleanly and 401s at the provider.\n */\nconst announced = new Set<string>()\n\n/** Test-only reset, so announcement state does not leak between cases. @public */\nexport function resetProviderAnnouncements(): void {\n announced.clear()\n}\n\n/** Where the announcement goes, and what it may contain. @public */\nexport interface ResolveOptions {\n /** Receives one line per provider, at most once. NEVER receives the key. */\n announce?: (line: string) => void\n}\n\n/**\n * Register a new provider (Registry pattern — runtime extension point).\n * Useful for self-hosted endpoints or custom providers without touching theokit src.\n *\n * @example\n * registerProvider({\n * name: 'self-hosted',\n * envKey: 'SELF_HOSTED_API_KEY',\n * baseUrl: 'https://llm.internal.acme.com/v1',\n * priority: 0, // highest priority\n * })\n *\n * @public\n */\nexport function registerProvider(descriptor: ProviderDescriptor): void {\n // Idempotent — replace existing by name.\n const registry = registryOf()\n const idx = registry.findIndex((p) => p.name === descriptor.name)\n if (idx >= 0) registry[idx] = descriptor\n else registry.push(descriptor)\n}\n\n/**\n * Reset registry to DEFAULT_REGISTRY (test-only / dev escape hatch).\n *\n * @public\n */\nexport function resetProviderRegistry(): void {\n const registry = registryOf()\n registry.length = 0\n registry.push(...DEFAULT_REGISTRY)\n}\n\n/**\n * Get current registry snapshot (read-only — inspection).\n *\n * @public\n */\nexport function listProviders(): readonly ProviderDescriptor[] {\n return [...registryOf()].sort((a, b) => a.priority - b.priority)\n}\n\n/**\n * Says which provider was selected, once.\n *\n * Carries the provider, HOW it was chosen, and the variable the credential came from — never the\n * credential. Which env var holds it is what an operator needs to fix a mis-selection; the value\n * is what a log must never hold.\n */\nfunction announce(\n desc: ProviderDescriptor,\n how: 'declared by the model' | 'by env priority',\n options: ResolveOptions | undefined,\n): void {\n if (announced.has(desc.name)) return\n announced.add(desc.name)\n const source = desc.envKey ?? 'none (this provider takes no credential)'\n const line = `[theokit] provider=${desc.name} (${how}) source=${source} baseUrl=${baseUrlOf(desc)}`\n if (options?.announce) options.announce(line)\n // `warn` rather than `info` because the repo's lint rule allows only `warn`/`error` — and this\n // line is closer to a warning in spirit anyway: it is the one chance an operator gets to notice\n // that the provider serving their agents is not the one they expected.\n else console.warn(line)\n}\n\n/**\n * Where this provider is listening, after any environment override.\n *\n * One function so the announcement line and the resolved config can never name different hosts —\n * an operator reading a log to find out where requests went must be reading the truth.\n */\nfunction baseUrlOf(desc: ProviderDescriptor): string {\n if (desc.baseUrlEnv === undefined) return desc.baseUrl\n const override = process.env[desc.baseUrlEnv]\n return override !== undefined && override.length > 0 ? override : desc.baseUrl\n}\n\n/**\n * The provider a model id declares, or `undefined` when it declares none.\n *\n * `provider/model` and `gateway/provider/model` both resolve on the FIRST segment: a gateway is a\n * provider from the framework's point of view (it holds the credential), and everything after it\n * is the upstream namespace the gateway itself routes on.\n *\n * A bare id (`gpt-4o-mini`, `qwen2.5:3b`) declares nothing, and an unregistered prefix is treated\n * the same way rather than as an error — a project may legitimately point a custom id at a\n * registered provider's endpoint, and refusing it here would break that before it reached the SDK.\n */\nfunction providerOf(\n modelId: string | undefined,\n registered: readonly ProviderDescriptor[],\n): ProviderDescriptor | undefined {\n const prefix = declaredPrefixOf(modelId)\n if (prefix === undefined) return undefined\n return registered.find((p) => p.name === prefix)\n}\n\n/**\n * The provider name a model id declares, registered or not.\n *\n * Split out of {@link providerOf} because the two questions differ exactly where the error message\n * used to go wrong: \"which registered provider is this?\" answers `undefined` both for a bare id and\n * for `groq/…`, and the resolver needs to tell those apart to say something true about the second.\n */\nfunction declaredPrefixOf(modelId: string | undefined): string | undefined {\n if (modelId === undefined) return undefined\n const slash = modelId.indexOf('/')\n if (slash <= 0) return undefined\n return modelId.slice(0, slash)\n}\n\n/**\n * The rest of the refusal: how to reach this model with a credential that IS present.\n *\n * A credentialed gateway serves the declared provider's catalog under the same id, so prefixing it\n * turns an unreachable model into a reachable one — and that string is what the reader needs, not\n * the abstract advice to \"change the prefix\". When no gateway is credentialed the sentence stays\n * exactly as it was: naming a gateway with no key would trade one dead end for another.\n *\n * A gateway is never offered for a model already routed through one — `openrouter/openrouter/…` is\n * not a thing — which is what excluding `declared` itself accomplishes.\n */\nfunction gatewayHint(\n declared: ProviderDescriptor,\n modelId: string,\n registered: readonly ProviderDescriptor[],\n): string {\n const gateway = registered.find(\n (p) =>\n p.gateway === true &&\n p.name !== declared.name &&\n p.envKey !== undefined &&\n (process.env[p.envKey] ?? '').length > 0,\n )\n if (gateway?.envKey === undefined) return `, or change the model's provider prefix`\n return (\n `, or use \"${gateway.name}/${modelId}\" to route it through ${gateway.name} ` +\n `(${gateway.envKey} is set, and ${gateway.name} serves this model id)`\n )\n}\n\n/** A provider that authenticates — the only kind the priority walk may select. */\ntype CredentialedProvider = ProviderDescriptor & { envKey: string }\n\nfunction takesCredential(desc: ProviderDescriptor): desc is CredentialedProvider {\n return desc.envKey !== undefined\n}\n\n/**\n * Resolve the provider for a model.\n *\n * When `modelId` declares a registered provider (`anthropic/…`), THAT provider's key is\n * required — no substitution. Otherwise the registry is walked by priority, first match wins.\n *\n * @returns ResolvedProvider with apiKey + baseUrl + name\n * @throws Error if NO provider env var is set (actionable message)\n *\n * @public\n */\n/**\n * What a keyless provider reports as its key.\n *\n * This was `''` until #501, on the reasoning that an empty string is the honest value for a\n * provider that needs no credential. The reasoning was sound and the value was wrong: the SDK's\n * `createLocalAgent` calls `resolveApiKey`, which treats `''` as ABSENT, falls back to\n * `THEOKIT_API_KEY` — which nobody sets for a local model — and throws `missing_api_key` before it\n * ever looks at the provider. So #407 opened the registry door and every keyless provider,\n * including the builtin `ollama`, still could not serve a turn.\n *\n * `'local'` is the SDK's own sentinel (`LOCAL_RUNTIME_MOCK_KEY`, `internal/auth/api-key-validator`).\n * It is not a placeholder credential and does not become one: `validateApiKeyShape` short-circuits\n * on it, and `mergeExplicitApiKey` excludes it from the credential pools, so it never reaches a\n * provider as an `authorization` header. That was the objection the old comment raised against any\n * non-empty value, and it is the reason this specific value is the one that answers it — measured\n * against @theokit/sdk 4.52.1, where `apiKey: ''` throws `missing_api_key` and `apiKey: 'local'`\n * builds the agent.\n */\nconst KEYLESS_API_KEY = 'local'\n\nexport function resolveProvider(modelId?: string, options?: ResolveOptions): ResolvedProvider {\n const sorted = [...registryOf()].sort((a, b) => a.priority - b.priority)\n\n const declared = modelId === undefined ? undefined : providerOf(modelId, sorted)\n if (declared !== undefined && modelId !== undefined) {\n if (declared.envKey === undefined) {\n announce(declared, 'declared by the model', options)\n return { name: declared.name, apiKey: KEYLESS_API_KEY, baseUrl: baseUrlOf(declared) }\n }\n const apiKey = process.env[declared.envKey]\n if (apiKey && apiKey.length > 0) {\n announce(declared, 'declared by the model', options)\n return { name: declared.name, apiKey, baseUrl: baseUrlOf(declared) }\n }\n // Deliberately NOT falling back to another provider's key. Silently substituting one is how\n // theokit#326 produced a 401 nobody could attribute: the request went somewhere the agent\n // never named. Say which variable is missing and stop.\n //\n // #554 — but say it usefully. \"change the model's provider prefix\" is true and unactionable:\n // it does not say which prefix, and the reader may already hold a key that serves this model.\n throw new Error(\n `Model \"${modelId}\" declares provider \"${declared.name}\", but ${declared.envKey} is not set. ` +\n `Set ${declared.envKey}${gatewayHint(declared, modelId, sorted)}.`,\n )\n }\n\n // A prefix the registry does not know is a CHOICE, not a silence — refuse before the walk.\n //\n // `providerOf` returns `undefined` for an unregistered prefix, which is indistinguishable from a\n // bare model id, so the priority walk below used to claim the turn and the \"not registered\" error\n // further down became unreachable for anyone holding a key. The visible cost was a confusing\n // message; the real one was a turn that SUCCEEDED against a provider nobody named — different\n // endpoint, different account billed, prompt delivered to a vendor the operator had routed away\n // from, announced only by a `console.warn` that fires once per process (#503).\n //\n // #326 settled the principle for a registered provider: declared wins, priority is the fallback\n // for a bare model id. This extends it to the case #326 did not reach — an unregistered prefix is\n // not a bare model id either.\n const unregisteredPrefix = declared === undefined ? declaredPrefixOf(modelId) : undefined\n if (unregisteredPrefix !== undefined) {\n throw new Error(\n `Model \"${String(modelId)}\" declares provider \"${unregisteredPrefix}\", which is not registered. ` +\n `Registered providers: ${sorted.map((p) => p.name).join(', ')}. ` +\n `Register it with registerProvider({ name: '${unregisteredPrefix}', … }), or use a registered prefix.`,\n )\n }\n\n // Only providers that take a credential. A keyless entry is reachable exclusively through the\n // declared branch above; see `ProviderDescriptor.envKey` for why the walk cannot include it.\n const credentialed = sorted.filter(takesCredential)\n\n for (const desc of credentialed) {\n const apiKey = process.env[desc.envKey]\n if (apiKey && apiKey.length > 0) {\n announce(desc, 'by env priority', options)\n return {\n name: desc.name,\n apiKey,\n baseUrl: baseUrlOf(desc),\n }\n }\n }\n\n // Nothing resolved, and the model id declared nothing — the unregistered-prefix case was refused\n // above the walk, which is where it belongs (#503). This message is for the bare-id case only.\n //\n // It used to live here and it was the right message in the wrong place: reachable exactly when\n // the operator had no keys at all, which is the case that needed it least (usetheokit/theokit#407\n // is why the message exists; #503 is why it moved).\n const envKeys = credentialed.map((p) => p.envKey).join(' OR ')\n throw new Error(\n `No LLM provider API key found in environment. Set one of: ${envKeys}. ` +\n `Get a free OpenRouter key at https://openrouter.ai/keys (recommended — one key, many models).`,\n )\n}\n\n/**\n * Try to resolve — does NOT throw. Returns null if no provider available.\n * Useful for graceful degradation (e.g., mock mode).\n *\n * @public\n */\nexport function tryResolveProvider(modelId?: string): ResolvedProvider | null {\n try {\n return resolveProvider(modelId)\n } catch {\n return null\n }\n}\n","/**\n * M4 (theokit-ai-first) — the in-process HITL approval registry.\n *\n * The HITL plugin's `awaitApproval` calls `register(approvalId)` and awaits the returned Promise\n * (this is what genuinely PAUSES the SDK run — the SDK `pre_tool_call` hook is awaited). The\n * approve route calls `resolve(approvalId, approved)` to settle it. A per-approval timeout settles\n * the Promise deterministically per the `@HumanInTheLoop` `onTimeout` policy so a hung approval\n * never leaks the paused stream.\n *\n * Single-process contract (ADR 0038 / plan Drawback 2): a multi-instance deploy needs a shared\n * registry — the interface is injectable so a durable impl (Redis, etc.) slots in without touching\n * the harness. We do NOT build a durable store now (YAGNI).\n */\n// The timeout policy vocabulary is owned by the `@HumanInTheLoop` decorator (DRY / G12) — reuse it\n// rather than re-declaring the union, so the two can never drift.\nimport type { TimeoutAction } from '@theokit/agents'\n\nimport { processSingleton } from '../_internal/process-singleton.js'\n\nexport interface RegisterOptions {\n /** Milliseconds before the approval auto-settles per `onTimeout`. */\n timeoutMs: number\n /**\n * What a timeout means. Only `'proceed'` auto-approves; `'abort'` and `'retry'` both deny — the\n * registry does NOT implement retry semantics (a timed-out `'retry'` is a deny, not a re-prompt).\n */\n onTimeout: TimeoutAction\n /** M14 — the gated tool name, surfaced by `list()` (optional; absent for legacy callers). */\n toolName?: string\n /** M14 — the approval question, surfaced by `list()` (optional). */\n question?: string\n /**\n * Who the run belongs to — the `RouteSubject.id` admitted for it (B-016).\n *\n * Absent when the run had no identity to record, which is the `'public'` agent path:\n * `admitAgentRequest` deliberately does not resolve a subject when the policy is absent or\n * `'public'`, so there is nothing to attribute the approval to. Absent therefore means \"no owner\n * was established\", never \"anyone\", and the caller must branch on the difference — a rule that\n * refused when it is absent would start turning public agents away.\n *\n * Deliberately NOT surfaced by `list()`: that listing feeds a UI, and owner ids are identity.\n */\n owner?: string\n /**\n * M20 — an optional JSON-schema descriptor of the custom payload the approver may attach. Carried\n * verbatim into `list()` + the `approval_required` event so the UI knows what to collect. Kept as\n * a plain JSON object (not a live Zod schema) so the registry stays serializable and SDK-free.\n */\n payloadSchema?: Record<string, unknown>\n}\n\n/**\n * M20 — a settled HITL decision. `approved` is the allow/deny bit (backward-compatible with the\n * legacy boolean); `reason` + `payload` are the optional approver-attached extras that surface to\n * the model (on denial, via the veto message) and to the app/UI.\n */\nexport interface ApprovalDecision {\n approved: boolean\n reason?: string\n payload?: unknown\n /**\n * What settled this, when it was not a person (usetheokit/theokit#393).\n *\n * Absent means the decision arrived through `resolve()` — a human, or whatever the application\n * wired to that route. `'timeout'` means the window closed with nobody deciding and `onTimeout`\n * was applied.\n *\n * The distinction is the whole point of a HITL gate: without it an expired approval and an\n * explicit deny are byte-identical on the wire, and the sentence the caller gets — \"denied by\n * human approver\" — asserts a fact that did not happen. It is marked on the ALLOW side too:\n * `onTimeout: 'proceed'` permits the tool BECAUSE nobody answered, and recording that as a plain\n * approval is the same fabrication with the opposite sign.\n *\n * One member rather than `timedOut: boolean`, because the question is what settled it; a second\n * cause (a cancel, a shutdown) joins the union instead of adding another boolean.\n */\n settledBy?: 'timeout'\n}\n\n/** M14 — a pending approval as surfaced by {@link ApprovalRegistry.list}. */\nexport interface PendingApproval {\n approvalId: string\n toolName?: string\n question?: string\n /** Epoch millis when the pending approval auto-settles (registeredAt + timeoutMs). */\n expiresAt: number\n /** M20 — the declared custom-payload schema, if the gated tool declares one. */\n payloadSchema?: Record<string, unknown>\n}\n\nexport interface ApprovalRegistry {\n /**\n * Register a pending approval; the returned Promise settles with the full {@link ApprovalDecision}\n * on `resolve` or timeout (M20 — was a bare boolean pre-M20).\n */\n register(approvalId: string, opts: RegisterOptions): Promise<ApprovalDecision>\n /**\n * Settle a pending approval. Accepts a full {@link ApprovalDecision} OR a bare boolean (coerced to\n * `{ approved }` — backward-compatible). Returns false if the id is unknown or already settled.\n */\n resolve(approvalId: string, decision: boolean | ApprovalDecision): boolean\n /** M14 — list the currently-pending approvals (process-wide; single-process contract). */\n list(): PendingApproval[]\n /**\n * Who owns the pending approval `approvalId`, or `undefined` (B-016).\n *\n * `undefined` covers three cases the caller treats identically — never registered, already\n * settled, and registered without an owner — because in all three there is nothing to compare a\n * caller against.\n */\n ownerOf(approvalId: string): string | undefined\n}\n\ninterface Pending {\n settle: (decision: ApprovalDecision) => void\n timer: ReturnType<typeof setTimeout>\n info: PendingApproval\n /** Held beside `info` rather than inside it, so `list()` cannot leak it (B-016). */\n owner?: string\n}\n\n/**\n * The one process-wide registry the stream mount (`mountAgent`) and the approve route share.\n *\n * The in-process impl holds LIVE Promise resolvers in memory — the approval a request awaits and\n * the approval the route resolves MUST be the same object, so a single instance per process is not\n * a convenience but a correctness requirement. Lazily created; a durable/multi-instance deploy\n * swaps this accessor for a shared-store impl (ADR 0038 / plan Drawback 2) without touching callers.\n * Tests use {@link createInProcessApprovalRegistry} directly — never this singleton.\n */\nexport function getApprovalRegistry(): ApprovalRegistry {\n // Per PROCESS, not per module instance (usetheokit/theokit#401). The paragraph above calls a\n // single instance \"not a convenience but a correctness requirement\", and a module-level `let`\n // does not deliver that: it gives one instance per MODULE INSTANCE, and this module is emitted\n // into two chunks of the published bundle. A run awaiting an approval and the route resolving it\n // could hold different objects, and the symptom would be a HITL pause that never resumes.\n return processSingleton('approval-registry', () => createInProcessApprovalRegistry())\n}\n\nexport function createInProcessApprovalRegistry(): ApprovalRegistry {\n const pending = new Map<string, Pending>()\n\n return {\n register(approvalId, opts) {\n return new Promise<ApprovalDecision>((resolve) => {\n const settle = (decision: ApprovalDecision): void => {\n const entry = pending.get(approvalId)\n if (!entry) return\n clearTimeout(entry.timer)\n pending.delete(approvalId)\n resolve(decision)\n }\n // 'proceed' → allow on timeout; 'abort'/'retry' → deny on timeout.\n const timer = setTimeout(() => {\n settle({\n approved: opts.onTimeout === 'proceed',\n settledBy: 'timeout',\n // Every value in this sentence was already here and none of it used to survive the\n // settle. `onTimeout` is named because all three of its values reach this line and two\n // of them deny — an operator who wrote `'retry'` and got a denial had nothing to read\n // that mentioned retry.\n reason: `no decision within ${String(opts.timeoutMs)} ms; onTimeout: '${opts.onTimeout}' was applied`,\n })\n }, opts.timeoutMs)\n const info: PendingApproval = {\n approvalId,\n toolName: opts.toolName,\n question: opts.question,\n expiresAt: Date.now() + opts.timeoutMs,\n ...(opts.payloadSchema !== undefined ? { payloadSchema: opts.payloadSchema } : {}),\n }\n pending.set(approvalId, {\n settle,\n timer,\n info,\n ...(opts.owner !== undefined ? { owner: opts.owner } : {}),\n })\n })\n },\n resolve(approvalId, decision) {\n const entry = pending.get(approvalId)\n if (!entry) return false\n // M20 — coerce the legacy bare-boolean form to a decision object.\n entry.settle(typeof decision === 'boolean' ? { approved: decision } : decision)\n return true\n },\n list() {\n return [...pending.values()].map((p) => p.info)\n },\n ownerOf(approvalId) {\n // `settle` deletes the entry, so an approval that has been answered or timed out reports no\n // owner — which is what stops a later registration of the same id from inheriting one.\n return pending.get(approvalId)?.owner\n },\n }\n}\n"],"mappings":"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","names":[]}
@@ -25,17 +25,17 @@ import {
25
25
  parseAgentRequestBody,
26
26
  readAgentPolicy,
27
27
  subjectFromContext
28
- } from "./chunk-IF7CZXPW.js";
28
+ } from "./chunk-GZ5YHD5H.js";
29
29
  import {
30
30
  getApprovalRegistry
31
- } from "./chunk-35QIWOHT.js";
31
+ } from "./chunk-WFIAYPBH.js";
32
32
  import {
33
33
  sendError,
34
34
  validateCsrfRequest
35
35
  } from "./chunk-L4TB6FXQ.js";
36
36
  import {
37
37
  generateNonce
38
- } from "./chunk-RPET332G.js";
38
+ } from "./chunk-VV2VU3LJ.js";
39
39
 
40
40
  // src/config/resolve-plugin-specifiers.ts
41
41
  import { isAbsolute, resolve } from "path";
@@ -867,4 +867,4 @@ export {
867
867
  applyNonceToInlineScripts,
868
868
  setupSsrDevMiddleware
869
869
  };
870
- //# sourceMappingURL=chunk-VJRPOUKM.js.map
870
+ //# sourceMappingURL=chunk-XES7ZE2K.js.map
package/dist/cli/index.js CHANGED
@@ -15,12 +15,12 @@ function cliVersion() {
15
15
  // src/cli/index.ts
16
16
  var cli = cac("theokit");
17
17
  cli.command("dev", "Start development server").option("--port <port>", "Port number").action(async (options) => {
18
- const { devCommand } = await import("../dev-SJE2GSAC.js");
18
+ const { devCommand } = await import("../dev-5ETOFKOP.js");
19
19
  await devCommand({ port: options.port ? Number(options.port) : void 0 });
20
20
  });
21
21
  cli.command("build", "Build for production").option("--target <target>", "Deploy target (node, vercel, cloudflare)").action(async (options) => {
22
22
  try {
23
- const { buildCommand } = await import("../build-H2UDKBV7.js");
23
+ const { buildCommand } = await import("../build-RG54DH3F.js");
24
24
  await buildCommand({ target: options.target });
25
25
  } catch (err) {
26
26
  const msg = err instanceof Error ? err.message : String(err);
@@ -32,7 +32,7 @@ cli.command("build", "Build for production").option("--target <target>", "Deploy
32
32
  });
33
33
  cli.command("start", "Start production server").option("--port <port>", "Port number").action(async (options) => {
34
34
  try {
35
- const { startCommand } = await import("../start-KEWSYVVU.js");
35
+ const { startCommand } = await import("../start-3FYIYTM7.js");
36
36
  await startCommand({ port: options.port ? Number(options.port) : void 0 });
37
37
  } catch (err) {
38
38
  const msg = err instanceof Error ? err.message : String(err);
@@ -44,7 +44,7 @@ cli.command("start", "Start production server").option("--port <port>", "Port nu
44
44
  });
45
45
  cli.command("preview", "Build for production, then serve it \u2014 one step (B-030)").option("--port <port>", "Port number").option("--target <target>", "Deploy target (node, vercel, cloudflare)").action(async (options) => {
46
46
  try {
47
- const { previewCommand } = await import("../preview-PWGO5LUE.js");
47
+ const { previewCommand } = await import("../preview-XP77EPTL.js");
48
48
  await previewCommand({
49
49
  port: options.port ? Number(options.port) : void 0,
50
50
  target: options.target
@@ -77,7 +77,7 @@ cli.command(
77
77
  "Run an agent in the terminal (stream + tool calls + approval)"
78
78
  ).action(async (name, message) => {
79
79
  try {
80
- const { agentCommand } = await import("../agent-X7RDENTI.js");
80
+ const { agentCommand } = await import("../agent-VWPSEDXD.js");
81
81
  const { sawError } = await agentCommand(name, message);
82
82
  if (sawError) process.exit(1);
83
83
  } catch (err) {
@@ -108,7 +108,7 @@ cli.command("agent sessions gc", "Collect old transcripts for this project (dry
108
108
  });
109
109
  cli.command("mcp <agent>", "Serve an agent as an MCP server over stdio (for desktop MCP clients)").action(async (agent) => {
110
110
  try {
111
- const { mcpCommand } = await import("../mcp-I5HCW4QF.js");
111
+ const { mcpCommand } = await import("../mcp-B42ISIJG.js");
112
112
  await mcpCommand(agent);
113
113
  } catch (err) {
114
114
  const msg = err instanceof Error ? err.message : String(err);
@@ -120,7 +120,7 @@ cli.command("mcp <agent>", "Serve an agent as an MCP server over stdio (for desk
120
120
  });
121
121
  cli.command("routes", "List all routes, actions, and WebSocket endpoints").action(async () => {
122
122
  try {
123
- const { routesCommand } = await import("../routes-A2YLFEYC.js");
123
+ const { routesCommand } = await import("../routes-ECCDRHXZ.js");
124
124
  await routesCommand();
125
125
  } catch (err) {
126
126
  const msg = err instanceof Error ? err.message : String(err);
@@ -161,7 +161,7 @@ cli.command("add <package>", "Install a known TheoKit adapter or plugin (whiteli
161
161
  await addCommand(pkg);
162
162
  });
163
163
  cli.command("info", "Print environment info (runtime, config, routes)").action(async () => {
164
- const { infoCommand } = await import("../info-6WJ3JRNE.js");
164
+ const { infoCommand } = await import("../info-2H7I55YN.js");
165
165
  await infoCommand();
166
166
  });
167
167
  cli.command("doctor", "Report the RESOLVED state of this installation (never prints a secret)").action(async () => {
@@ -173,7 +173,7 @@ cli.command(
173
173
  "Generate <distDir>/openapi.json from route schemas (opt-in via config.openapi)"
174
174
  ).option("--dry-run", "Print the document to stdout without writing to disk (EC-3)").action(async (options) => {
175
175
  try {
176
- const { openapiCommand } = await import("../openapi-WDX3XX2J.js");
176
+ const { openapiCommand } = await import("../openapi-SKKXWFX4.js");
177
177
  await openapiCommand({ dryRun: Boolean(options.dryRun) });
178
178
  } catch (err) {
179
179
  const msg = err instanceof Error ? err.message : String(err);
@@ -11,14 +11,14 @@ import {
11
11
  deployedTraceFragment,
12
12
  describeDeployedSecurityHeaders,
13
13
  renderSecurityHeadersConfigLiteral
14
- } from "./chunk-E43VJ3E5.js";
14
+ } from "./chunk-VPX3MS5D.js";
15
15
  import {
16
16
  findRootDiv
17
17
  } from "./chunk-3PWQQWT6.js";
18
18
  import {
19
19
  nodeAdapter
20
20
  } from "./chunk-KI4DOTN4.js";
21
- import "./chunk-RPET332G.js";
21
+ import "./chunk-VV2VU3LJ.js";
22
22
  import {
23
23
  assertServicesUnsupported,
24
24
  readManifest
@@ -332,4 +332,4 @@ export {
332
332
  renderCloudflareWorkerEntry,
333
333
  renderWranglerToml
334
334
  };
335
- //# sourceMappingURL=cloudflare-SIG42OIW.js.map
335
+ //# sourceMappingURL=cloudflare-F5DNYH4B.js.map
@@ -11,11 +11,11 @@ import {
11
11
  deployedTraceFragment,
12
12
  describeDeployedSecurityHeaders,
13
13
  renderSecurityHeadersConfigLiteral
14
- } from "./chunk-E43VJ3E5.js";
14
+ } from "./chunk-VPX3MS5D.js";
15
15
  import {
16
16
  nodeAdapter
17
17
  } from "./chunk-KI4DOTN4.js";
18
- import "./chunk-RPET332G.js";
18
+ import "./chunk-VV2VU3LJ.js";
19
19
  import {
20
20
  assertServicesUnsupported,
21
21
  readManifest
@@ -193,4 +193,4 @@ export {
193
193
  denoDeployAdapter,
194
194
  renderDenoEntry
195
195
  };
196
- //# sourceMappingURL=deno-deploy-LQAYOOPW.js.map
196
+ //# sourceMappingURL=deno-deploy-47E7GIME.js.map
@@ -11,16 +11,16 @@ import {
11
11
  } from "./chunk-KTMQUYED.js";
12
12
  import {
13
13
  theoPluginAsync
14
- } from "./chunk-6YRZWT5X.js";
15
- import "./chunk-VJRPOUKM.js";
14
+ } from "./chunk-ETKLM6UN.js";
15
+ import "./chunk-XES7ZE2K.js";
16
16
  import "./chunk-2CVV6CNN.js";
17
17
  import "./chunk-3PWQQWT6.js";
18
18
  import "./chunk-FDOOBTXJ.js";
19
19
  import "./chunk-47MM2JUK.js";
20
- import "./chunk-IF7CZXPW.js";
21
- import "./chunk-35QIWOHT.js";
20
+ import "./chunk-GZ5YHD5H.js";
21
+ import "./chunk-WFIAYPBH.js";
22
22
  import "./chunk-L4TB6FXQ.js";
23
- import "./chunk-RPET332G.js";
23
+ import "./chunk-VV2VU3LJ.js";
24
24
  import "./chunk-5VLP3GQ2.js";
25
25
  import "./chunk-C27VRUVN.js";
26
26
  import "./chunk-7UZZHCRN.js";
@@ -30,12 +30,12 @@ import "./chunk-NO6HA4FG.js";
30
30
  import "./chunk-Z7CJHDLS.js";
31
31
  import {
32
32
  loadConfig
33
- } from "./chunk-HA35FS7U.js";
33
+ } from "./chunk-FFS7IRZF.js";
34
34
  import {
35
35
  loadEnv
36
36
  } from "./chunk-3QPVEJ7D.js";
37
37
  import "./chunk-U5PWXS3J.js";
38
- import "./chunk-4OTIXAUY.js";
38
+ import "./chunk-TMQ52AZR.js";
39
39
  import {
40
40
  orchestrateDev
41
41
  } from "./chunk-ZNZISJDZ.js";
@@ -46,6 +46,18 @@ import "./chunk-VQIUTCK2.js";
46
46
  // src/cli/commands/dev.ts
47
47
  import react from "@vitejs/plugin-react";
48
48
  import { createServer } from "vite";
49
+ function buildDevServerOptions(config, port) {
50
+ return {
51
+ port,
52
+ host: config.host,
53
+ open: config.open,
54
+ strictPort: config.strictPort,
55
+ ...config.allowedHosts !== void 0 ? { allowedHosts: config.allowedHosts } : {},
56
+ warmup: config.warmup ?? {
57
+ clientFiles: ["./app/**/*.tsx", "./app/**/*.ts"]
58
+ }
59
+ };
60
+ }
49
61
  async function startDevServer(cwd, options) {
50
62
  preflightNodeAndBindings(cwd);
51
63
  loadEnv({ cwd, mode: "development" });
@@ -85,15 +97,7 @@ async function startDevServer(cwd, options) {
85
97
  server = await createServer({
86
98
  root: cwd,
87
99
  plugins: [react(), ...theoPlugins],
88
- server: {
89
- port,
90
- host: config.host,
91
- open: config.open,
92
- strictPort: config.strictPort,
93
- warmup: config.warmup ?? {
94
- clientFiles: ["./app/**/*.tsx", "./app/**/*.ts"]
95
- }
96
- },
100
+ server: buildDevServerOptions(config, port),
97
101
  logLevel: options?.port === 0 ? "silent" : void 0
98
102
  });
99
103
  await server.listen();
@@ -120,7 +124,8 @@ async function devCommand(options) {
120
124
  }
121
125
  }
122
126
  export {
127
+ buildDevServerOptions,
123
128
  devCommand,
124
129
  startDevServer
125
130
  };
126
- //# sourceMappingURL=dev-SJE2GSAC.js.map
131
+ //# sourceMappingURL=dev-5ETOFKOP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/dev.ts"],"sourcesContent":["import react from '@vitejs/plugin-react'\nimport { createServer, type Plugin, type ServerOptions, type ViteDevServer } from 'vite'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { loadEnv } from '../../config/load-env.js'\nimport type { TheoConfig } from '../../config/schema.js'\nimport { validateProjectStructure } from '../../config/validate-structure.js'\nimport { initCacheEngineFromConfig } from '../../server/cache-bootstrap.js'\nimport { orchestrateDev } from '../../services/index.js'\nimport { theoPluginAsync } from '../../vite-plugin/index.js'\nimport { preflightNodeAndBindings } from '../preflight-node-version.js'\n\ninterface DevOptions {\n port?: number\n}\n\n/**\n * The Vite `server` block, as a pure function of the loaded config — the same reasoning\n * `buildSecurityHeaders` is written down with: a setting that reaches Vite only from inside a\n * booted dev server can be asserted only by booting one, and a setting nobody can assert is how a\n * config field ends up existing and doing nothing (#555).\n *\n * Every key here is omitted rather than set to `undefined` when the app said nothing, so Vite's own\n * defaults apply. That matters most for `allowedHosts`, where the falsy value is `[]` — an empty\n * allowlist blocks every host, loopback included, so writing one on silence would turn \"no opinion\"\n * into \"refuse everything\".\n */\nexport function buildDevServerOptions(config: TheoConfig, port: number): ServerOptions {\n return {\n port,\n host: config.host,\n open: config.open,\n strictPort: config.strictPort,\n ...(config.allowedHosts !== undefined ? { allowedHosts: config.allowedHosts } : {}),\n warmup: config.warmup ?? {\n clientFiles: ['./app/**/*.tsx', './app/**/*.ts'],\n },\n }\n}\n\nexport async function startDevServer(cwd: string, options?: DevOptions): Promise<ViteDevServer> {\n // Preflight (FIRST — BEFORE anything that touches native bindings):\n // verifies Node version + native binding ABI match. Emits actionable\n // error referencing `nvm use` + `pnpm rebuild` when the dev server\n // was spawned under the wrong Node (common gotcha with `nohup env ...`\n // stripping the nvm PATH prefix).\n preflightNodeAndBindings(cwd)\n\n // Phase 1 (T1.2) — Load .env files into process.env BEFORE any module\n // reads them. Must run before loadConfig() so theo.config.ts functions\n // referencing process.env.* resolve correctly.\n loadEnv({ cwd, mode: 'development' })\n\n const config = await loadConfig(cwd)\n // #95 — pass config.appDir so the structure gate honors a custom frontend dir (e.g. apps/web).\n validateProjectStructure(cwd, config.appDir)\n\n // #352 — same engine, same config key, so `revalidateTag` behaves identically\n // under `theo dev` and `theo start`. A cache that only exists in production is\n // a cache nobody develops against.\n await initCacheEngineFromConfig(config.cache)\n\n // Wave 2 (T1.1) — spawn polyglot services BEFORE Vite. Healthcheck gates\n // readiness. Empty `services: {}` → early return (Wave 1 BC preserved).\n const orchestration = await orchestrateDev({\n cwd,\n services: config.services,\n })\n if (!orchestration.allHealthy) {\n await orchestration.stop()\n throw new Error(\n `[services] services failed healthcheck: ${orchestration.unhealthy.join(', ')}. ` +\n `Check that each declared service binds its port and responds 200 on its ` +\n `healthcheck path within 30s.`,\n )\n }\n\n // Phase 7 — legacy fs-mtime sweep removed: SDK's Agent.registry handles\n // LRU + idle eviction natively (configurable via theo.config.ts >\n // agents.registry; lazy-configured on first request).\n\n const port = options?.port ?? config.port\n // Narrow the rateLimit union: only the legacy flat shape (windowMs+max)\n // flows into the theoPlugin's RateLimitConfig parameter. The per-route\n // variant is consumed inside the api-middleware via the loaded user\n // config separately.\n const flatRateLimit =\n config.rateLimit && 'windowMs' in config.rateLimit && 'max' in config.rateLimit\n ? config.rateLimit\n : undefined\n // theoPluginAsync auto-chains @theokit/ui + @tailwindcss/vite via\n // top-level Plugin[] return (sync `theoPlugin()` factory's config()\n // hook returning {plugins:[...]} is silently dropped by Vite — see\n // commit history). Spread the result so Vite flattens them.\n let server: ViteDevServer\n try {\n const theoPlugins = await theoPluginAsync({\n root: cwd,\n rateLimit: flatRateLimit,\n ssr: config.ssr,\n // #95 — honor config `serverDir` (default \"server\") so route/action discovery scans it.\n serverDir: config.serverDir,\n // #95 follow-up — honor config `agentsDir` (default \"agents\") for the agent scan/middleware.\n agentsDir: config.agentsDir,\n // #95 follow-up — honor config `appDir` (default \"app\") for the file-based router + SSR/client entry.\n appDir: config.appDir,\n // Wave 2 (T3.1) — wire typed-client plugin when services declared.\n services: config.services,\n // T4.1 (canvas-ecosystem-refactor / ADR D6) — passthrough peer-deps\n // that plugins import dynamically (e.g., mermaid via plugin-canvas).\n viteOptimizeDeps: config.viteOptimizeDeps,\n })\n server = await createServer({\n root: cwd,\n plugins: [react(), ...theoPlugins] as Plugin[],\n server: buildDevServerOptions(config, port),\n logLevel: options?.port === 0 ? 'silent' : undefined,\n })\n\n await server.listen()\n } catch (err) {\n // EC-1 / try-finally pattern: if Vite create/listen fails AFTER services\n // started, stop them so we don't leak child processes.\n await orchestration.stop()\n throw err\n }\n\n // EC-1 fix: attach orchestration.stop() to the underlying HTTP server's\n // close event via Node-native API. We do NOT mutate `server.close`\n // (fragile across Vite upgrades). httpServer is `http.Server | null`.\n server.httpServer?.on('close', () => {\n void orchestration.stop()\n })\n\n return server\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n try {\n const cwd = process.cwd()\n const server = await startDevServer(cwd, options)\n server.printUrls()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n console.error(`\\n ✗ ${msg}\\n`)\n process.exit(1)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,WAAW;AAClB,SAAS,oBAAyE;AA0B3E,SAAS,sBAAsB,QAAoB,MAA6B;AACrF,SAAO;AAAA,IACL;AAAA,IACA,MAAM,OAAO;AAAA,IACb,MAAM,OAAO;AAAA,IACb,YAAY,OAAO;AAAA,IACnB,GAAI,OAAO,iBAAiB,SAAY,EAAE,cAAc,OAAO,aAAa,IAAI,CAAC;AAAA,IACjF,QAAQ,OAAO,UAAU;AAAA,MACvB,aAAa,CAAC,kBAAkB,eAAe;AAAA,IACjD;AAAA,EACF;AACF;AAEA,eAAsB,eAAe,KAAa,SAA8C;AAM9F,2BAAyB,GAAG;AAK5B,UAAQ,EAAE,KAAK,MAAM,cAAc,CAAC;AAEpC,QAAM,SAAS,MAAM,WAAW,GAAG;AAEnC,2BAAyB,KAAK,OAAO,MAAM;AAK3C,QAAM,0BAA0B,OAAO,KAAK;AAI5C,QAAM,gBAAgB,MAAM,eAAe;AAAA,IACzC;AAAA,IACA,UAAU,OAAO;AAAA,EACnB,CAAC;AACD,MAAI,CAAC,cAAc,YAAY;AAC7B,UAAM,cAAc,KAAK;AACzB,UAAM,IAAI;AAAA,MACR,2CAA2C,cAAc,UAAU,KAAK,IAAI,CAAC;AAAA,IAG/E;AAAA,EACF;AAMA,QAAM,OAAO,SAAS,QAAQ,OAAO;AAKrC,QAAM,gBACJ,OAAO,aAAa,cAAc,OAAO,aAAa,SAAS,OAAO,YAClE,OAAO,YACP;AAKN,MAAI;AACJ,MAAI;AACF,UAAM,cAAc,MAAM,gBAAgB;AAAA,MACxC,MAAM;AAAA,MACN,WAAW;AAAA,MACX,KAAK,OAAO;AAAA;AAAA,MAEZ,WAAW,OAAO;AAAA;AAAA,MAElB,WAAW,OAAO;AAAA;AAAA,MAElB,QAAQ,OAAO;AAAA;AAAA,MAEf,UAAU,OAAO;AAAA;AAAA;AAAA,MAGjB,kBAAkB,OAAO;AAAA,IAC3B,CAAC;AACD,aAAS,MAAM,aAAa;AAAA,MAC1B,MAAM;AAAA,MACN,SAAS,CAAC,MAAM,GAAG,GAAG,WAAW;AAAA,MACjC,QAAQ,sBAAsB,QAAQ,IAAI;AAAA,MAC1C,UAAU,SAAS,SAAS,IAAI,WAAW;AAAA,IAC7C,CAAC;AAED,UAAM,OAAO,OAAO;AAAA,EACtB,SAAS,KAAK;AAGZ,UAAM,cAAc,KAAK;AACzB,UAAM;AAAA,EACR;AAKA,SAAO,YAAY,GAAG,SAAS,MAAM;AACnC,SAAK,cAAc,KAAK;AAAA,EAC1B,CAAC;AAED,SAAO;AACT;AAEA,eAAsB,WAAW,SAAoC;AACnE,MAAI;AACF,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,SAAS,MAAM,eAAe,KAAK,OAAO;AAChD,WAAO,UAAU;AAAA,EACnB,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,MAAM;AAAA,WAAS,GAAG;AAAA,CAAI;AAC9B,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
package/dist/index.d.ts CHANGED
@@ -47,6 +47,7 @@ declare const theoConfigSchema: z.ZodObject<{
47
47
  }, z.core.$strip>>;
48
48
  port: z.ZodDefault<z.ZodNumber>;
49
49
  host: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
50
+ allowedHosts: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodLiteral<true>]>>;
50
51
  open: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
51
52
  strictPort: z.ZodDefault<z.ZodBoolean>;
52
53
  forwardConsole: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
@@ -298,6 +299,12 @@ interface ConfigBuilder {
298
299
  port(value: TheoConfig['port']): ConfigBuilder;
299
300
  /** Bind host. Default `'localhost'`. */
300
301
  host(value: TheoConfig['host']): ConfigBuilder;
302
+ /**
303
+ * Hostnames the dev server will answer for (Vite's `server.allowedHosts`) — what a tunnel or a
304
+ * reverse proxy needs. NOT the same as {@link ConfigBuilder.host}, which is the bind address;
305
+ * see `TheoConfig['allowedHosts']` for the matching rules and the cost of `true`.
306
+ */
307
+ allowedHosts(value: TheoConfig['allowedHosts']): ConfigBuilder;
301
308
  /** Enable server-side rendering. Default `false`. */
302
309
  ssr(value: TheoConfig['ssr']): ConfigBuilder;
303
310
  /** Escape hatch — merge any config fields not covered by a dedicated setter (rate-limit, security…). */
package/dist/index.js CHANGED
@@ -8,15 +8,15 @@ import {
8
8
  scanRoutes,
9
9
  theoConfigSchema,
10
10
  theoPlugin
11
- } from "./chunk-YGL3H3UW.js";
11
+ } from "./chunk-K3DUUEQ5.js";
12
12
  import "./chunk-7NRFZBAL.js";
13
13
  import "./chunk-SSYDEKWF.js";
14
14
  import "./chunk-WOANPZBT.js";
15
- import "./chunk-M357ILK5.js";
15
+ import "./chunk-DMODPX7P.js";
16
16
  import "./chunk-OIGJOYX6.js";
17
17
  import "./chunk-U3SPBLJF.js";
18
- import "./chunk-BPYMC7SU.js";
19
- import "./chunk-IRQGAC4L.js";
18
+ import "./chunk-EYRWD2AB.js";
19
+ import "./chunk-5GZROLDV.js";
20
20
  import "./chunk-OGLS76RN.js";
21
21
  import "./chunk-JW5UFKQ3.js";
22
22
  import "./chunk-P6OS7652.js";
@@ -39,7 +39,7 @@ import "./chunk-V6Y2SMRG.js";
39
39
  import "./chunk-ACKBVBUI.js";
40
40
  import "./chunk-IZ3TIL6K.js";
41
41
  import "./chunk-YOIVCCZT.js";
42
- import "./chunk-CF2DNN5R.js";
42
+ import "./chunk-45XETXSG.js";
43
43
  import "./chunk-YSJOZEI6.js";
44
44
  import "./chunk-Z4PJMZLM.js";
45
45
  import "./chunk-UMNXCU3K.js";
@@ -64,6 +64,7 @@ function makeConfigBuilder(spec) {
64
64
  distDir: (value) => merge({ distDir: value }),
65
65
  port: (value) => merge({ port: value }),
66
66
  host: (value) => merge({ host: value }),
67
+ allowedHosts: (value) => merge({ allowedHosts: value }),
67
68
  ssr: (value) => merge({ ssr: value }),
68
69
  set: (partial) => merge(partial),
69
70
  build: () => defineConfig(spec)
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config/define-config.ts","../src/config/config-builder.ts","../src/config/validate-structure.ts","../src/core/errors.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOO,SAAS,aAAaA,SAAkD;AAC7E,SAAOA;AACT;;;ACiCA,SAAS,kBAAkB,MAA0C;AACnE,QAAM,QAAQ,CAAC,UACb,kBAAkB,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC;AACzC,SAAO;AAAA,IACL,MAAM,CAAC,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAAA,IACtC,WAAW,CAAC,UAAU,MAAM,EAAE,WAAW,MAAM,CAAC;AAAA,IAChD,QAAQ,CAAC,UAAU,MAAM,EAAE,QAAQ,MAAM,CAAC;AAAA,IAC1C,WAAW,CAAC,UAAU,MAAM,EAAE,WAAW,MAAM,CAAC;AAAA,IAChD,SAAS,CAAC,UAAU,MAAM,EAAE,SAAS,MAAM,CAAC;AAAA,IAC5C,MAAM,CAAC,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAAA,IACtC,MAAM,CAAC,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAAA,IACtC,KAAK,CAAC,UAAU,MAAM,EAAE,KAAK,MAAM,CAAC;AAAA,IACpC,KAAK,CAAC,YAAY,MAAM,OAAO;AAAA,IAC/B,OAAO,MAAM,aAAa,IAAI;AAAA,EAChC;AACF;AAKO,SAAS,SAAwB;AACtC,SAAO,kBAAkB,CAAC,CAAC;AAC7B;;;AC5DA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;;;ACLd,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1B;AAAA,EACA;AAAA,EAEhB,YAAY,QAAkB,SAAiB;AAC7C,UAAM,aAAa,OAAO,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI;AAE1D;AAAA,MACE;AAAA;AAAA,UACa,OAAO;AAAA;AAAA,KACjB,aAAa;AAAA,EAAc,UAAU;AAAA,IAAO;AAAA,IACjD;AAEA,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AACF;;;ADHA,IAAM,iBAAmC;AAAA,EACvC;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAIO,SAAS,yBAAyB,SAAiB,SAAS,OAAa;AAC9E,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,UAAM,IAAI,iBAAiB,CAAC,qCAAqC,OAAO,EAAE,GAAG,OAAO;AAAA,EACtF;AAEA,QAAM,SAAmB,CAAC;AAE1B,QAAM,eAAiC;AAAA,IACrC,EAAE,MAAM,QAAQ,cAAc,+BAA+B,MAAM,IAAI;AAAA,EACzE;AAEA,aAAW,QAAQ,cAAc;AAC/B,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,aAAW,QAAQ,gBAAgB;AACjC,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,iBAAiB,QAAQ,OAAO;AAAA,EAC5C;AACF;","names":["config"]}
1
+ {"version":3,"sources":["../src/config/define-config.ts","../src/config/config-builder.ts","../src/config/validate-structure.ts","../src/core/errors.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOO,SAAS,aAAaA,SAAkD;AAC7E,SAAOA;AACT;;;ACuCA,SAAS,kBAAkB,MAA0C;AACnE,QAAM,QAAQ,CAAC,UACb,kBAAkB,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC;AACzC,SAAO;AAAA,IACL,MAAM,CAAC,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAAA,IACtC,WAAW,CAAC,UAAU,MAAM,EAAE,WAAW,MAAM,CAAC;AAAA,IAChD,QAAQ,CAAC,UAAU,MAAM,EAAE,QAAQ,MAAM,CAAC;AAAA,IAC1C,WAAW,CAAC,UAAU,MAAM,EAAE,WAAW,MAAM,CAAC;AAAA,IAChD,SAAS,CAAC,UAAU,MAAM,EAAE,SAAS,MAAM,CAAC;AAAA,IAC5C,MAAM,CAAC,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAAA,IACtC,MAAM,CAAC,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAAA,IACtC,cAAc,CAAC,UAAU,MAAM,EAAE,cAAc,MAAM,CAAC;AAAA,IACtD,KAAK,CAAC,UAAU,MAAM,EAAE,KAAK,MAAM,CAAC;AAAA,IACpC,KAAK,CAAC,YAAY,MAAM,OAAO;AAAA,IAC/B,OAAO,MAAM,aAAa,IAAI;AAAA,EAChC;AACF;AAKO,SAAS,SAAwB;AACtC,SAAO,kBAAkB,CAAC,CAAC;AAC7B;;;ACnEA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;;;ACLd,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1B;AAAA,EACA;AAAA,EAEhB,YAAY,QAAkB,SAAiB;AAC7C,UAAM,aAAa,OAAO,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI;AAE1D;AAAA,MACE;AAAA;AAAA,UACa,OAAO;AAAA;AAAA,KACjB,aAAa;AAAA,EAAc,UAAU;AAAA,IAAO;AAAA,IACjD;AAEA,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AACF;;;ADHA,IAAM,iBAAmC;AAAA,EACvC;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAIO,SAAS,yBAAyB,SAAiB,SAAS,OAAa;AAC9E,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,UAAM,IAAI,iBAAiB,CAAC,qCAAqC,OAAO,EAAE,GAAG,OAAO;AAAA,EACtF;AAEA,QAAM,SAAmB,CAAC;AAE1B,QAAM,eAAiC;AAAA,IACrC,EAAE,MAAM,QAAQ,cAAc,+BAA+B,MAAM,IAAI;AAAA,EACzE;AAEA,aAAW,QAAQ,cAAc;AAC/B,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,aAAW,QAAQ,gBAAgB;AACjC,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,iBAAiB,QAAQ,OAAO;AAAA,EAC5C;AACF;","names":["config"]}
@@ -5,10 +5,10 @@ import {
5
5
  } from "./chunk-47MM2JUK.js";
6
6
  import {
7
7
  loadConfig
8
- } from "./chunk-HA35FS7U.js";
8
+ } from "./chunk-FFS7IRZF.js";
9
9
  import "./chunk-3QPVEJ7D.js";
10
10
  import "./chunk-U5PWXS3J.js";
11
- import "./chunk-4OTIXAUY.js";
11
+ import "./chunk-TMQ52AZR.js";
12
12
  import "./chunk-ZNZISJDZ.js";
13
13
  import "./chunk-OSBGZYXZ.js";
14
14
 
@@ -103,4 +103,4 @@ export {
103
103
  buildInfo,
104
104
  infoCommand
105
105
  };
106
- //# sourceMappingURL=info-6WJ3JRNE.js.map
106
+ //# sourceMappingURL=info-2H7I55YN.js.map
@@ -3,15 +3,15 @@ import {
3
3
  } from "./chunk-WOANPZBT.js";
4
4
  import {
5
5
  mountAgent
6
- } from "./chunk-M357ILK5.js";
6
+ } from "./chunk-DMODPX7P.js";
7
7
  import {
8
8
  CSP_REPORT_PATH,
9
9
  CsrfReadinessStore,
10
10
  applySecurityHeaders,
11
11
  handleCspReport,
12
12
  handleCsrfReadiness
13
- } from "./chunk-BPYMC7SU.js";
14
- import "./chunk-IRQGAC4L.js";
13
+ } from "./chunk-EYRWD2AB.js";
14
+ import "./chunk-5GZROLDV.js";
15
15
  import {
16
16
  BATCH_PATH,
17
17
  createCorsHandler,
@@ -64,7 +64,7 @@ import "./chunk-ACKBVBUI.js";
64
64
  import "./chunk-IZ3TIL6K.js";
65
65
  import {
66
66
  resolveProvider
67
- } from "./chunk-CF2DNN5R.js";
67
+ } from "./chunk-45XETXSG.js";
68
68
  import "./chunk-YSJOZEI6.js";
69
69
  import {
70
70
  generateNonce
@@ -107,4 +107,4 @@ export {
107
107
  scanWebSocketRoutes,
108
108
  sendError
109
109
  };
110
- //# sourceMappingURL=internal-api-W5RURFU7.js.map
110
+ //# sourceMappingURL=internal-api-3YDGG5FF.js.map
@@ -16,10 +16,10 @@ import {
16
16
  handleCspReport,
17
17
  handleCsrfReadiness,
18
18
  mountAgent
19
- } from "./chunk-IF7CZXPW.js";
19
+ } from "./chunk-GZ5YHD5H.js";
20
20
  import {
21
21
  resolveProvider
22
- } from "./chunk-35QIWOHT.js";
22
+ } from "./chunk-WFIAYPBH.js";
23
23
  import {
24
24
  dispatchControllerRequest,
25
25
  incomingMessageToHandlerRequest,
@@ -27,7 +27,7 @@ import {
27
27
  } from "./chunk-L4TB6FXQ.js";
28
28
  import {
29
29
  generateNonce
30
- } from "./chunk-RPET332G.js";
30
+ } from "./chunk-VV2VU3LJ.js";
31
31
  import {
32
32
  TRACE_HEADER,
33
33
  extractTraceId
@@ -87,4 +87,4 @@ export {
87
87
  scanWebSocketRoutes,
88
88
  sendError
89
89
  };
90
- //# sourceMappingURL=internal-api-4FBFRQ4G.js.map
90
+ //# sourceMappingURL=internal-api-UM77XDXN.js.map
@@ -3,14 +3,14 @@ import "tsx/esm";
3
3
  import {
4
4
  deepMerge,
5
5
  loadConfig
6
- } from "./chunk-HA35FS7U.js";
6
+ } from "./chunk-FFS7IRZF.js";
7
7
  import "./chunk-3QPVEJ7D.js";
8
8
  import "./chunk-U5PWXS3J.js";
9
- import "./chunk-4OTIXAUY.js";
9
+ import "./chunk-TMQ52AZR.js";
10
10
  import "./chunk-ZNZISJDZ.js";
11
11
  import "./chunk-OSBGZYXZ.js";
12
12
  export {
13
13
  deepMerge,
14
14
  loadConfig
15
15
  };
16
- //# sourceMappingURL=load-config-BKUAMGO5.js.map
16
+ //# sourceMappingURL=load-config-VMKOV44Y.js.map
@@ -2,12 +2,12 @@
2
2
  import "tsx/esm";
3
3
  import {
4
4
  createAgentSsrLoader
5
- } from "./chunk-MWPC7EOR.js";
5
+ } from "./chunk-IZTOGKLS.js";
6
6
  import {
7
7
  extractAppResources,
8
8
  handleMcpJsonRpc
9
9
  } from "./chunk-2CVV6CNN.js";
10
- import "./chunk-35QIWOHT.js";
10
+ import "./chunk-WFIAYPBH.js";
11
11
  import {
12
12
  scanAgents
13
13
  } from "./chunk-5NNFO7R7.js";
@@ -46,7 +46,7 @@ async function serveMcpStdio(mod, name, appResources, streams) {
46
46
  // src/cli/commands/mcp.ts
47
47
  async function mcpCommand(name, deps = {}) {
48
48
  const projectRoot = deps.projectRoot ?? process.cwd();
49
- const agentsDir = deps.agentsDir ?? (await (await import("./load-config-BKUAMGO5.js")).loadConfig(projectRoot)).agentsDir;
49
+ const agentsDir = deps.agentsDir ?? (await (await import("./load-config-VMKOV44Y.js")).loadConfig(projectRoot)).agentsDir;
50
50
  const agent = scanAgents(projectRoot, agentsDir).find((a) => a.name === name);
51
51
  if (!agent) {
52
52
  const names = scanAgents(projectRoot, agentsDir).map((a) => a.name).join(", ");
@@ -75,4 +75,4 @@ async function mcpCommand(name, deps = {}) {
75
75
  export {
76
76
  mcpCommand
77
77
  };
78
- //# sourceMappingURL=mcp-I5HCW4QF.js.map
78
+ //# sourceMappingURL=mcp-B42ISIJG.js.map
@@ -7,13 +7,13 @@ import {
7
7
  deployedTraceFragment,
8
8
  describeDeployedSecurityHeaders,
9
9
  renderSecurityHeadersConfigLiteral
10
- } from "./chunk-E43VJ3E5.js";
10
+ } from "./chunk-VPX3MS5D.js";
11
11
  import {
12
12
  nodeAdapter
13
13
  } from "./chunk-KI4DOTN4.js";
14
14
  import {
15
15
  buildSecurityHeaders
16
- } from "./chunk-RPET332G.js";
16
+ } from "./chunk-VV2VU3LJ.js";
17
17
  import {
18
18
  assertServicesUnsupported,
19
19
  readManifest
@@ -242,4 +242,4 @@ export {
242
242
  netlifyAdapter,
243
243
  renderNetlifyFunction
244
244
  };
245
- //# sourceMappingURL=netlify-C2IRCZKI.js.map
245
+ //# sourceMappingURL=netlify-BJQGPMGY.js.map
@@ -12,10 +12,10 @@ import "./chunk-NO6HA4FG.js";
12
12
  import "./chunk-Z7CJHDLS.js";
13
13
  import {
14
14
  loadConfig
15
- } from "./chunk-HA35FS7U.js";
15
+ } from "./chunk-FFS7IRZF.js";
16
16
  import "./chunk-3QPVEJ7D.js";
17
17
  import "./chunk-U5PWXS3J.js";
18
- import "./chunk-4OTIXAUY.js";
18
+ import "./chunk-TMQ52AZR.js";
19
19
  import "./chunk-ZNZISJDZ.js";
20
20
  import "./chunk-OSBGZYXZ.js";
21
21
 
@@ -84,4 +84,4 @@ function emitOpenApiInMemory(manifest, config) {
84
84
  export {
85
85
  openapiCommand
86
86
  };
87
- //# sourceMappingURL=openapi-WDX3XX2J.js.map
87
+ //# sourceMappingURL=openapi-SKKXWFX4.js.map
@@ -4,8 +4,8 @@ import "tsx/esm";
4
4
  // src/cli/commands/preview.ts
5
5
  async function defaultSteps() {
6
6
  const [{ buildCommand }, { startCommand }] = await Promise.all([
7
- import("./build-H2UDKBV7.js"),
8
- import("./start-KEWSYVVU.js")
7
+ import("./build-RG54DH3F.js"),
8
+ import("./start-3FYIYTM7.js")
9
9
  ]);
10
10
  return {
11
11
  build: (opts) => buildCommand(opts),
@@ -20,4 +20,4 @@ async function previewCommand(options = {}, steps) {
20
20
  export {
21
21
  previewCommand
22
22
  };
23
- //# sourceMappingURL=preview-PWGO5LUE.js.map
23
+ //# sourceMappingURL=preview-XP77EPTL.js.map
@@ -60,6 +60,23 @@ interface ProviderDescriptor {
60
60
  * contradicts the real one is a debugging trap rather than a harmless inaccuracy.
61
61
  */
62
62
  baseUrlEnv?: string;
63
+ /**
64
+ * True when this provider is a GATEWAY — it serves other providers' catalogs under their own
65
+ * model ids, reachable by prefixing its name (`openrouter/openai/gpt-4o-mini`).
66
+ *
67
+ * It changes nothing about resolution. It exists so the refusal below can name a model id that
68
+ * would work instead of only the variable that is missing (#554): a scaffold shipping
69
+ * `openai/gpt-4o-mini` and an `.env.example` asking for `OPENROUTER_API_KEY` produced an error
70
+ * naming `OPENAI_API_KEY` — the one key the reader was never told to get — while the key they had
71
+ * serves that exact id through the gateway.
72
+ *
73
+ * Deliberately NOT a licence to substitute. Every caller here takes `.apiKey` and discards
74
+ * `.baseUrl`; the SDK picks the endpoint from the model id (measured: `openai/gpt-4o-mini` →
75
+ * `api.openai.com`, `openrouter/openai/gpt-4o-mini` → `openrouter.ai`). Handing an OpenRouter key
76
+ * to a request bound for OpenAI is #326 exactly, so the caller changes the id and the routing
77
+ * follows — the framework does not change it for them.
78
+ */
79
+ gateway?: boolean;
63
80
  /** Resolution priority (lower = higher priority). FIRST match wins. */
64
81
  priority: number;
65
82
  }