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,41 @@
1
+ # Example routing cluster catalog — copy to config/routing-clusters.yaml and customize.
2
+ # Reference prompts are embedded at load time; centroids are the mean embedding per cluster.
3
+ # Reason codes use stable prefixes: cluster_${id} (e.g. cluster_low_stakes_general).
4
+ # See specs/001-build-smart-router/data-model.md and GitHub issue #55.
5
+
6
+ clusters:
7
+ - id: low_stakes_general
8
+ tier_bias: zero-tier
9
+ reference_prompts:
10
+ - "what is 2+2"
11
+ - "define polymorphism in one sentence"
12
+ - "explain what a closure is"
13
+ min_similarity: 0.82
14
+ min_margin: 0.05
15
+
16
+ - id: mechanical_edit
17
+ tier_bias: zero-tier
18
+ reference_prompts:
19
+ - "format this file with prettier"
20
+ - "fix lint errors"
21
+ - "add semicolons to this file"
22
+ min_similarity: 0.80
23
+ min_margin: 0.05
24
+
25
+ - id: deep_debug
26
+ tier_bias: frontier-cloud
27
+ reference_prompts:
28
+ - "race condition in async handler"
29
+ - "memory leak in production service"
30
+ - "debug intermittent test failure"
31
+ min_similarity: 0.78
32
+ min_margin: 0.04
33
+
34
+ - id: architecture
35
+ tier_bias: frontier-cloud
36
+ reference_prompts:
37
+ - "design a microservices migration"
38
+ - "system design for a distributed cache"
39
+ - "architect an event sourcing layer"
40
+ min_similarity: 0.78
41
+ min_margin: 0.04
@@ -0,0 +1,25 @@
1
+ [
2
+ {
3
+ "version": 1,
4
+ "timestamp": "2026-07-07T12:00:00.000Z",
5
+ "session_id_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
6
+ "turn_type": "main_loop",
7
+ "stage": "hydra_match",
8
+ "reason_code": "hydra_embedding_match",
9
+ "selected_model_id": "gpt-4o-mini",
10
+ "tier": "economical-cloud",
11
+ "routing_latency_ms": 12,
12
+ "estimated_cost_usd": 0.001,
13
+ "estimated_input_tokens": 60,
14
+ "has_tool_context": false,
15
+ "compaction_flag": false,
16
+ "triage_verdict": "ambiguous",
17
+ "triage_reason_code": "mixed_signals",
18
+ "triage_cyclomatic_score": 1,
19
+ "requirement_reasoning": 0.5,
20
+ "requirement_code_gen": 0.4,
21
+ "requirement_tool_use": 0.2,
22
+ "success_label": true,
23
+ "outcome_signals": ["feedback_good"]
24
+ }
25
+ ]
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Explain endpoint handler — T041.
3
+ *
4
+ * Returns routing rationale (tier, stage, reason_code, candidates) without
5
+ * dispatching upstream inference. Runs the same pipeline as the live path
6
+ * to guarantee bit-for-bit decision equivalence.
7
+ *
8
+ * Contract: specs/001-build-smart-router/contracts/explain-endpoint.md v1.0.0
9
+ *
10
+ * Invariants:
11
+ * - MUST NOT call upstream LLM providers
12
+ * - MUST produce a decision identical to the live pipeline for the same input
13
+ * - MUST emit no RoutingTelemetry with upstream cost (routing_latency_ms only)
14
+ */
15
+ import type { ModelProfile, RoutingDecision } from '../../domain/types/index.js';
16
+ import { RouterPipeline } from '../../domain/pipeline/router-pipeline.js';
17
+ import type { ClusterMatcher } from '../../domain/matching/cluster-matcher.js';
18
+ export interface ExplainValidationError {
19
+ readonly error: 'validation_failed';
20
+ readonly details: readonly string[];
21
+ }
22
+ export interface ExplainSuccess {
23
+ readonly status: 200;
24
+ readonly body: RoutingDecision;
25
+ }
26
+ export interface ExplainBadRequest {
27
+ readonly status: 400;
28
+ readonly body: ExplainValidationError;
29
+ }
30
+ export interface ExplainUnavailable {
31
+ readonly status: 503;
32
+ readonly body: RoutingDecision;
33
+ }
34
+ export type ExplainResult = ExplainSuccess | ExplainBadRequest | ExplainUnavailable;
35
+ export interface ExplainHandlerDeps {
36
+ readonly fleet: readonly ModelProfile[];
37
+ readonly pipeline: RouterPipeline;
38
+ readonly clusterMatcher?: ClusterMatcher;
39
+ }
40
+ /**
41
+ * Create an explain handler bound to a shared pipeline instance.
42
+ *
43
+ * Reuses the same RouterPipeline as the live dispatch path so
44
+ * session pin state and stage ordering are identical.
45
+ */
46
+ export declare function createExplainHandler(deps: ExplainHandlerDeps): (rawBody: unknown) => Promise<ExplainResult>;
47
+ //# sourceMappingURL=router-explain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-explain.d.ts","sourceRoot":"","sources":["../../../src/api/explain/router-explain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAA2B,MAAM,6BAA6B,CAAC;AAE1G,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAK/E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAIpF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;CAC1C;AAID;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,IAG7B,SAAS,OAAO,KAAG,OAAO,CAAC,aAAa,CAAC,CA+CxE"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Explain endpoint handler — T041.
3
+ *
4
+ * Returns routing rationale (tier, stage, reason_code, candidates) without
5
+ * dispatching upstream inference. Runs the same pipeline as the live path
6
+ * to guarantee bit-for-bit decision equivalence.
7
+ *
8
+ * Contract: specs/001-build-smart-router/contracts/explain-endpoint.md v1.0.0
9
+ *
10
+ * Invariants:
11
+ * - MUST NOT call upstream LLM providers
12
+ * - MUST produce a decision identical to the live pipeline for the same input
13
+ * - MUST emit no RoutingTelemetry with upstream cost (routing_latency_ms only)
14
+ */
15
+ import { RoutingRequestSchema } from '../../domain/types/schemas.js';
16
+ import { safeCloudDefault } from '../../domain/pipeline/safe-default.js';
17
+ import { enrichRoutingDecisionForExplain } from '../../infrastructure/telemetry/routing-telemetry.js';
18
+ // ─── Handler factory ──────────────────────────────────────────────────────────
19
+ /**
20
+ * Create an explain handler bound to a shared pipeline instance.
21
+ *
22
+ * Reuses the same RouterPipeline as the live dispatch path so
23
+ * session pin state and stage ordering are identical.
24
+ */
25
+ export function createExplainHandler(deps) {
26
+ const { fleet, pipeline, clusterMatcher } = deps;
27
+ return async function explain(rawBody) {
28
+ const parsed = RoutingRequestSchema.safeParse(rawBody);
29
+ if (!parsed.success) {
30
+ return {
31
+ status: 400,
32
+ body: {
33
+ error: 'validation_failed',
34
+ details: parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`),
35
+ },
36
+ };
37
+ }
38
+ const request = toRoutingRequest(parsed.data);
39
+ const start = performance.now();
40
+ try {
41
+ const decision = await pipeline.route(request);
42
+ return {
43
+ status: 200,
44
+ body: await enrichRoutingDecisionForExplain(request, decision, {
45
+ fleet,
46
+ ...(clusterMatcher !== undefined ? { clusterMatcher } : {}),
47
+ }),
48
+ };
49
+ }
50
+ catch {
51
+ const fallbackModel = safeCloudDefault(fleet);
52
+ const decision = {
53
+ request_id: request.request_id,
54
+ selected_model_id: fallbackModel?.id ?? 'unknown',
55
+ tier: fallbackModel?.tier ?? 'economical-cloud',
56
+ stage: 'fallback',
57
+ reason_code: 'safe_default',
58
+ routing_latency_ms: performance.now() - start,
59
+ pin_reason: null,
60
+ };
61
+ return {
62
+ status: 503,
63
+ body: await enrichRoutingDecisionForExplain(request, decision, {
64
+ fleet,
65
+ ...(clusterMatcher !== undefined ? { clusterMatcher } : {}),
66
+ }),
67
+ };
68
+ }
69
+ };
70
+ }
71
+ /**
72
+ * Map Zod-validated data to the domain RoutingRequest entity, bridging
73
+ * schema field names (tool_call_id/tool_calls) to entity shape (tool_blocks)
74
+ * and satisfying exactOptionalPropertyTypes constraints.
75
+ */
76
+ function toRoutingRequest(data) {
77
+ // Build with known required fields, then add optional fields only when present
78
+ // to satisfy exactOptionalPropertyTypes (no explicit undefined values).
79
+ const result = {
80
+ request_id: data.request_id,
81
+ session_id: data.session_id,
82
+ prompt_text: data.prompt_text,
83
+ };
84
+ if (data.messages !== undefined) {
85
+ result['messages'] = data.messages.map((m) => ({
86
+ role: m.role,
87
+ content: m.content,
88
+ ...(m.tool_calls !== undefined ? { tool_blocks: m.tool_calls } : {}),
89
+ }));
90
+ }
91
+ if (data.turn_type !== undefined)
92
+ result['turn_type'] = data.turn_type;
93
+ if (data.compaction_flag !== undefined)
94
+ result['compaction_flag'] = data.compaction_flag;
95
+ if (data.force_model_id !== undefined)
96
+ result['force_model_id'] = data.force_model_id;
97
+ if (data.estimated_input_tokens !== undefined)
98
+ result['estimated_input_tokens'] = data.estimated_input_tokens;
99
+ return result;
100
+ }
101
+ //# sourceMappingURL=router-explain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-explain.js","sourceRoot":"","sources":["../../../src/api/explain/router-explain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Pi lifecycle hook integration — T021, T021b, SP-055.
3
+ *
4
+ * Registers compaction and model-override hooks on pi extension events.
5
+ * Lifecycle flags are consumed when building the next routing request
6
+ * (extension `buildRoutingRequest` or embedder `dispatch.dispatch`).
7
+ *
8
+ * Routing and stream delegation live in `.pi/extensions/smart-router/` for
9
+ * pi users, or in embedder code that calls `GatewayDispatch.dispatch()`.
10
+ * This module does not register routing or no-op context hooks.
11
+ *
12
+ * Contract: specs/001-build-smart-router/contracts/pi-middleware.md v1.0.0
13
+ */
14
+ export interface PiSessionManager {
15
+ getSessionFile(): string | undefined;
16
+ getSessionId(): string;
17
+ }
18
+ export interface PiExtensionContext {
19
+ readonly cwd: string;
20
+ readonly sessionManager: PiSessionManager;
21
+ }
22
+ export interface PiMessage {
23
+ readonly role: string;
24
+ readonly content: string;
25
+ readonly tool_call_id?: string;
26
+ readonly tool_calls?: readonly unknown[];
27
+ }
28
+ export interface PiContextEvent {
29
+ readonly messages: readonly PiMessage[];
30
+ }
31
+ export interface PiModelSelectEvent {
32
+ readonly source: string;
33
+ readonly model: {
34
+ readonly provider: string;
35
+ readonly id: string;
36
+ };
37
+ }
38
+ /** Legacy embedder event shape; routing is handled in the pi extension stream path. */
39
+ export interface PiProviderRequestEvent {
40
+ readonly provider: string;
41
+ readonly model: string;
42
+ readonly messages?: readonly PiMessage[];
43
+ [key: string]: unknown;
44
+ }
45
+ export interface PiExtensionHooks {
46
+ on(event: 'before_provider_request', handler: (event: PiProviderRequestEvent, ctx: PiExtensionContext) => PiProviderRequestEvent | Promise<PiProviderRequestEvent>): void;
47
+ on(event: 'context', handler: (event: PiContextEvent, ctx: PiExtensionContext) => void): void;
48
+ on(event: 'session_compact', handler: (event: unknown, ctx: PiExtensionContext) => void): void;
49
+ on(event: 'session_before_compact', handler: (event: unknown, ctx: PiExtensionContext) => void): void;
50
+ on(event: 'model_select', handler: (event: PiModelSelectEvent, ctx: PiExtensionContext) => void): void;
51
+ }
52
+ export interface LifecycleFlags {
53
+ readonly compaction_flag?: boolean;
54
+ readonly force_model_id?: string;
55
+ }
56
+ /**
57
+ * Per-session lifecycle flags set by pi compaction and model_select hooks.
58
+ * Shared across router rebuilds so hook state survives fleet refresh.
59
+ */
60
+ export declare class LifecycleHookState {
61
+ private readonly sessions;
62
+ private getOrCreate;
63
+ markCompaction(sessionId: string): void;
64
+ setForceModel(sessionId: string, modelId: string): void;
65
+ /**
66
+ * Consume lifecycle flags for the next routing request.
67
+ * Compaction is one-shot; force_model_id is one-shot per consume.
68
+ */
69
+ consume(sessionId: string): LifecycleFlags;
70
+ }
71
+ export interface PiRouterMiddlewareOptions {
72
+ readonly lifecycleHookState?: LifecycleHookState;
73
+ }
74
+ export interface PiRouterMiddleware {
75
+ readonly register: (hooks: PiExtensionHooks) => void;
76
+ readonly lifecycleHookState: LifecycleHookState;
77
+ }
78
+ /**
79
+ * Create pi lifecycle hook handlers for session compaction and model overrides.
80
+ *
81
+ * Library embedders: call `router.register(hooks)` on the returned `RouterHandle`
82
+ * and route via `router.dispatch.dispatch()`. For pi, use the project extension at
83
+ * `.pi/extensions/smart-router/` — it owns stream delegation and routing telemetry.
84
+ */
85
+ export declare function createPiRouterMiddleware(options?: PiRouterMiddlewareOptions): PiRouterMiddleware;
86
+ //# sourceMappingURL=pi-router-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-router-middleware.d.ts","sourceRoot":"","sources":["../../../src/api/middleware/pi-router-middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Pi lifecycle hook integration — T021, T021b, SP-055.
3
+ *
4
+ * Registers compaction and model-override hooks on pi extension events.
5
+ * Lifecycle flags are consumed when building the next routing request
6
+ * (extension `buildRoutingRequest` or embedder `dispatch.dispatch`).
7
+ *
8
+ * Routing and stream delegation live in `.pi/extensions/smart-router/` for
9
+ * pi users, or in embedder code that calls `GatewayDispatch.dispatch()`.
10
+ * This module does not register routing or no-op context hooks.
11
+ *
12
+ * Contract: specs/001-build-smart-router/contracts/pi-middleware.md v1.0.0
13
+ */
14
+ /**
15
+ * Per-session lifecycle flags set by pi compaction and model_select hooks.
16
+ * Shared across router rebuilds so hook state survives fleet refresh.
17
+ */
18
+ export class LifecycleHookState {
19
+ sessions = new Map();
20
+ getOrCreate(sessionId) {
21
+ let state = this.sessions.get(sessionId);
22
+ if (!state) {
23
+ state = { compactionPending: false };
24
+ this.sessions.set(sessionId, state);
25
+ }
26
+ return state;
27
+ }
28
+ markCompaction(sessionId) {
29
+ this.getOrCreate(sessionId).compactionPending = true;
30
+ }
31
+ setForceModel(sessionId, modelId) {
32
+ const state = this.getOrCreate(sessionId);
33
+ state.forceModelId = modelId;
34
+ }
35
+ /**
36
+ * Consume lifecycle flags for the next routing request.
37
+ * Compaction is one-shot; force_model_id is one-shot per consume.
38
+ */
39
+ consume(sessionId) {
40
+ const state = this.sessions.get(sessionId);
41
+ if (!state) {
42
+ return {};
43
+ }
44
+ const flags = {
45
+ ...(state.compactionPending ? { compaction_flag: true } : {}),
46
+ ...(state.forceModelId !== undefined ? { force_model_id: state.forceModelId } : {}),
47
+ };
48
+ state.compactionPending = false;
49
+ delete state.forceModelId;
50
+ if (!state.compactionPending && state.forceModelId === undefined) {
51
+ this.sessions.delete(sessionId);
52
+ }
53
+ return flags;
54
+ }
55
+ }
56
+ function resolveHookSessionId(ctx) {
57
+ return ctx.sessionManager.getSessionId();
58
+ }
59
+ /**
60
+ * Create pi lifecycle hook handlers for session compaction and model overrides.
61
+ *
62
+ * Library embedders: call `router.register(hooks)` on the returned `RouterHandle`
63
+ * and route via `router.dispatch.dispatch()`. For pi, use the project extension at
64
+ * `.pi/extensions/smart-router/` — it owns stream delegation and routing telemetry.
65
+ */
66
+ export function createPiRouterMiddleware(options) {
67
+ const lifecycleHookState = options?.lifecycleHookState ?? new LifecycleHookState();
68
+ function register(hooks) {
69
+ hooks.on('session_compact', (_event, ctx) => {
70
+ lifecycleHookState.markCompaction(resolveHookSessionId(ctx));
71
+ });
72
+ hooks.on('session_before_compact', (_event, ctx) => {
73
+ lifecycleHookState.markCompaction(resolveHookSessionId(ctx));
74
+ });
75
+ hooks.on('model_select', (event, ctx) => {
76
+ if (event.source === 'set') {
77
+ lifecycleHookState.setForceModel(resolveHookSessionId(ctx), event.model.id);
78
+ }
79
+ });
80
+ }
81
+ return { register, lifecycleHookState };
82
+ }
83
+ //# sourceMappingURL=pi-router-middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-router-middleware.js","sourceRoot":"","sources":["../../../src/api/middleware/pi-router-middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Smart Router CLI subcommands — library-facing command handlers.
3
+ *
4
+ * Extension wiring lives in `.pi/extensions/smart-router/commands.ts`;
5
+ * this module holds reusable subcommand logic for dogfooding and tests.
6
+ */
7
+ import { SessionPinner } from '../domain/pinning/session-pinner.js';
8
+ import type { RoutingDatasetRecord, RoutingOutcomeRecord } from '../domain/types/index.js';
9
+ import type { StorePort } from '../domain/types/store-port.js';
10
+ export declare const UNPIN_SUBCOMMAND: "unpin";
11
+ export declare const EXPORT_TELEMETRY_CONTRIB_COMMAND: "export telemetry-contrib";
12
+ export declare const TELEMETRY_CONTRIB_VERSION: 1;
13
+ export declare const TELEMETRY_CONTRIB_SCHEMA_PATH = "specs/001-build-smart-router/contracts/telemetry-contrib.schema.json";
14
+ export declare const DEFAULT_TELEMETRY_CONTRIB_EXPORT_DIR = ".pi-smart-router/exports";
15
+ export declare const DEFAULT_TELEMETRY_CONTRIB_EXPORT_LIMIT = 10000;
16
+ /** Keys stripped from contrib export (install-local identifiers and prompt-adjacent fields). */
17
+ export declare const TELEMETRY_CONTRIB_STRIP_KEYS: readonly ["request_id", "session_id", "prompt_text", "messages", "prompt", "prompt_fingerprint", "pepper", "install_pepper", "dataset_pepper", "dataset_key", "pepper_key", "install_key", "candidates_json", "context_fit_rejected_json"];
18
+ /** Mirrors SP-116 `CALIBRATION_CONTRIB_REJECT_KEYS` for ingest-safe export. */
19
+ export declare const TELEMETRY_CONTRIB_REJECT_KEYS: readonly ["session_id", "prompt_text", "messages", "prompt", "prompt_fingerprint"];
20
+ /** Mirrors SP-116 tainted key pattern for ingest-safe export. */
21
+ export declare const TELEMETRY_CONTRIB_TAINTED_KEY_PATTERN: RegExp;
22
+ export declare class TelemetryContribValidationError extends Error {
23
+ readonly name = "TelemetryContribValidationError";
24
+ constructor(message: string, options?: ErrorOptions);
25
+ }
26
+ /** Fail closed when a contrib row contains forbidden or tainted keys (SP-116 parity). */
27
+ export declare function assertTelemetryContribRecordSafe(record: unknown, context?: string): asserts record is Record<string, unknown>;
28
+ /** Strip install-local pepper fields from a validated contrib row (SP-116 parity). */
29
+ export declare function sanitizeTelemetryContribRecord(record: Record<string, unknown>): Record<string, unknown>;
30
+ export type UnpinOutcome = 'cleared' | 'noop' | 'unavailable';
31
+ export interface UnpinCommandResult {
32
+ readonly outcome: UnpinOutcome;
33
+ readonly message: string;
34
+ readonly level: 'info' | 'error';
35
+ readonly previousModelId?: string;
36
+ }
37
+ export interface UnpinCommandContext {
38
+ readonly sessionId: string;
39
+ readonly sessionPinner: SessionPinner | undefined;
40
+ }
41
+ export interface ExportTelemetryContribOptions {
42
+ readonly limit?: number;
43
+ readonly cwd?: string;
44
+ readonly writeFile?: boolean;
45
+ }
46
+ export interface ExportTelemetryContribResult {
47
+ readonly path: string | null;
48
+ readonly recordCount: number;
49
+ readonly json: string;
50
+ }
51
+ export interface TelemetryContribExportContext {
52
+ readonly store: StorePort;
53
+ readonly cwd: string;
54
+ readonly limit: number;
55
+ }
56
+ /** Returns true when args invoke `/smart-router unpin`. */
57
+ export declare function isUnpinInvocation(args: string): boolean;
58
+ /** Returns true when args invoke `export telemetry-contrib`. */
59
+ export declare function isExportTelemetryContribInvocation(args: string): boolean;
60
+ export declare function parseExportTelemetryContribArgs(args: string): {
61
+ readonly limit: number;
62
+ };
63
+ /**
64
+ * Clear the current session pin via SessionPinner.breakPin().
65
+ * Does not modify SessionPinner break rules — operator-initiated unpin only.
66
+ */
67
+ export declare function executeUnpinCommand(ctx: UnpinCommandContext): UnpinCommandResult;
68
+ export declare function hashSessionIdForContribExport(sessionId: string): string;
69
+ /** Map a dataset row plus linked outcomes to a privacy-safe telemetry contrib record. */
70
+ export declare function toTelemetryContribRecord(record: RoutingDatasetRecord, outcomes?: readonly RoutingOutcomeRecord[]): Record<string, unknown>;
71
+ export declare function formatTelemetryContribJson(records: readonly Record<string, unknown>[]): string;
72
+ export declare function formatTelemetryContribJsonl(records: readonly Record<string, unknown>[]): string;
73
+ export declare function formatTelemetryContribExportTimestamp(date?: Date): string;
74
+ export declare function getTelemetryContribExportPath(cwd: string, timestamp: string): string;
75
+ /** Defense-in-depth validation using SP-116 contrib safety checks. */
76
+ export declare function validateTelemetryContribRecord(record: unknown, context?: string): Record<string, unknown>;
77
+ export declare function buildTelemetryContribRecords(datasetRecords: readonly RoutingDatasetRecord[], outcomeRecords?: readonly RoutingOutcomeRecord[]): Record<string, unknown>[];
78
+ export declare function exportTelemetryContrib(ctx: TelemetryContribExportContext, options?: Pick<ExportTelemetryContribOptions, 'writeFile'>): Promise<ExportTelemetryContribResult>;
79
+ /** Join dataset export shape with outcome labels, then strip to contrib-safe fields. */
80
+ export declare function datasetExportRowToTelemetryContrib(exportRecord: Record<string, unknown>, outcomes: readonly RoutingOutcomeRecord[]): Record<string, unknown>;
81
+ export declare const smartRouterCliSubcommands: {
82
+ readonly unpin: typeof executeUnpinCommand;
83
+ readonly exportTelemetryContrib: typeof exportTelemetryContrib;
84
+ };
85
+ //# sourceMappingURL=smart-router-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smart-router-cli.d.ts","sourceRoot":"","sources":["../../src/cli/smart-router-cli.ts"],"names":[],"mappings":"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"}