pi-smart-router 0.1.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 (310) hide show
  1. package/.pi/extensions/smart-router/command-formatters.ts +167 -0
  2. package/.pi/extensions/smart-router/commands.ts +242 -0
  3. package/.pi/extensions/smart-router/dataset-export.ts +142 -0
  4. package/.pi/extensions/smart-router/delegate-stream.ts +105 -0
  5. package/.pi/extensions/smart-router/delegation-runtime.ts +355 -0
  6. package/.pi/extensions/smart-router/extension-setup.ts +115 -0
  7. package/.pi/extensions/smart-router/fleet-bootstrap.ts +244 -0
  8. package/.pi/extensions/smart-router/index.ts +115 -0
  9. package/.pi/extensions/smart-router/package.json +3 -0
  10. package/.pi/extensions/smart-router/pi-model-scope.ts +55 -0
  11. package/.pi/extensions/smart-router/pricing-lifecycle.ts +43 -0
  12. package/.pi/extensions/smart-router/route-and-delegate.ts +562 -0
  13. package/.pi/extensions/smart-router/routing-context.ts +171 -0
  14. package/.pi/extensions/smart-router/routing-outcomes.ts +44 -0
  15. package/.pi/extensions/smart-router/session-lifecycle.ts +140 -0
  16. package/.pi/extensions/smart-router/stream-delegation.ts +75 -0
  17. package/.pi/extensions/smart-router/types.ts +90 -0
  18. package/.pi/extensions/smart-router/utils.ts +58 -0
  19. package/README.md +593 -0
  20. package/bin/pi-smart-router.mjs +79 -0
  21. package/config/.gitkeep +0 -0
  22. package/config/hydra-projection-weights.json.example +1169 -0
  23. package/config/models.yaml.example +58 -0
  24. package/config/p-success-weights.json.example +19 -0
  25. package/config/routing-calibration.json.example +1605 -0
  26. package/config/routing-centroids.json.example +1570 -0
  27. package/config/routing-clusters.yaml.example +41 -0
  28. package/data/contrib/example.json +25 -0
  29. package/dist/api/explain/router-explain.d.ts +47 -0
  30. package/dist/api/explain/router-explain.d.ts.map +1 -0
  31. package/dist/api/explain/router-explain.js +101 -0
  32. package/dist/api/explain/router-explain.js.map +1 -0
  33. package/dist/api/middleware/pi-router-middleware.d.ts +86 -0
  34. package/dist/api/middleware/pi-router-middleware.d.ts.map +1 -0
  35. package/dist/api/middleware/pi-router-middleware.js +83 -0
  36. package/dist/api/middleware/pi-router-middleware.js.map +1 -0
  37. package/dist/cli/smart-router-cli.d.ts +85 -0
  38. package/dist/cli/smart-router-cli.d.ts.map +1 -0
  39. package/dist/cli/smart-router-cli.js +293 -0
  40. package/dist/cli/smart-router-cli.js.map +1 -0
  41. package/dist/config/defaults.d.ts +7 -0
  42. package/dist/config/defaults.d.ts.map +1 -0
  43. package/dist/config/defaults.js +33 -0
  44. package/dist/config/defaults.js.map +1 -0
  45. package/dist/config/models-loader.d.ts +22 -0
  46. package/dist/config/models-loader.d.ts.map +1 -0
  47. package/dist/config/models-loader.js +51 -0
  48. package/dist/config/models-loader.js.map +1 -0
  49. package/dist/config/pi-model-mapper.d.ts +40 -0
  50. package/dist/config/pi-model-mapper.d.ts.map +1 -0
  51. package/dist/config/pi-model-mapper.js +216 -0
  52. package/dist/config/pi-model-mapper.js.map +1 -0
  53. package/dist/config/routing-clusters-loader.d.ts +35 -0
  54. package/dist/config/routing-clusters-loader.d.ts.map +1 -0
  55. package/dist/config/routing-clusters-loader.js +100 -0
  56. package/dist/config/routing-clusters-loader.js.map +1 -0
  57. package/dist/domain/delegation/delegation-context.d.ts +23 -0
  58. package/dist/domain/delegation/delegation-context.d.ts.map +1 -0
  59. package/dist/domain/delegation/delegation-context.js +73 -0
  60. package/dist/domain/delegation/delegation-context.js.map +1 -0
  61. package/dist/domain/delegation/execution-ledger.d.ts +19 -0
  62. package/dist/domain/delegation/execution-ledger.d.ts.map +1 -0
  63. package/dist/domain/delegation/execution-ledger.js +23 -0
  64. package/dist/domain/delegation/execution-ledger.js.map +1 -0
  65. package/dist/domain/delegation/output-headroom.d.ts +39 -0
  66. package/dist/domain/delegation/output-headroom.d.ts.map +1 -0
  67. package/dist/domain/delegation/output-headroom.js +78 -0
  68. package/dist/domain/delegation/output-headroom.js.map +1 -0
  69. package/dist/domain/matching/cluster-matcher.d.ts +69 -0
  70. package/dist/domain/matching/cluster-matcher.d.ts.map +1 -0
  71. package/dist/domain/matching/cluster-matcher.js +294 -0
  72. package/dist/domain/matching/cluster-matcher.js.map +1 -0
  73. package/dist/domain/matching/embedding-provider.d.ts +21 -0
  74. package/dist/domain/matching/embedding-provider.d.ts.map +1 -0
  75. package/dist/domain/matching/embedding-provider.js +42 -0
  76. package/dist/domain/matching/embedding-provider.js.map +1 -0
  77. package/dist/domain/matching/hydra-input.d.ts +18 -0
  78. package/dist/domain/matching/hydra-input.d.ts.map +1 -0
  79. package/dist/domain/matching/hydra-input.js +40 -0
  80. package/dist/domain/matching/hydra-input.js.map +1 -0
  81. package/dist/domain/matching/hydra-matcher.d.ts +103 -0
  82. package/dist/domain/matching/hydra-matcher.d.ts.map +1 -0
  83. package/dist/domain/matching/hydra-matcher.js +275 -0
  84. package/dist/domain/matching/hydra-matcher.js.map +1 -0
  85. package/dist/domain/pinning/cache-economics.d.ts +53 -0
  86. package/dist/domain/pinning/cache-economics.d.ts.map +1 -0
  87. package/dist/domain/pinning/cache-economics.js +72 -0
  88. package/dist/domain/pinning/cache-economics.js.map +1 -0
  89. package/dist/domain/pinning/loop-escalation.d.ts +41 -0
  90. package/dist/domain/pinning/loop-escalation.d.ts.map +1 -0
  91. package/dist/domain/pinning/loop-escalation.js +137 -0
  92. package/dist/domain/pinning/loop-escalation.js.map +1 -0
  93. package/dist/domain/pinning/session-pinner.d.ts +81 -0
  94. package/dist/domain/pinning/session-pinner.d.ts.map +1 -0
  95. package/dist/domain/pinning/session-pinner.js +237 -0
  96. package/dist/domain/pinning/session-pinner.js.map +1 -0
  97. package/dist/domain/pinning/sub-route-policy.d.ts +41 -0
  98. package/dist/domain/pinning/sub-route-policy.d.ts.map +1 -0
  99. package/dist/domain/pinning/sub-route-policy.js +72 -0
  100. package/dist/domain/pinning/sub-route-policy.js.map +1 -0
  101. package/dist/domain/pipeline/router-pipeline.d.ts +180 -0
  102. package/dist/domain/pipeline/router-pipeline.d.ts.map +1 -0
  103. package/dist/domain/pipeline/router-pipeline.js +807 -0
  104. package/dist/domain/pipeline/router-pipeline.js.map +1 -0
  105. package/dist/domain/pipeline/safe-default.d.ts +25 -0
  106. package/dist/domain/pipeline/safe-default.d.ts.map +1 -0
  107. package/dist/domain/pipeline/safe-default.js +41 -0
  108. package/dist/domain/pipeline/safe-default.js.map +1 -0
  109. package/dist/domain/refactor-guardrails.d.ts +48 -0
  110. package/dist/domain/refactor-guardrails.d.ts.map +1 -0
  111. package/dist/domain/refactor-guardrails.js +129 -0
  112. package/dist/domain/refactor-guardrails.js.map +1 -0
  113. package/dist/domain/routing/context-fit.d.ts +53 -0
  114. package/dist/domain/routing/context-fit.d.ts.map +1 -0
  115. package/dist/domain/routing/context-fit.js +142 -0
  116. package/dist/domain/routing/context-fit.js.map +1 -0
  117. package/dist/domain/routing/expected-cost.d.ts +63 -0
  118. package/dist/domain/routing/expected-cost.d.ts.map +1 -0
  119. package/dist/domain/routing/expected-cost.js +177 -0
  120. package/dist/domain/routing/expected-cost.js.map +1 -0
  121. package/dist/domain/routing/p-success-classifier.d.ts +130 -0
  122. package/dist/domain/routing/p-success-classifier.d.ts.map +1 -0
  123. package/dist/domain/routing/p-success-classifier.js +350 -0
  124. package/dist/domain/routing/p-success-classifier.js.map +1 -0
  125. package/dist/domain/routing/tier-features.d.ts +70 -0
  126. package/dist/domain/routing/tier-features.d.ts.map +1 -0
  127. package/dist/domain/routing/tier-features.js +174 -0
  128. package/dist/domain/routing/tier-features.js.map +1 -0
  129. package/dist/domain/routing/tool-history-guard.d.ts +32 -0
  130. package/dist/domain/routing/tool-history-guard.d.ts.map +1 -0
  131. package/dist/domain/routing/tool-history-guard.js +110 -0
  132. package/dist/domain/routing/tool-history-guard.js.map +1 -0
  133. package/dist/domain/scoring/multi-objective.d.ts +48 -0
  134. package/dist/domain/scoring/multi-objective.d.ts.map +1 -0
  135. package/dist/domain/scoring/multi-objective.js +116 -0
  136. package/dist/domain/scoring/multi-objective.js.map +1 -0
  137. package/dist/domain/triage/triage-engine.d.ts +43 -0
  138. package/dist/domain/triage/triage-engine.d.ts.map +1 -0
  139. package/dist/domain/triage/triage-engine.js +317 -0
  140. package/dist/domain/triage/triage-engine.js.map +1 -0
  141. package/dist/domain/triage/turn-envelope.d.ts +17 -0
  142. package/dist/domain/triage/turn-envelope.d.ts.map +1 -0
  143. package/dist/domain/triage/turn-envelope.js +81 -0
  144. package/dist/domain/triage/turn-envelope.js.map +1 -0
  145. package/dist/domain/types/entities.d.ts +303 -0
  146. package/dist/domain/types/entities.d.ts.map +1 -0
  147. package/dist/domain/types/entities.js +6 -0
  148. package/dist/domain/types/entities.js.map +1 -0
  149. package/dist/domain/types/index.d.ts +4 -0
  150. package/dist/domain/types/index.d.ts.map +1 -0
  151. package/dist/domain/types/index.js +2 -0
  152. package/dist/domain/types/index.js.map +1 -0
  153. package/dist/domain/types/schemas.d.ts +346 -0
  154. package/dist/domain/types/schemas.d.ts.map +1 -0
  155. package/dist/domain/types/schemas.js +241 -0
  156. package/dist/domain/types/schemas.js.map +1 -0
  157. package/dist/domain/types/store-port.d.ts +44 -0
  158. package/dist/domain/types/store-port.d.ts.map +1 -0
  159. package/dist/domain/types/store-port.js +6 -0
  160. package/dist/domain/types/store-port.js.map +1 -0
  161. package/dist/index.d.ts +39 -0
  162. package/dist/index.d.ts.map +1 -0
  163. package/dist/index.js +36 -0
  164. package/dist/index.js.map +1 -0
  165. package/dist/infra/gemini-provider.d.ts +28 -0
  166. package/dist/infra/gemini-provider.d.ts.map +1 -0
  167. package/dist/infra/gemini-provider.js +112 -0
  168. package/dist/infra/gemini-provider.js.map +1 -0
  169. package/dist/infra/telemetry.d.ts +51 -0
  170. package/dist/infra/telemetry.d.ts.map +1 -0
  171. package/dist/infra/telemetry.js +100 -0
  172. package/dist/infra/telemetry.js.map +1 -0
  173. package/dist/infrastructure/delegation/provider-error.d.ts +44 -0
  174. package/dist/infrastructure/delegation/provider-error.d.ts.map +1 -0
  175. package/dist/infrastructure/delegation/provider-error.js +179 -0
  176. package/dist/infrastructure/delegation/provider-error.js.map +1 -0
  177. package/dist/infrastructure/gateway/circuit-breaker.d.ts +68 -0
  178. package/dist/infrastructure/gateway/circuit-breaker.d.ts.map +1 -0
  179. package/dist/infrastructure/gateway/circuit-breaker.js +150 -0
  180. package/dist/infrastructure/gateway/circuit-breaker.js.map +1 -0
  181. package/dist/infrastructure/gateway/gateway-dispatch.d.ts +138 -0
  182. package/dist/infrastructure/gateway/gateway-dispatch.d.ts.map +1 -0
  183. package/dist/infrastructure/gateway/gateway-dispatch.js +308 -0
  184. package/dist/infrastructure/gateway/gateway-dispatch.js.map +1 -0
  185. package/dist/infrastructure/hardware/hardware-probe.d.ts +32 -0
  186. package/dist/infrastructure/hardware/hardware-probe.d.ts.map +1 -0
  187. package/dist/infrastructure/hardware/hardware-probe.js +199 -0
  188. package/dist/infrastructure/hardware/hardware-probe.js.map +1 -0
  189. package/dist/infrastructure/local/local-zero-tier.d.ts +47 -0
  190. package/dist/infrastructure/local/local-zero-tier.d.ts.map +1 -0
  191. package/dist/infrastructure/local/local-zero-tier.js +92 -0
  192. package/dist/infrastructure/local/local-zero-tier.js.map +1 -0
  193. package/dist/infrastructure/persistence/memory-store.d.ts +31 -0
  194. package/dist/infrastructure/persistence/memory-store.d.ts.map +1 -0
  195. package/dist/infrastructure/persistence/memory-store.js +87 -0
  196. package/dist/infrastructure/persistence/memory-store.js.map +1 -0
  197. package/dist/infrastructure/persistence/sqlite-store.d.ts +81 -0
  198. package/dist/infrastructure/persistence/sqlite-store.d.ts.map +1 -0
  199. package/dist/infrastructure/persistence/sqlite-store.js +658 -0
  200. package/dist/infrastructure/persistence/sqlite-store.js.map +1 -0
  201. package/dist/infrastructure/pricing/litellm-fetch.d.ts +46 -0
  202. package/dist/infrastructure/pricing/litellm-fetch.d.ts.map +1 -0
  203. package/dist/infrastructure/pricing/litellm-fetch.js +145 -0
  204. package/dist/infrastructure/pricing/litellm-fetch.js.map +1 -0
  205. package/dist/infrastructure/pricing/price-broker.d.ts +56 -0
  206. package/dist/infrastructure/pricing/price-broker.d.ts.map +1 -0
  207. package/dist/infrastructure/pricing/price-broker.js +133 -0
  208. package/dist/infrastructure/pricing/price-broker.js.map +1 -0
  209. package/dist/infrastructure/pricing/pricing-monitor.d.ts +27 -0
  210. package/dist/infrastructure/pricing/pricing-monitor.d.ts.map +1 -0
  211. package/dist/infrastructure/pricing/pricing-monitor.js +46 -0
  212. package/dist/infrastructure/pricing/pricing-monitor.js.map +1 -0
  213. package/dist/infrastructure/telemetry/dataset-limits.d.ts +13 -0
  214. package/dist/infrastructure/telemetry/dataset-limits.d.ts.map +1 -0
  215. package/dist/infrastructure/telemetry/dataset-limits.js +28 -0
  216. package/dist/infrastructure/telemetry/dataset-limits.js.map +1 -0
  217. package/dist/infrastructure/telemetry/dataset-recorder.d.ts +48 -0
  218. package/dist/infrastructure/telemetry/dataset-recorder.d.ts.map +1 -0
  219. package/dist/infrastructure/telemetry/dataset-recorder.js +154 -0
  220. package/dist/infrastructure/telemetry/dataset-recorder.js.map +1 -0
  221. package/dist/infrastructure/telemetry/outcome-limits.d.ts +12 -0
  222. package/dist/infrastructure/telemetry/outcome-limits.d.ts.map +1 -0
  223. package/dist/infrastructure/telemetry/outcome-limits.js +28 -0
  224. package/dist/infrastructure/telemetry/outcome-limits.js.map +1 -0
  225. package/dist/infrastructure/telemetry/outcome-recorder.d.ts +29 -0
  226. package/dist/infrastructure/telemetry/outcome-recorder.d.ts.map +1 -0
  227. package/dist/infrastructure/telemetry/outcome-recorder.js +49 -0
  228. package/dist/infrastructure/telemetry/outcome-recorder.js.map +1 -0
  229. package/dist/infrastructure/telemetry/routing-telemetry.d.ts +115 -0
  230. package/dist/infrastructure/telemetry/routing-telemetry.d.ts.map +1 -0
  231. package/dist/infrastructure/telemetry/routing-telemetry.js +529 -0
  232. package/dist/infrastructure/telemetry/routing-telemetry.js.map +1 -0
  233. package/dist/infrastructure/telemetry/telemetry-limits.d.ts +16 -0
  234. package/dist/infrastructure/telemetry/telemetry-limits.d.ts.map +1 -0
  235. package/dist/infrastructure/telemetry/telemetry-limits.js +35 -0
  236. package/dist/infrastructure/telemetry/telemetry-limits.js.map +1 -0
  237. package/package.json +110 -0
  238. package/skills/router-backlog-orchestrator/SKILL.md +183 -0
  239. package/skills/router-backlog-orchestrator/references/github-router-issue-template.md +78 -0
  240. package/skills/router-backlog-orchestrator/references/packet-from-issue.md +99 -0
  241. package/skills/router-backlog-orchestrator/references/prioritization-rubric.md +65 -0
  242. package/skills/router-backlog-orchestrator/scripts/collect-backlog.sh +44 -0
  243. package/specs/001-build-smart-router/contracts/telemetry-contrib.schema.json +151 -0
  244. package/src/api/explain/.gitkeep +0 -0
  245. package/src/api/explain/router-explain.ts +146 -0
  246. package/src/api/middleware/.gitkeep +0 -0
  247. package/src/api/middleware/pi-router-middleware.ts +169 -0
  248. package/src/cli/smart-router-cli.ts +436 -0
  249. package/src/config/.gitkeep +0 -0
  250. package/src/config/defaults.ts +35 -0
  251. package/src/config/models-loader.ts +68 -0
  252. package/src/config/pi-model-mapper.ts +285 -0
  253. package/src/config/routing-clusters-loader.ts +148 -0
  254. package/src/domain/delegation/delegation-context.ts +111 -0
  255. package/src/domain/delegation/execution-ledger.ts +34 -0
  256. package/src/domain/delegation/output-headroom.ts +131 -0
  257. package/src/domain/matching/.gitkeep +0 -0
  258. package/src/domain/matching/cluster-matcher.ts +474 -0
  259. package/src/domain/matching/embedding-provider.ts +79 -0
  260. package/src/domain/matching/hydra-input.ts +53 -0
  261. package/src/domain/matching/hydra-matcher.ts +454 -0
  262. package/src/domain/pinning/.gitkeep +0 -0
  263. package/src/domain/pinning/cache-economics.ts +128 -0
  264. package/src/domain/pinning/loop-escalation.ts +181 -0
  265. package/src/domain/pinning/session-pinner.ts +388 -0
  266. package/src/domain/pinning/sub-route-policy.ts +119 -0
  267. package/src/domain/pipeline/.gitkeep +0 -0
  268. package/src/domain/pipeline/router-pipeline.ts +1139 -0
  269. package/src/domain/pipeline/safe-default.ts +70 -0
  270. package/src/domain/refactor-guardrails.ts +202 -0
  271. package/src/domain/routing/context-fit.ts +221 -0
  272. package/src/domain/routing/expected-cost.ts +320 -0
  273. package/src/domain/routing/p-success-classifier.ts +511 -0
  274. package/src/domain/routing/tier-features.ts +290 -0
  275. package/src/domain/routing/tool-history-guard.ts +159 -0
  276. package/src/domain/scoring/.gitkeep +0 -0
  277. package/src/domain/scoring/multi-objective.ts +175 -0
  278. package/src/domain/triage/.gitkeep +0 -0
  279. package/src/domain/triage/triage-engine.ts +373 -0
  280. package/src/domain/triage/turn-envelope.ts +104 -0
  281. package/src/domain/types/.gitkeep +0 -0
  282. package/src/domain/types/entities.ts +384 -0
  283. package/src/domain/types/index.ts +41 -0
  284. package/src/domain/types/schemas.ts +289 -0
  285. package/src/domain/types/store-port.ts +59 -0
  286. package/src/index.ts +103 -0
  287. package/src/infra/gemini-provider.ts +142 -0
  288. package/src/infra/telemetry.ts +169 -0
  289. package/src/infrastructure/delegation/provider-error.ts +251 -0
  290. package/src/infrastructure/gateway/.gitkeep +0 -0
  291. package/src/infrastructure/gateway/circuit-breaker.ts +196 -0
  292. package/src/infrastructure/gateway/gateway-dispatch.ts +444 -0
  293. package/src/infrastructure/hardware/.gitkeep +0 -0
  294. package/src/infrastructure/hardware/hardware-probe.ts +276 -0
  295. package/src/infrastructure/local/.gitkeep +0 -0
  296. package/src/infrastructure/local/local-zero-tier.ts +151 -0
  297. package/src/infrastructure/persistence/.gitkeep +0 -0
  298. package/src/infrastructure/persistence/memory-store.ts +122 -0
  299. package/src/infrastructure/persistence/sqlite-store.ts +900 -0
  300. package/src/infrastructure/pricing/.gitkeep +0 -0
  301. package/src/infrastructure/pricing/litellm-fetch.ts +215 -0
  302. package/src/infrastructure/pricing/price-broker.ts +183 -0
  303. package/src/infrastructure/pricing/pricing-monitor.ts +65 -0
  304. package/src/infrastructure/telemetry/.gitkeep +0 -0
  305. package/src/infrastructure/telemetry/dataset-limits.ts +39 -0
  306. package/src/infrastructure/telemetry/dataset-recorder.ts +215 -0
  307. package/src/infrastructure/telemetry/outcome-limits.ts +42 -0
  308. package/src/infrastructure/telemetry/outcome-recorder.ts +118 -0
  309. package/src/infrastructure/telemetry/routing-telemetry.ts +824 -0
  310. package/src/infrastructure/telemetry/telemetry-limits.ts +50 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Circuit breaker for gateway dispatch — T055, FR-018, FR-022.
3
+ *
4
+ * Tracks consecutive infrastructure failures per model endpoint and trips
5
+ * the breaker when the threshold is exceeded. Only infra errors (timeouts,
6
+ * network failures, 5xx) count — policy/safety rejections (4xx content
7
+ * filtering, 403 auth) do NOT trigger the breaker (FR-018).
8
+ *
9
+ * States: CLOSED → OPEN → HALF_OPEN → CLOSED (on success) or OPEN (on fail).
10
+ */
11
+ export type CircuitState = 'closed' | 'open' | 'half_open';
12
+ export interface CircuitBreakerConfig {
13
+ /** Consecutive infra failures before tripping the breaker. */
14
+ readonly failureThreshold: number;
15
+ /** Milliseconds to remain in OPEN state before allowing a probe. */
16
+ readonly resetTimeoutMs: number;
17
+ /** Max consecutive successes in HALF_OPEN required to close. */
18
+ readonly halfOpenSuccesses: number;
19
+ }
20
+ export declare const DEFAULT_CIRCUIT_CONFIG: CircuitBreakerConfig;
21
+ export interface CircuitSnapshot {
22
+ readonly modelId: string;
23
+ readonly state: CircuitState;
24
+ readonly consecutiveFailures: number;
25
+ readonly consecutiveSuccesses: number;
26
+ readonly lastFailureAt: number | null;
27
+ readonly lastStateChangeAt: number;
28
+ }
29
+ /**
30
+ * Returns true when the error is an infrastructure failure that should
31
+ * trigger the circuit breaker. Policy/safety rejections are excluded
32
+ * per FR-018.
33
+ */
34
+ export declare function isInfraError(error: {
35
+ statusCode?: number;
36
+ code?: string;
37
+ message?: string;
38
+ }): boolean;
39
+ export declare class CircuitBreaker {
40
+ private readonly circuits;
41
+ private readonly config;
42
+ constructor(config?: Partial<CircuitBreakerConfig>);
43
+ /**
44
+ * Check whether a model endpoint is available for requests.
45
+ * OPEN circuits reject immediately. OPEN circuits whose reset timeout
46
+ * has elapsed transition to HALF_OPEN and allow a probe.
47
+ */
48
+ canDispatch(modelId: string): boolean;
49
+ /**
50
+ * Record a successful response from a model endpoint.
51
+ * In HALF_OPEN, accumulates successes until the threshold is met,
52
+ * then transitions to CLOSED. In CLOSED, resets the failure counter.
53
+ */
54
+ recordSuccess(modelId: string): void;
55
+ /**
56
+ * Record an infrastructure failure from a model endpoint.
57
+ * Only infra errors should be reported here (caller checks isInfraError).
58
+ * HALF_OPEN → OPEN immediately. CLOSED → OPEN when threshold exceeded.
59
+ */
60
+ recordFailure(modelId: string): void;
61
+ /** Get a read-only snapshot of a model's circuit state. */
62
+ getSnapshot(modelId: string): CircuitSnapshot;
63
+ /** Reset a specific model's circuit to CLOSED. */
64
+ reset(modelId: string): void;
65
+ /** List all model IDs with OPEN circuits (unhealthy endpoints). */
66
+ getOpenCircuits(): readonly string[];
67
+ }
68
+ //# sourceMappingURL=circuit-breaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/gateway/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,gEAAgE;IAChE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAED,eAAO,MAAM,sBAAsB,EAAE,oBAIpC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAUD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAcrG;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;gBAElC,MAAM,GAAE,OAAO,CAAC,oBAAoB,CAAM;IAItD;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAmBrC;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAiBpC;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IA+BpC,2DAA2D;IAC3D,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;IAuB7C,kDAAkD;IAClD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI5B,mEAAmE;IACnE,eAAe,IAAI,SAAS,MAAM,EAAE;CASrC"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Circuit breaker for gateway dispatch — T055, FR-018, FR-022.
3
+ *
4
+ * Tracks consecutive infrastructure failures per model endpoint and trips
5
+ * the breaker when the threshold is exceeded. Only infra errors (timeouts,
6
+ * network failures, 5xx) count — policy/safety rejections (4xx content
7
+ * filtering, 403 auth) do NOT trigger the breaker (FR-018).
8
+ *
9
+ * States: CLOSED → OPEN → HALF_OPEN → CLOSED (on success) or OPEN (on fail).
10
+ */
11
+ export const DEFAULT_CIRCUIT_CONFIG = {
12
+ failureThreshold: 3,
13
+ resetTimeoutMs: 30_000,
14
+ halfOpenSuccesses: 2,
15
+ };
16
+ /**
17
+ * Returns true when the error is an infrastructure failure that should
18
+ * trigger the circuit breaker. Policy/safety rejections are excluded
19
+ * per FR-018.
20
+ */
21
+ export function isInfraError(error) {
22
+ if (error.code === 'ECONNREFUSED' || error.code === 'ECONNRESET' ||
23
+ error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND' ||
24
+ error.code === 'UND_ERR_CONNECT_TIMEOUT' || error.code === 'UND_ERR_SOCKET') {
25
+ return true;
26
+ }
27
+ if (error.statusCode !== undefined) {
28
+ if (error.statusCode >= 500 || error.statusCode === 429) {
29
+ return true;
30
+ }
31
+ }
32
+ return false;
33
+ }
34
+ export class CircuitBreaker {
35
+ circuits = new Map();
36
+ config;
37
+ constructor(config = {}) {
38
+ this.config = { ...DEFAULT_CIRCUIT_CONFIG, ...config };
39
+ }
40
+ /**
41
+ * Check whether a model endpoint is available for requests.
42
+ * OPEN circuits reject immediately. OPEN circuits whose reset timeout
43
+ * has elapsed transition to HALF_OPEN and allow a probe.
44
+ */
45
+ canDispatch(modelId) {
46
+ const entry = this.circuits.get(modelId);
47
+ if (!entry)
48
+ return true;
49
+ if (entry.state === 'closed' || entry.state === 'half_open') {
50
+ return true;
51
+ }
52
+ const elapsed = Date.now() - entry.lastStateChangeAt;
53
+ if (elapsed >= this.config.resetTimeoutMs) {
54
+ entry.state = 'half_open';
55
+ entry.consecutiveSuccesses = 0;
56
+ entry.lastStateChangeAt = Date.now();
57
+ return true;
58
+ }
59
+ return false;
60
+ }
61
+ /**
62
+ * Record a successful response from a model endpoint.
63
+ * In HALF_OPEN, accumulates successes until the threshold is met,
64
+ * then transitions to CLOSED. In CLOSED, resets the failure counter.
65
+ */
66
+ recordSuccess(modelId) {
67
+ const entry = this.circuits.get(modelId);
68
+ if (!entry)
69
+ return;
70
+ entry.consecutiveFailures = 0;
71
+ if (entry.state === 'half_open') {
72
+ entry.consecutiveSuccesses += 1;
73
+ if (entry.consecutiveSuccesses >= this.config.halfOpenSuccesses) {
74
+ entry.state = 'closed';
75
+ entry.lastStateChangeAt = Date.now();
76
+ }
77
+ }
78
+ else if (entry.state === 'closed') {
79
+ entry.consecutiveSuccesses = 0;
80
+ }
81
+ }
82
+ /**
83
+ * Record an infrastructure failure from a model endpoint.
84
+ * Only infra errors should be reported here (caller checks isInfraError).
85
+ * HALF_OPEN → OPEN immediately. CLOSED → OPEN when threshold exceeded.
86
+ */
87
+ recordFailure(modelId) {
88
+ const now = Date.now();
89
+ let entry = this.circuits.get(modelId);
90
+ if (!entry) {
91
+ entry = {
92
+ state: 'closed',
93
+ consecutiveFailures: 0,
94
+ consecutiveSuccesses: 0,
95
+ lastFailureAt: null,
96
+ lastStateChangeAt: now,
97
+ };
98
+ this.circuits.set(modelId, entry);
99
+ }
100
+ entry.consecutiveFailures += 1;
101
+ entry.lastFailureAt = now;
102
+ entry.consecutiveSuccesses = 0;
103
+ if (entry.state === 'half_open') {
104
+ entry.state = 'open';
105
+ entry.lastStateChangeAt = now;
106
+ return;
107
+ }
108
+ if (entry.consecutiveFailures >= this.config.failureThreshold) {
109
+ entry.state = 'open';
110
+ entry.lastStateChangeAt = now;
111
+ }
112
+ }
113
+ /** Get a read-only snapshot of a model's circuit state. */
114
+ getSnapshot(modelId) {
115
+ const entry = this.circuits.get(modelId);
116
+ if (!entry) {
117
+ return {
118
+ modelId,
119
+ state: 'closed',
120
+ consecutiveFailures: 0,
121
+ consecutiveSuccesses: 0,
122
+ lastFailureAt: null,
123
+ lastStateChangeAt: 0,
124
+ };
125
+ }
126
+ return {
127
+ modelId,
128
+ state: entry.state,
129
+ consecutiveFailures: entry.consecutiveFailures,
130
+ consecutiveSuccesses: entry.consecutiveSuccesses,
131
+ lastFailureAt: entry.lastFailureAt,
132
+ lastStateChangeAt: entry.lastStateChangeAt,
133
+ };
134
+ }
135
+ /** Reset a specific model's circuit to CLOSED. */
136
+ reset(modelId) {
137
+ this.circuits.delete(modelId);
138
+ }
139
+ /** List all model IDs with OPEN circuits (unhealthy endpoints). */
140
+ getOpenCircuits() {
141
+ const open = [];
142
+ for (const [modelId, entry] of this.circuits) {
143
+ if (entry.state === 'open') {
144
+ open.push(modelId);
145
+ }
146
+ }
147
+ return open;
148
+ }
149
+ }
150
+ //# sourceMappingURL=circuit-breaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../src/infrastructure/gateway/circuit-breaker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Gateway dispatch — T020, T055, T056, FR-001, FR-017, FR-018, FR-022, FR-023.
3
+ *
4
+ * Infrastructure entry point that delegates routing to the domain pipeline.
5
+ * Integrates circuit breaker for resilient failover (FR-018: infra errors only),
6
+ * weighted distribution across same-tier model endpoints (T056), and
7
+ * per-key rate limiting with 429 + Retry-After responses (T057, FR-017).
8
+ *
9
+ * FR-023: preserves provider context-caching semantics on same-provider
10
+ * request paths by tracking the last-used provider per session.
11
+ *
12
+ * SP-097: Cursor subscription quota exhaustion detection and failover to
13
+ * `cursor/auto` or economical API models with `cursor_quota_exhausted` telemetry.
14
+ */
15
+ import type { ModelProfile, RoutingDecision, RoutingRequest } from '../../domain/types/index.js';
16
+ import type { PipelineOptions } from '../../domain/pipeline/router-pipeline.js';
17
+ import { CircuitBreaker } from './circuit-breaker.js';
18
+ import type { CircuitBreakerConfig } from './circuit-breaker.js';
19
+ export interface CacheMarker {
20
+ readonly sessionId: string;
21
+ readonly provider: string;
22
+ readonly modelId: string;
23
+ readonly cacheFriendly: boolean;
24
+ }
25
+ export interface RateLimitResult {
26
+ readonly limited: true;
27
+ readonly error: 'rate_limit_exceeded';
28
+ /** Seconds until the bucket refills enough for the next request. */
29
+ readonly retry_after_seconds: number;
30
+ }
31
+ export interface RateLimitPort {
32
+ /** Attempt to consume a token for the given key. */
33
+ consumeToken(key: string, cost?: number): {
34
+ allowed: boolean;
35
+ remaining: number;
36
+ retryAfterSeconds: number | null;
37
+ };
38
+ }
39
+ export interface ProviderErrorShape {
40
+ readonly statusCode?: number;
41
+ readonly code?: string;
42
+ readonly message?: string;
43
+ }
44
+ /**
45
+ * Classify Cursor subscription quota / usage-limit errors from dogfood evidence
46
+ * (#70: "You've hit your usage limit… Switch to Auto for more usage…").
47
+ */
48
+ export declare function isCursorQuotaExhaustedError(error: ProviderErrorShape): boolean;
49
+ /** Cursor frontier models billed against subscription quota (composer-*, cursor/*). */
50
+ export declare function isCursorSubscriptionModel(model: ModelProfile): boolean;
51
+ /** Cursor opaque-auto models (`cursor/auto`, `cursor/*`). */
52
+ export declare function isCursorAutoModel(model: ModelProfile): boolean;
53
+ /**
54
+ * Whether a provider error should trigger model failover (infra or Cursor quota).
55
+ * Quota exhaustion on Cursor subscription models must not trip the circuit breaker.
56
+ */
57
+ export declare function shouldFailoverOnProviderError(error: ProviderErrorShape, failedModel?: ModelProfile): boolean;
58
+ export interface GatewayDispatchOptions extends PipelineOptions {
59
+ readonly circuitBreakerConfig?: Partial<CircuitBreakerConfig>;
60
+ readonly rateLimiter?: RateLimitPort;
61
+ }
62
+ export interface GatewayDispatchRequestOptions {
63
+ readonly effectiveFleet?: readonly ModelProfile[];
64
+ }
65
+ /**
66
+ * Select a model from same-tier candidates using inverse-cost weighting.
67
+ * Models with lower cost get proportionally more traffic. Falls back to
68
+ * uniform random when all costs are equal.
69
+ */
70
+ export declare function weightedSelect(candidates: readonly ModelProfile[]): ModelProfile | undefined;
71
+ export declare class GatewayDispatch {
72
+ private readonly pipeline;
73
+ private readonly fleet;
74
+ private readonly cacheMarkers;
75
+ private readonly circuitBreaker;
76
+ private readonly rateLimiter;
77
+ private readonly quotaExhaustedModels;
78
+ constructor(fleet: readonly ModelProfile[], options?: GatewayDispatchOptions);
79
+ /**
80
+ * Route a single request through the pipeline.
81
+ *
82
+ * 1. Run pipeline to get routing decision.
83
+ * 2. Verify circuit breaker allows dispatch to the selected model.
84
+ * 3. If the selected model's circuit is open, attempt failover to a
85
+ * same-tier alternative (T056).
86
+ * 4. Update FR-023 cache marker.
87
+ *
88
+ * Never throws.
89
+ */
90
+ dispatch(request: RoutingRequest, options?: GatewayDispatchRequestOptions): Promise<RoutingDecision>;
91
+ /**
92
+ * Route with per-key rate limiting (T057, FR-017).
93
+ *
94
+ * Checks the token bucket before routing. Returns a RateLimitResult with
95
+ * 429-compatible body `{ error, retry_after_seconds }` when the bucket is
96
+ * exhausted. Otherwise delegates to dispatch().
97
+ */
98
+ dispatchWithRateLimit(request: RoutingRequest, rateLimitKey: string): Promise<RoutingDecision | RateLimitResult>;
99
+ /**
100
+ * Record an upstream response outcome for circuit breaker tracking.
101
+ * Only infra errors trip the breaker; policy/safety rejections are ignored (FR-018).
102
+ * Cursor quota exhaustion is tracked for SP-097 failover without opening circuits.
103
+ */
104
+ recordOutcome(modelId: string, error?: ProviderErrorShape): void;
105
+ /** Expose circuit breaker for observability and testing. */
106
+ getCircuitBreaker(): CircuitBreaker;
107
+ /** Whether a model recently returned a Cursor quota exhaustion error (SP-097). */
108
+ hasQuotaExhaustion(modelId: string): boolean;
109
+ /**
110
+ * Retrieve the current cache marker for a session (FR-023 inspection).
111
+ */
112
+ getCacheMarker(sessionId: string): CacheMarker | null;
113
+ /**
114
+ * Select an alternate model when the current target is unavailable or failed.
115
+ * Prefers same-tier healthy models with closed circuits, then any tier.
116
+ * When the failed model hit Cursor quota limits, prefers `cursor/auto` then
117
+ * economical API models with `cursor_quota_exhausted`.
118
+ */
119
+ selectFailover(decision: RoutingDecision, excludeModelIds?: readonly string[], fleetOverride?: readonly ModelProfile[]): RoutingDecision | undefined;
120
+ /**
121
+ * If the selected model's circuit is open, attempt to fail over to
122
+ * an alternative model on the same tier (T056 weighted distribution).
123
+ * Falls back to any healthy model on any tier if no same-tier alternative exists.
124
+ */
125
+ private applyCircuitBreaker;
126
+ /**
127
+ * SP-097: fail over from quota-exhausted Cursor models to `cursor/auto` or
128
+ * the cheapest viable economical API model in the scoped fleet.
129
+ */
130
+ private selectCursorQuotaFailover;
131
+ /**
132
+ * FR-023: track the provider and cache-friendly status for the session.
133
+ * Sub-routed requests on the same provider preserve the existing marker
134
+ * rather than replacing it — the pin model's cache state is authoritative.
135
+ */
136
+ private updateCacheMarker;
137
+ }
138
+ //# sourceMappingURL=gateway-dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-dispatch.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/gateway/gateway-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACf,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,EAAE,cAAc,EAAgB,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIjE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAID,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,oEAAoE;IACpE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACrH;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAiBD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAgB9E;AAED,uFAAuF;AACvF,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAQtE;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAG9D;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,kBAAkB,EACzB,WAAW,CAAC,EAAE,YAAY,GACzB,OAAO,CAUT;AAID,MAAM,WAAW,sBAAuB,SAAQ,eAAe;IAC7D,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,QAAQ,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC;CACtC;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACnD;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,SAAS,YAAY,EAAE,GAClC,YAAY,GAAG,SAAS,CAqB1B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;IACxD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;gBAE9C,KAAK,EAAE,SAAS,YAAY,EAAE,EAAE,OAAO,CAAC,EAAE,sBAAsB;IAO5E;;;;;;;;;;OAUG;IACG,QAAQ,CACZ,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,eAAe,CAAC;IAQ3B;;;;;;OAMG;IACG,qBAAqB,CACzB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,GAAG,eAAe,CAAC;IAe7C;;;;OAIG;IACH,aAAa,CACX,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,kBAAkB,GACzB,IAAI;IAsBP,4DAA4D;IAC5D,iBAAiB,IAAI,cAAc;IAInC,kFAAkF;IAClF,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI5C;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAIrD;;;;;OAKG;IACH,cAAc,CACZ,QAAQ,EAAE,eAAe,EACzB,eAAe,GAAE,SAAS,MAAM,EAAO,EACvC,aAAa,CAAC,EAAE,SAAS,YAAY,EAAE,GACtC,eAAe,GAAG,SAAS;IAgD9B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAuDjC;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;CAwB1B"}