profclaw 2.0.0 → 2.3.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 (477) hide show
  1. package/.env.example +14 -2
  2. package/CHANGELOG.md +37 -0
  3. package/CONTRIBUTING.md +20 -0
  4. package/Dockerfile +39 -23
  5. package/README.md +174 -40
  6. package/dist/agents/agent-summary.d.ts +44 -0
  7. package/dist/agents/agent-summary.js +176 -0
  8. package/dist/agents/auto-memory.d.ts +59 -0
  9. package/dist/agents/auto-memory.js +310 -0
  10. package/dist/agents/checkpoint-manager.d.ts +76 -0
  11. package/dist/agents/checkpoint-manager.js +156 -0
  12. package/dist/agents/circuit-breaker.d.ts +54 -0
  13. package/dist/agents/circuit-breaker.js +153 -0
  14. package/dist/agents/context-compactor.d.ts +70 -0
  15. package/dist/agents/context-compactor.js +361 -0
  16. package/dist/agents/conversation-branch.d.ts +55 -0
  17. package/dist/agents/conversation-branch.js +115 -0
  18. package/dist/agents/error-recovery.d.ts +44 -0
  19. package/dist/agents/error-recovery.js +197 -0
  20. package/dist/agents/events.d.ts +76 -0
  21. package/dist/agents/events.js +8 -0
  22. package/dist/agents/executor.d.ts +35 -1
  23. package/dist/agents/executor.js +573 -124
  24. package/dist/agents/file-snapshots.d.ts +72 -0
  25. package/dist/agents/file-snapshots.js +182 -0
  26. package/dist/agents/index.d.ts +2 -0
  27. package/dist/agents/orchestrator.d.ts +183 -0
  28. package/dist/agents/orchestrator.js +495 -0
  29. package/dist/agents/permissions.d.ts +63 -0
  30. package/dist/agents/permissions.js +138 -0
  31. package/dist/agents/plan-mode.d.ts +71 -0
  32. package/dist/agents/plan-mode.js +186 -0
  33. package/dist/agents/prompt-suggestions.d.ts +33 -0
  34. package/dist/agents/prompt-suggestions.js +208 -0
  35. package/dist/agents/rate-limit-monitor.d.ts +56 -0
  36. package/dist/agents/rate-limit-monitor.js +224 -0
  37. package/dist/agents/result-store.d.ts +37 -0
  38. package/dist/agents/result-store.js +104 -0
  39. package/dist/agents/session-diff.d.ts +43 -0
  40. package/dist/agents/session-diff.js +258 -0
  41. package/dist/agents/tool-loader.d.ts +65 -0
  42. package/dist/agents/tool-loader.js +273 -0
  43. package/dist/agents/transcript.d.ts +96 -0
  44. package/dist/agents/transcript.js +267 -0
  45. package/dist/agents/worktree-manager.d.ts +80 -0
  46. package/dist/agents/worktree-manager.js +203 -0
  47. package/dist/auth/middleware.js +14 -0
  48. package/dist/chat/agentic-executor.d.ts +7 -0
  49. package/dist/chat/agentic-executor.js +253 -30
  50. package/dist/chat/conversations.d.ts +2 -0
  51. package/dist/chat/conversations.js +8 -0
  52. package/dist/chat/execution/executor.d.ts +8 -0
  53. package/dist/chat/execution/executor.js +157 -103
  54. package/dist/chat/execution/index.js +5 -1
  55. package/dist/chat/execution/tool-router.js +40 -14
  56. package/dist/chat/execution/tools/canvas.d.ts +2 -2
  57. package/dist/chat/execution/tools/code-tools.d.ts +66 -0
  58. package/dist/chat/execution/tools/code-tools.js +353 -0
  59. package/dist/chat/execution/tools/complete-task.d.ts +2 -2
  60. package/dist/chat/execution/tools/cron-tool.d.ts +26 -10
  61. package/dist/chat/execution/tools/cron-tool.js +48 -3
  62. package/dist/chat/execution/tools/decompose-task.d.ts +58 -0
  63. package/dist/chat/execution/tools/decompose-task.js +166 -0
  64. package/dist/chat/execution/tools/exec.d.ts +5 -2
  65. package/dist/chat/execution/tools/exec.js +294 -155
  66. package/dist/chat/execution/tools/feed-tools.d.ts +77 -0
  67. package/dist/chat/execution/tools/feed-tools.js +142 -0
  68. package/dist/chat/execution/tools/file-ops.d.ts +1 -1
  69. package/dist/chat/execution/tools/github.d.ts +2 -2
  70. package/dist/chat/execution/tools/index.d.ts +14 -2
  71. package/dist/chat/execution/tools/index.js +86 -12
  72. package/dist/chat/execution/tools/integrations.d.ts +8 -8
  73. package/dist/chat/execution/tools/multi-patch.d.ts +53 -0
  74. package/dist/chat/execution/tools/multi-patch.js +373 -0
  75. package/dist/chat/execution/tools/openai-image-gen.d.ts +2 -2
  76. package/dist/chat/execution/tools/plan-tools.d.ts +80 -0
  77. package/dist/chat/execution/tools/plan-tools.js +134 -0
  78. package/dist/chat/execution/tools/profclaw-ops.d.ts +6 -6
  79. package/dist/chat/execution/tools/repl-tool.d.ts +46 -0
  80. package/dist/chat/execution/tools/repl-tool.js +207 -0
  81. package/dist/chat/execution/tools/sessions-send.d.ts +2 -2
  82. package/dist/chat/execution/tools/slack-actions.d.ts +2 -2
  83. package/dist/chat/execution/tools/system.d.ts +4 -4
  84. package/dist/chat/execution/tools/todo-tool.d.ts +72 -0
  85. package/dist/chat/execution/tools/todo-tool.js +152 -0
  86. package/dist/chat/execution/tools/tool-search.d.ts +50 -0
  87. package/dist/chat/execution/tools/tool-search.js +155 -0
  88. package/dist/chat/execution/types.d.ts +21 -14
  89. package/dist/chat/execution/workflows/executor.d.ts +11 -0
  90. package/dist/chat/execution/workflows/executor.js +99 -1
  91. package/dist/chat/execution/workflows/types.d.ts +35 -2
  92. package/dist/chat/format/index.d.ts +1 -0
  93. package/dist/chat/format/index.js +1 -0
  94. package/dist/chat/format/markdown-convert.d.ts +23 -0
  95. package/dist/chat/format/markdown-convert.js +149 -0
  96. package/dist/chat/index.d.ts +1 -1
  97. package/dist/chat/index.js +1 -1
  98. package/dist/chat/message-handler.js +58 -6
  99. package/dist/chat/proactive/index.d.ts +62 -0
  100. package/dist/chat/proactive/index.js +147 -0
  101. package/dist/chat/prompt-adapter.js +2 -0
  102. package/dist/chat/providers/irc/index.d.ts +2 -2
  103. package/dist/chat/providers/telegram/index.d.ts +2 -0
  104. package/dist/chat/providers/telegram/index.js +21 -2
  105. package/dist/chat/system-prompts.d.ts +3 -3
  106. package/dist/chat/system-prompts.js +116 -61
  107. package/dist/chat/tools.d.ts +6 -6
  108. package/dist/cli/commands/auth.js +64 -1
  109. package/dist/cli/commands/chat.js +1327 -138
  110. package/dist/cli/commands/doctor.js +222 -16
  111. package/dist/cli/commands/history.d.ts +3 -0
  112. package/dist/cli/commands/history.js +138 -0
  113. package/dist/cli/commands/import.d.ts +44 -0
  114. package/dist/cli/commands/import.js +526 -0
  115. package/dist/cli/commands/init.d.ts +13 -0
  116. package/dist/cli/commands/init.js +381 -0
  117. package/dist/cli/commands/onboard.js +210 -30
  118. package/dist/cli/commands/plan.d.ts +13 -0
  119. package/dist/cli/commands/plan.js +184 -0
  120. package/dist/cli/commands/serve.d.ts +7 -0
  121. package/dist/cli/commands/serve.js +77 -18
  122. package/dist/cli/commands/setup.js +84 -3
  123. package/dist/cli/commands/status.js +54 -0
  124. package/dist/cli/commands/tools.js +13 -13
  125. package/dist/cli/commands/tui.js +26 -1
  126. package/dist/cli/index.js +19 -0
  127. package/dist/cli/ink/App.d.ts +13 -0
  128. package/dist/cli/ink/App.js +6 -0
  129. package/dist/cli/ink/ChatApp.d.ts +60 -0
  130. package/dist/cli/ink/ChatApp.js +325 -0
  131. package/dist/cli/ink/DashboardApp.d.ts +3 -0
  132. package/dist/cli/ink/DashboardApp.js +143 -0
  133. package/dist/cli/ink/components/AgentStatus.d.ts +17 -0
  134. package/dist/cli/ink/components/AgentStatus.js +33 -0
  135. package/dist/cli/ink/components/ChannelList.d.ts +17 -0
  136. package/dist/cli/ink/components/ChannelList.js +33 -0
  137. package/dist/cli/ink/components/ConnectionStatus.d.ts +19 -0
  138. package/dist/cli/ink/components/ConnectionStatus.js +50 -0
  139. package/dist/cli/ink/components/CostBar.d.ts +19 -0
  140. package/dist/cli/ink/components/CostBar.js +35 -0
  141. package/dist/cli/ink/components/HookStatus.d.ts +15 -0
  142. package/dist/cli/ink/components/HookStatus.js +7 -0
  143. package/dist/cli/ink/components/PermissionPrompt.d.ts +21 -0
  144. package/dist/cli/ink/components/PermissionPrompt.js +51 -0
  145. package/dist/cli/ink/components/PlanView.d.ts +23 -0
  146. package/dist/cli/ink/components/PlanView.js +26 -0
  147. package/dist/cli/ink/components/ProviderList.d.ts +16 -0
  148. package/dist/cli/ink/components/ProviderList.js +16 -0
  149. package/dist/cli/ink/components/SessionHeader.d.ts +15 -0
  150. package/dist/cli/ink/components/SessionHeader.js +15 -0
  151. package/dist/cli/ink/components/StreamingMessage.d.ts +20 -0
  152. package/dist/cli/ink/components/StreamingMessage.js +27 -0
  153. package/dist/cli/ink/components/SuggestionBar.d.ts +15 -0
  154. package/dist/cli/ink/components/SuggestionBar.js +28 -0
  155. package/dist/cli/ink/components/SystemHealth.d.ts +20 -0
  156. package/dist/cli/ink/components/SystemHealth.js +39 -0
  157. package/dist/cli/ink/components/TaskList.d.ts +17 -0
  158. package/dist/cli/ink/components/TaskList.js +59 -0
  159. package/dist/cli/ink/components/ToolCall.d.ts +17 -0
  160. package/dist/cli/ink/components/ToolCall.js +43 -0
  161. package/dist/cli/ink/index.d.ts +23 -0
  162. package/dist/cli/ink/index.js +15 -0
  163. package/dist/cli/ink/keybindings.d.ts +35 -0
  164. package/dist/cli/ink/keybindings.js +105 -0
  165. package/dist/cli/ink/markdown-renderer.d.ts +47 -0
  166. package/dist/cli/ink/markdown-renderer.js +380 -0
  167. package/dist/cli/ink/output-styles.d.ts +39 -0
  168. package/dist/cli/ink/output-styles.js +119 -0
  169. package/dist/cli/interactive/auto-serve.d.ts +38 -0
  170. package/dist/cli/interactive/auto-serve.js +209 -0
  171. package/dist/cli/interactive/index.d.ts +25 -0
  172. package/dist/cli/interactive/index.js +23 -0
  173. package/dist/cli/interactive/picker.d.ts +28 -0
  174. package/dist/cli/interactive/picker.js +153 -0
  175. package/dist/cli/interactive/renderer.d.ts +26 -0
  176. package/dist/cli/interactive/renderer.js +555 -0
  177. package/dist/cli/interactive/repl.d.ts +14 -0
  178. package/dist/cli/interactive/repl.js +1516 -0
  179. package/dist/cli/interactive/stream-client.d.ts +59 -0
  180. package/dist/cli/interactive/stream-client.js +506 -0
  181. package/dist/cli/interactive/types.d.ts +121 -0
  182. package/dist/cli/interactive/types.js +10 -0
  183. package/dist/cli/utils/api.d.ts +9 -0
  184. package/dist/cli/utils/api.js +58 -2
  185. package/dist/cron/natural-language.d.ts +54 -0
  186. package/dist/cron/natural-language.js +569 -0
  187. package/dist/cron/scheduler.d.ts +34 -4
  188. package/dist/cron/scheduler.js +141 -9
  189. package/dist/cron/templates.d.ts +1 -1
  190. package/dist/cron/templates.js +219 -0
  191. package/dist/feeds/feed-engine.d.ts +149 -0
  192. package/dist/feeds/feed-engine.js +600 -0
  193. package/dist/feeds/index.d.ts +9 -0
  194. package/dist/feeds/index.js +8 -0
  195. package/dist/gateway/router.js +6 -1
  196. package/dist/hooks/built-in/audit-log.d.ts +11 -0
  197. package/dist/hooks/built-in/audit-log.js +84 -0
  198. package/dist/hooks/built-in/cost-warning.d.ts +10 -0
  199. package/dist/hooks/built-in/cost-warning.js +59 -0
  200. package/dist/hooks/built-in/dangerous-tool.d.ts +32 -0
  201. package/dist/hooks/built-in/dangerous-tool.js +80 -0
  202. package/dist/hooks/built-in/index.d.ts +14 -0
  203. package/dist/hooks/built-in/index.js +20 -0
  204. package/dist/hooks/index.d.ts +3 -0
  205. package/dist/hooks/index.js +6 -0
  206. package/dist/hooks/loader.d.ts +17 -0
  207. package/dist/hooks/loader.js +142 -0
  208. package/dist/hooks/registry.d.ts +62 -0
  209. package/dist/hooks/registry.js +127 -0
  210. package/dist/integrations/web-search.js +6 -3
  211. package/dist/memory/index.d.ts +1 -0
  212. package/dist/memory/index.js +2 -0
  213. package/dist/memory/memory-service.js +14 -13
  214. package/dist/memory/observational.d.ts +62 -0
  215. package/dist/memory/observational.js +396 -0
  216. package/dist/middleware/content-filter.d.ts +22 -0
  217. package/dist/middleware/content-filter.js +69 -0
  218. package/dist/middleware/request-validator.d.ts +159 -0
  219. package/dist/middleware/request-validator.js +93 -0
  220. package/dist/plugins/sdk.d.ts +22 -0
  221. package/dist/plugins/sdk.js +17 -1
  222. package/dist/projects/types.d.ts +10 -10
  223. package/dist/providers/ai-sdk.d.ts +11 -0
  224. package/dist/providers/ai-sdk.js +78 -19
  225. package/dist/providers/core/models.js +67 -1
  226. package/dist/providers/core/types.d.ts +3 -0
  227. package/dist/providers/index.d.ts +1 -0
  228. package/dist/providers/index.js +1 -0
  229. package/dist/providers/smart-router.d.ts +118 -0
  230. package/dist/providers/smart-router.js +685 -0
  231. package/dist/providers/types.d.ts +16 -16
  232. package/dist/routes/agents.js +50 -0
  233. package/dist/routes/auth.js +8 -1
  234. package/dist/routes/chat.js +259 -14
  235. package/dist/routes/costs.js +32 -0
  236. package/dist/routes/cron.js +80 -5
  237. package/dist/routes/feeds.d.ts +10 -0
  238. package/dist/routes/feeds.js +168 -0
  239. package/dist/routes/mcp.js +21 -0
  240. package/dist/routes/memory.js +130 -0
  241. package/dist/routes/skills.js +129 -0
  242. package/dist/routes/teams.d.ts +9 -0
  243. package/dist/routes/teams.js +210 -0
  244. package/dist/routes/tools.js +14 -0
  245. package/dist/routes/users.js +2 -0
  246. package/dist/server/route-loader.js +3 -1
  247. package/dist/server/stream-bridge.d.ts +19 -0
  248. package/dist/server/stream-bridge.js +38 -0
  249. package/dist/server.js +310 -36
  250. package/dist/skills/marketplace.d.ts +87 -0
  251. package/dist/skills/marketplace.js +268 -0
  252. package/dist/storage/index.d.ts +14 -0
  253. package/dist/storage/index.js +64 -3
  254. package/dist/storage/schema.d.ts +4 -4
  255. package/dist/storage/schema.js +1 -1
  256. package/dist/teams/index.d.ts +110 -0
  257. package/dist/teams/index.js +365 -0
  258. package/dist/tickets/types.d.ts +39 -39
  259. package/dist/types/summary.d.ts +5 -5
  260. package/dist/utils/auto-update.d.ts +23 -0
  261. package/dist/utils/auto-update.js +157 -0
  262. package/dist/utils/offline-detect.d.ts +55 -0
  263. package/dist/utils/offline-detect.js +121 -0
  264. package/dist/utils/pid-file.d.ts +36 -0
  265. package/dist/utils/pid-file.js +125 -0
  266. package/dist/utils/prevent-sleep.d.ts +27 -0
  267. package/dist/utils/prevent-sleep.js +95 -0
  268. package/dist/utils/sanitizer.d.ts +57 -0
  269. package/dist/utils/sanitizer.js +174 -0
  270. package/docker-compose.yml +13 -137
  271. package/package.json +31 -6
  272. package/skills/deploy-cloudflare/SKILL.md +62 -0
  273. package/skills/deploy-vercel/SKILL.md +67 -0
  274. package/skills/docker-deploy/SKILL.md +158 -0
  275. package/skills/full-stack-builder/SKILL.md +92 -0
  276. package/skills/humanizer/SKILL.md +488 -0
  277. package/skills/profclaw-assistant/SKILL.md +85 -56
  278. package/ui/dist/assets/ActivityView-Da6HQ8ws.js +1 -0
  279. package/ui/dist/assets/AgentList-H58R0kX3.js +1 -0
  280. package/ui/dist/assets/AnalyticsDashboard-B28AeRsc.js +1 -0
  281. package/ui/dist/assets/AreaChart-DwLzeAQ8.js +1 -0
  282. package/ui/dist/assets/CartesianChart-piKQsTAR.js +36 -0
  283. package/ui/dist/assets/ChatView-Dbj8rxU5.js +10 -0
  284. package/ui/dist/assets/CostsDashboard-C6F5df0F.js +1 -0
  285. package/ui/dist/assets/EstimateSelect-wga0rUKX.js +1 -0
  286. package/ui/dist/assets/FeedDashboard-Ef6FEzEf.js +1 -0
  287. package/ui/dist/assets/FloatingChatbot-0yhkUeCq.js +2 -0
  288. package/ui/dist/assets/InviteCodeManagement-tUXfx4Bc.js +2 -0
  289. package/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  290. package/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  291. package/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  292. package/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  293. package/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  294. package/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  295. package/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  296. package/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  297. package/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  298. package/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  299. package/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  300. package/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  301. package/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  302. package/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  303. package/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  304. package/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  305. package/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  306. package/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  307. package/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  308. package/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  309. package/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  310. package/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  311. package/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  312. package/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  313. package/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  314. package/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  315. package/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  316. package/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  317. package/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  318. package/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  319. package/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  320. package/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  321. package/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  322. package/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  323. package/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  324. package/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  325. package/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  326. package/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  327. package/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  328. package/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  329. package/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  330. package/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  331. package/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  332. package/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  333. package/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  334. package/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  335. package/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  336. package/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  337. package/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  338. package/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  339. package/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  340. package/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  341. package/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  342. package/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  343. package/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  344. package/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  345. package/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  346. package/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  347. package/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  348. package/ui/dist/assets/MarketplacePage-DGrLtolR.js +1 -0
  349. package/ui/dist/assets/{ProjectDetail--gPfvATB.js → ProjectDetail-BgWvA6UP.js} +2 -2
  350. package/ui/dist/assets/ProjectIcon-TDuliqwg.js +1 -0
  351. package/ui/dist/assets/ProjectList-DXgM4187.js +1 -0
  352. package/ui/dist/assets/{Settings-1kaf7VHE.js → Settings-xA8Zhi3p.js} +3 -3
  353. package/ui/dist/assets/StatusIndicator-B8vfFAxg.js +1 -0
  354. package/ui/dist/assets/{SummaryDetail-DV3AmONk.js → SummaryDetail-eedRfTtF.js} +2 -2
  355. package/ui/dist/assets/SummaryList-CLNX2P84.js +1 -0
  356. package/ui/dist/assets/TaskDetail-BtWclNiR.js +1 -0
  357. package/ui/dist/assets/TaskList-JjODAgSc.js +1 -0
  358. package/ui/dist/assets/TeamsPage-CBK5iAyx.js +1 -0
  359. package/ui/dist/assets/TicketBoard-C9aJNNYp.js +1 -0
  360. package/ui/dist/assets/TicketDetail-CV5JmL31.js +1 -0
  361. package/ui/dist/assets/TicketList-68V4lzUz.js +1 -0
  362. package/ui/dist/assets/UserManagement-DKTb6UpE.js +2 -0
  363. package/ui/dist/assets/ViewSwitcher-BvdQht2Y.js +1 -0
  364. package/ui/dist/assets/alert-dialog-O6qBOv_f.js +1 -0
  365. package/ui/dist/assets/{archive-BsLa7brF.js → archive-CX7UvZpf.js} +1 -1
  366. package/ui/dist/assets/badge-BbfnwDJP.js +1 -0
  367. package/ui/dist/assets/{bug-CmZI7jqM.js → bug-3WX0394-.js} +1 -1
  368. package/ui/dist/assets/{calendar-Bf0t2B82.js → calendar-buq77TaS.js} +1 -1
  369. package/ui/dist/assets/{chevron-up-CoUsOvZj.js → chevron-up-4ymsBJRo.js} +1 -1
  370. package/ui/dist/assets/{circle-x-Dgw11LgT.js → circle-x-ByXtwR6Y.js} +1 -1
  371. package/ui/dist/assets/constants-CVdDh7p0.js +1 -0
  372. package/ui/dist/assets/{cpu-DiPMQICV.js → cpu-CiuoCeDY.js} +1 -1
  373. package/ui/dist/assets/{database-BwTfupDi.js → database-BWXSBg5a.js} +1 -1
  374. package/ui/dist/assets/dialog-lDg4NvMV.js +1 -0
  375. package/ui/dist/assets/dollar-sign-Dmijd6DP.js +1 -0
  376. package/ui/dist/assets/{ellipsis-UJx4Xnf6.js → ellipsis-vT-5SVIp.js} +1 -1
  377. package/ui/dist/assets/{ellipsis-vertical-DoULYEKn.js → ellipsis-vertical-B4tAX_bL.js} +1 -1
  378. package/ui/dist/assets/feature-markdown-DBo5Q7uq.js +289 -0
  379. package/ui/dist/assets/{file-code-B2UrJLwi.js → file-code-ChOWdKXf.js} +1 -1
  380. package/ui/dist/assets/{folder-open-BJi577JH.js → folder-open-CkOUbIiP.js} +1 -1
  381. package/ui/dist/assets/{funnel-PkaD_HSR.js → funnel-uZkoSPP-.js} +1 -1
  382. package/ui/dist/assets/{git-branch-C_K30am0.js → git-branch-CwvUxj7C.js} +1 -1
  383. package/ui/dist/assets/{git-commit-horizontal-IT6ecxWQ.js → git-commit-horizontal-lxPzCvhD.js} +1 -1
  384. package/ui/dist/assets/{globe-f-jjCa_g.js → globe-D_WpJdsj.js} +1 -1
  385. package/ui/dist/assets/{hash-DNEv8Elo.js → hash-CA0hJ7vh.js} +1 -1
  386. package/ui/dist/assets/{history-BQSBo_1Q.js → history-CEWMLELs.js} +1 -1
  387. package/ui/dist/assets/inbox-ehtOptTV.js +1 -0
  388. package/ui/dist/assets/{index-nS3EErDg.js → index-C1rhbml4.js} +3 -3
  389. package/ui/dist/assets/index-CR8gfCHv.js +1 -0
  390. package/ui/dist/assets/index-DVXMpWai.js +1 -0
  391. package/ui/dist/assets/index-DaDJMM2v.js +1 -0
  392. package/ui/dist/assets/index-Djdq_0xu.js +1 -0
  393. package/ui/dist/assets/index-M46Gcb8K.css +1 -0
  394. package/ui/dist/assets/index-sEBN7w8J.js +109 -0
  395. package/ui/dist/assets/layers-C6Mhf8Nk.js +1 -0
  396. package/ui/dist/assets/{layout-dashboard-CcKt1hXw.js → layout-dashboard-BRUM0kpT.js} +1 -1
  397. package/ui/dist/assets/{lightbulb-Jhp7FvbU.js → lightbulb-CN2q1O5l.js} +1 -1
  398. package/ui/dist/assets/{link-2-DdO7ostF.js → link-2-CC4E28yJ.js} +1 -1
  399. package/ui/dist/assets/markdown-renderer-C6ENbB0u.js +10 -0
  400. package/ui/dist/assets/markdown-renderer-CfVKi3_s.css +1 -0
  401. package/ui/dist/assets/{message-square-CBgH65X7.js → message-square-Bbb241am.js} +1 -1
  402. package/ui/dist/assets/package-CwT3IzFx.js +1 -0
  403. package/ui/dist/assets/{pencil-CYXCEPQh.js → pencil-CDmiHzoj.js} +1 -1
  404. package/ui/dist/assets/{play-d1ONaH0-.js → play-tjy5jMQX.js} +1 -1
  405. package/ui/dist/assets/popover-CFHZf3pE.js +1 -0
  406. package/ui/dist/assets/progress-BboUHuLf.js +1 -0
  407. package/ui/dist/assets/{rocket-DhvUetsk.js → rocket--8ZHgdXJ.js} +1 -1
  408. package/ui/dist/assets/select-n108rXny.js +1 -0
  409. package/ui/dist/assets/{send-P0r5D7td.js → send-Cu7MR2Qh.js} +1 -1
  410. package/ui/dist/assets/server-DmodxZp-.js +1 -0
  411. package/ui/dist/assets/{settings-2-ocj5uHGx.js → settings-2-CkRNqkxj.js} +1 -1
  412. package/ui/dist/assets/sheet-7aUJkyzU.js +1 -0
  413. package/ui/dist/assets/{shield-check-DfVdE34l.js → shield-check-ViI3nMKE.js} +1 -1
  414. package/ui/dist/assets/{shield-off-8l0J6R-8.js → shield-off-DN3tagkn.js} +1 -1
  415. package/ui/dist/assets/skeleton-C0E1Nfro.js +1 -0
  416. package/ui/dist/assets/{smartphone-77_g3SQn.js → smartphone-5FghiXpA.js} +1 -1
  417. package/ui/dist/assets/{square-Ckzsr3Rn.js → square-DiNSx8K7.js} +1 -1
  418. package/ui/dist/assets/star-DhdFVidS.js +1 -0
  419. package/ui/dist/assets/switch-CIgVbZqQ.js +1 -0
  420. package/ui/dist/assets/table-BC56FVMV.js +1 -0
  421. package/ui/dist/assets/{tag-C35mgoEv.js → tag-CH_vQliw.js} +1 -1
  422. package/ui/dist/assets/textarea-CxDiKFXZ.js +1 -0
  423. package/ui/dist/assets/{timer-DHJBS2w1.js → timer-CdGVTb60.js} +1 -1
  424. package/ui/dist/assets/tooltip-BuW9A5UO.js +1 -0
  425. package/ui/dist/assets/{trash-2-keO5tKPE.js → trash-2-CucZJL0N.js} +1 -1
  426. package/ui/dist/assets/trending-down-BCezC1z2.js +1 -0
  427. package/ui/dist/assets/{trending-up-BBv8KbrY.js → trending-up-C5OYX2k2.js} +1 -1
  428. package/ui/dist/assets/vendor-query-C0p7b53x.js +4 -0
  429. package/ui/dist/assets/vendor-react-Mc5_kfYG.js +12 -0
  430. package/ui/dist/assets/vendor-ui-CXPAUBFv.js +51 -0
  431. package/ui/dist/assets/{wand-sparkles-BusQ52ZK.js → wand-sparkles-BaH6pJnc.js} +1 -1
  432. package/ui/dist/assets/{wrench-02YaOSiK.js → wrench-CjxjP6ok.js} +1 -1
  433. package/ui/dist/index.html +5 -2
  434. package/ui/dist/assets/ActivityView-BeshZXmE.js +0 -1
  435. package/ui/dist/assets/AgentList-QVHeldPi.js +0 -1
  436. package/ui/dist/assets/AnalyticsDashboard-C9R97dXE.js +0 -1
  437. package/ui/dist/assets/AreaChart-B73gaiVQ.js +0 -1
  438. package/ui/dist/assets/CartesianChart-CXYL2R-A.js +0 -36
  439. package/ui/dist/assets/ChatView-qjZErlrp.js +0 -10
  440. package/ui/dist/assets/CostsDashboard-O4DCLy1i.js +0 -1
  441. package/ui/dist/assets/EstimateSelect-1hQf4Osk.js +0 -1
  442. package/ui/dist/assets/FloatingChatbot-BZBzrJxd.js +0 -2
  443. package/ui/dist/assets/InviteCodeManagement-LEoGqYeA.js +0 -2
  444. package/ui/dist/assets/ProjectIcon-Chd_ofsw.js +0 -1
  445. package/ui/dist/assets/ProjectList-BvUfVB6c.js +0 -4
  446. package/ui/dist/assets/StatusIndicator-pRfkNcdV.js +0 -1
  447. package/ui/dist/assets/SummaryList-B6kXiTxL.js +0 -1
  448. package/ui/dist/assets/TaskDetail-CV6CzbOS.js +0 -1
  449. package/ui/dist/assets/TaskList-DL7brQKd.js +0 -1
  450. package/ui/dist/assets/TicketBoard-CrkkFj7C.js +0 -1
  451. package/ui/dist/assets/TicketDetail-hnnYclpD.js +0 -1
  452. package/ui/dist/assets/TicketList-CsDAyQCL.js +0 -1
  453. package/ui/dist/assets/UserManagement-BGFQyTSr.js +0 -2
  454. package/ui/dist/assets/ViewSwitcher-CnL0OqA8.js +0 -1
  455. package/ui/dist/assets/alert-dialog-SKM6Gjz4.js +0 -7
  456. package/ui/dist/assets/badge-CVHH1a-v.js +0 -1
  457. package/ui/dist/assets/brain-6o_RHVpe.js +0 -1
  458. package/ui/dist/assets/constants-Q5Zivl-a.js +0 -1
  459. package/ui/dist/assets/dialog-BUqN9Psj.js +0 -1
  460. package/ui/dist/assets/index-BUmnwPQr.js +0 -1
  461. package/ui/dist/assets/index-BfF5G4Cw.js +0 -1
  462. package/ui/dist/assets/index-C8N375c2.js +0 -1
  463. package/ui/dist/assets/index-CuxnqVVz.css +0 -1
  464. package/ui/dist/assets/index-CzMM3QOk.js +0 -1
  465. package/ui/dist/assets/index-lJlNPFVJ.js +0 -67
  466. package/ui/dist/assets/layers-jFqVrWMD.js +0 -1
  467. package/ui/dist/assets/markdown-renderer-n1ToC4XQ.js +0 -36
  468. package/ui/dist/assets/popover-CrmZgoWu.js +0 -1
  469. package/ui/dist/assets/progress-uv4SPkL5.js +0 -1
  470. package/ui/dist/assets/select-CeQ8AHGy.js +0 -1
  471. package/ui/dist/assets/server-CVHTZB72.js +0 -1
  472. package/ui/dist/assets/sheet-DuQExSho.js +0 -1
  473. package/ui/dist/assets/skeleton-TFiFJ3nB.js +0 -1
  474. package/ui/dist/assets/switch-CAePkd6k.js +0 -1
  475. package/ui/dist/assets/table-ZjAZ7hkt.js +0 -1
  476. package/ui/dist/assets/textarea-DsVPnSgR.js +0 -1
  477. package/ui/dist/assets/tooltip-BYmQ331W.js +0 -1
@@ -0,0 +1,685 @@
1
+ /**
2
+ * Smart Model Router
3
+ *
4
+ * Classifies query complexity and routes to the cheapest model
5
+ * that can handle it. This is the engine behind profClaw's "10x cheaper" claim.
6
+ *
7
+ * Complexity tiers:
8
+ * trivial -> Haiku/Groq/Gemini Flash ($0.001-0.005/conv)
9
+ * standard -> Sonnet/GPT-4o/Gemini Pro ($0.02-0.05/conv)
10
+ * complex -> Opus/GPT-4.5/Gemini Ultra ($0.05-0.15/conv)
11
+ *
12
+ * The router saves users 60-90% vs always using a top-tier model.
13
+ */
14
+ import { logger } from '../utils/logger.js';
15
+ import { MODEL_CATALOG } from './core/models.js';
16
+ // Default config - overridable via env or runtime config
17
+ // defaultModel is empty - resolved dynamically from available providers
18
+ const DEFAULT_CONFIG = {
19
+ enabled: true,
20
+ defaultModel: '', // resolved dynamically via resolveDefaultModel()
21
+ preferredProviders: [],
22
+ maxCostPerRequest: 0.50,
23
+ enableLocalFallback: true,
24
+ complexityThresholds: {
25
+ trivialMax: 0.25,
26
+ standardMax: 0.60,
27
+ },
28
+ };
29
+ /**
30
+ * Resolve the default model from whatever providers are available.
31
+ * Picks the best mid-range model from configured providers.
32
+ * Truly provider-agnostic - works with any combination.
33
+ */
34
+ export function resolveDefaultModel(availableProviders) {
35
+ // If user explicitly set a default, use it
36
+ if (config.defaultModel)
37
+ return config.defaultModel;
38
+ // Find the best "standard tier" model from available providers, sorted by quality-per-dollar
39
+ const candidates = MODEL_CATALOG
40
+ .filter(m => availableProviders.has(m.provider))
41
+ .filter(m => m.supportsTools && m.supportsStreaming)
42
+ .filter(m => m.contextWindow >= 32000) // need decent context
43
+ .sort((a, b) => {
44
+ // Quality heuristic: context window * tool support, balanced against cost
45
+ const aScore = a.contextWindow / 1000 / Math.max(a.costPer1MInput + a.costPer1MOutput, 0.01);
46
+ const bScore = b.contextWindow / 1000 / Math.max(b.costPer1MInput + b.costPer1MOutput, 0.01);
47
+ return bScore - aScore; // higher score = better value
48
+ });
49
+ return candidates[0]?.id ?? MODEL_CATALOG[0]?.id ?? 'llama3.2';
50
+ }
51
+ let config = { ...DEFAULT_CONFIG };
52
+ /**
53
+ * Signals that push toward HIGHER complexity (need stronger model).
54
+ * Each returns a detail string if matched, false otherwise.
55
+ */
56
+ const COMPLEXITY_UP_SIGNALS = [
57
+ {
58
+ name: 'multi_step_reasoning',
59
+ weight: 0.20,
60
+ test: (prompt) => {
61
+ const patterns = [
62
+ /\b(analyze|compare|evaluate|assess|critique|design|architect|plan)\b/i,
63
+ /\b(step by step|think through|break down|walk me through)\b/i,
64
+ /\b(trade.?offs?|pros? and cons|advantages? and disadvantages?)\b/i,
65
+ /\b(why.*and.*how|how.*and.*why)\b/i,
66
+ ];
67
+ const matched = patterns.filter(p => p.test(prompt));
68
+ return matched.length >= 1
69
+ ? `reasoning keywords (${matched.length} patterns)`
70
+ : false;
71
+ },
72
+ },
73
+ {
74
+ name: 'code_generation',
75
+ weight: 0.15,
76
+ test: (prompt) => {
77
+ const patterns = [
78
+ /\b(implement|refactor|write|create|build|develop)\b.*\b(function|class|component|module|service|api|endpoint|handler)\b/i,
79
+ /\b(fix|debug|resolve)\b.*\b(bug|error|issue|crash|failure)\b/i,
80
+ /```[\s\S]{50,}/,
81
+ ];
82
+ const matched = patterns.filter(p => p.test(prompt));
83
+ return matched.length >= 1 ? 'code generation/debugging request' : false;
84
+ },
85
+ },
86
+ {
87
+ name: 'long_context',
88
+ weight: 0.12,
89
+ test: (prompt, ctx) => {
90
+ if (prompt.length > 2000)
91
+ return `long prompt (${prompt.length} chars)`;
92
+ if (ctx.conversationLength > 10)
93
+ return `long conversation (${ctx.conversationLength} turns)`;
94
+ return false;
95
+ },
96
+ },
97
+ {
98
+ name: 'tool_orchestration',
99
+ weight: 0.15,
100
+ test: (_prompt, ctx) => {
101
+ return ctx.hasToolUse ? 'tool use enabled' : false;
102
+ },
103
+ },
104
+ {
105
+ name: 'image_analysis',
106
+ weight: 0.10,
107
+ test: (_prompt, ctx) => {
108
+ return ctx.hasImages ? 'image analysis required' : false;
109
+ },
110
+ },
111
+ {
112
+ name: 'creative_complex',
113
+ weight: 0.10,
114
+ test: (prompt) => {
115
+ const patterns = [
116
+ /\b(write|draft|compose)\b.*\b(essay|article|report|documentation|spec|rfc)\b/i,
117
+ /\b(translate|convert|migrate|port)\b.*\b(from|to|into)\b/i,
118
+ ];
119
+ return patterns.some(p => p.test(prompt)) ? 'complex creative task' : false;
120
+ },
121
+ },
122
+ {
123
+ name: 'multi_file_scope',
124
+ weight: 0.10,
125
+ test: (prompt) => {
126
+ const fileRefs = prompt.match(/\b[\w-]+\.(ts|js|py|go|rs|java|tsx|jsx|vue|svelte|css|html)\b/g);
127
+ return fileRefs && fileRefs.length > 2
128
+ ? `multi-file scope (${fileRefs.length} files referenced)`
129
+ : false;
130
+ },
131
+ },
132
+ {
133
+ name: 'system_design',
134
+ weight: 0.18,
135
+ test: (prompt) => {
136
+ const patterns = [
137
+ /\b(system design|architecture|infrastructure|scaling|distributed)\b/i,
138
+ /\b(database schema|api design|microservice|event.?driven)\b/i,
139
+ /\b(security|auth|encryption|compliance|gdpr|hipaa)\b/i,
140
+ ];
141
+ const matched = patterns.filter(p => p.test(prompt));
142
+ return matched.length >= 1 ? 'system design/architecture' : false;
143
+ },
144
+ },
145
+ ];
146
+ /**
147
+ * Signals that push toward LOWER complexity (cheap model is fine).
148
+ */
149
+ const COMPLEXITY_DOWN_SIGNALS = [
150
+ {
151
+ name: 'simple_question',
152
+ weight: -0.20,
153
+ test: (prompt) => {
154
+ if (prompt.length < 100 && /^(what|who|when|where|how much|how many|is |are |does |do |can |will )/i.test(prompt)) {
155
+ return 'short factual question';
156
+ }
157
+ return false;
158
+ },
159
+ },
160
+ {
161
+ name: 'greeting_chitchat',
162
+ weight: -0.25,
163
+ test: (prompt) => {
164
+ const patterns = [
165
+ /^(hi|hello|hey|yo|sup|thanks|thank you|ok|okay|got it|cool|nice|great|good morning|good night)\b/i,
166
+ /^(how are you|what's up|what can you do)\b/i,
167
+ ];
168
+ return patterns.some(p => p.test(prompt.trim())) ? 'greeting/chitchat' : false;
169
+ },
170
+ },
171
+ {
172
+ name: 'format_conversion',
173
+ weight: -0.15,
174
+ test: (prompt) => {
175
+ const patterns = [
176
+ /\b(convert|format|prettify|minify|sort|list|enumerate)\b/i,
177
+ /\b(json|yaml|csv|xml|markdown|html)\b.*\b(to|from|into)\b/i,
178
+ ];
179
+ return patterns.some(p => p.test(prompt)) && prompt.length < 300
180
+ ? 'simple format/conversion'
181
+ : false;
182
+ },
183
+ },
184
+ {
185
+ name: 'lookup_retrieval',
186
+ weight: -0.18,
187
+ test: (prompt) => {
188
+ const patterns = [
189
+ /\b(show|get|find|list|display|fetch|check)\b.*\b(status|config|settings|version|info)\b/i,
190
+ /\b(what is|define|explain briefly|summarize in one)\b/i,
191
+ ];
192
+ return patterns.some(p => p.test(prompt)) && prompt.length < 200
193
+ ? 'simple lookup/retrieval'
194
+ : false;
195
+ },
196
+ },
197
+ {
198
+ name: 'typo_fix',
199
+ weight: -0.22,
200
+ test: (prompt) => {
201
+ const patterns = [
202
+ /\b(fix typo|rename|change name|update string|change text|replace)\b/i,
203
+ /\b(add comment|add import|remove import|add export)\b/i,
204
+ ];
205
+ return patterns.some(p => p.test(prompt)) && prompt.length < 200
206
+ ? 'trivial edit'
207
+ : false;
208
+ },
209
+ },
210
+ {
211
+ name: 'short_prompt',
212
+ weight: -0.10,
213
+ test: (prompt) => {
214
+ return prompt.length < 50 ? `very short prompt (${prompt.length} chars)` : false;
215
+ },
216
+ },
217
+ ];
218
+ // Core Functions
219
+ /**
220
+ * Classify the complexity of a user prompt.
221
+ */
222
+ export function classifyComplexity(prompt, context = {}) {
223
+ const ctx = {
224
+ conversationLength: context.conversationLength ?? 0,
225
+ hasToolUse: context.hasToolUse ?? false,
226
+ hasCodeContext: context.hasCodeContext ?? false,
227
+ hasImages: context.hasImages ?? false,
228
+ previousModel: context.previousModel,
229
+ sessionCostSoFar: context.sessionCostSoFar ?? 0,
230
+ };
231
+ const signals = [];
232
+ let score = 0.35; // baseline: lean slightly toward standard
233
+ // Evaluate UP signals
234
+ for (const signal of COMPLEXITY_UP_SIGNALS) {
235
+ const result = signal.test(prompt, ctx);
236
+ const matched = result !== false;
237
+ signals.push({
238
+ name: signal.name,
239
+ weight: signal.weight,
240
+ matched,
241
+ detail: matched ? result : undefined,
242
+ });
243
+ if (matched)
244
+ score += signal.weight;
245
+ }
246
+ // Evaluate DOWN signals
247
+ for (const signal of COMPLEXITY_DOWN_SIGNALS) {
248
+ const result = signal.test(prompt, ctx);
249
+ const matched = result !== false;
250
+ signals.push({
251
+ name: signal.name,
252
+ weight: signal.weight,
253
+ matched,
254
+ detail: matched ? result : undefined,
255
+ });
256
+ if (matched)
257
+ score += signal.weight; // weight is already negative
258
+ }
259
+ // Clamp to [0, 1]
260
+ score = Math.max(0, Math.min(1, score));
261
+ // Classify tier
262
+ const { trivialMax, standardMax } = config.complexityThresholds;
263
+ let tier;
264
+ if (score <= trivialMax) {
265
+ tier = 'trivial';
266
+ }
267
+ else if (score <= standardMax) {
268
+ tier = 'standard';
269
+ }
270
+ else {
271
+ tier = 'complex';
272
+ }
273
+ // Confidence: how many signals fired and how strongly
274
+ const matchedSignals = signals.filter(s => s.matched);
275
+ const confidence = Math.min(1, 0.4 + matchedSignals.length * 0.12);
276
+ const reasoning = matchedSignals.length > 0
277
+ ? matchedSignals.map(s => s.detail).join('; ')
278
+ : 'no strong signals, defaulting to standard';
279
+ return { tier, score, confidence, signals, reasoning };
280
+ }
281
+ /**
282
+ * Cost thresholds per tier (cost per 1M input tokens).
283
+ * Models below the threshold qualify for that tier.
284
+ * This replaces the old hardcoded MODEL_PREFERENCES.
285
+ */
286
+ const TIER_COST_CEILINGS = {
287
+ trivial: 1.00, // $1/1M input - cheap models only
288
+ standard: 5.00, // $5/1M input - mid-range
289
+ complex: Infinity, // no ceiling - best available
290
+ };
291
+ /**
292
+ * Minimum capability requirements per tier.
293
+ */
294
+ const TIER_REQUIREMENTS = {
295
+ trivial: { minContext: 4096, requiresTools: false, requiresVision: false, preferStreaming: true },
296
+ standard: { minContext: 32000, requiresTools: true, requiresVision: false, preferStreaming: true },
297
+ complex: { minContext: 64000, requiresTools: true, requiresVision: false, preferStreaming: true },
298
+ };
299
+ /**
300
+ * Dynamically select the best model from the catalog for a given tier.
301
+ * Truly provider-agnostic - works with ANY combination of configured providers.
302
+ *
303
+ * Selection strategy per tier:
304
+ * trivial -> cheapest model that meets minimum requirements
305
+ * standard -> best value (capability/cost ratio)
306
+ * complex -> most capable model (largest context, best tools support)
307
+ */
308
+ function getCandidatesForTier(tier, availableProviders, signals) {
309
+ const ceiling = TIER_COST_CEILINGS[tier];
310
+ const reqs = TIER_REQUIREMENTS[tier];
311
+ const needsVision = signals.some(s => s.name === 'image_analysis' && s.matched);
312
+ const needsTools = signals.some(s => s.name === 'tool_orchestration' && s.matched);
313
+ // Filter catalog to available + capable models
314
+ const eligible = MODEL_CATALOG.filter(m => {
315
+ if (!availableProviders.has(m.provider))
316
+ return false;
317
+ if (m.costPer1MInput > ceiling)
318
+ return false;
319
+ if (m.contextWindow < reqs.minContext)
320
+ return false;
321
+ if ((reqs.requiresTools || needsTools) && !m.supportsTools)
322
+ return false;
323
+ if (needsVision && !m.supportsVision)
324
+ return false;
325
+ if (reqs.preferStreaming && !m.supportsStreaming)
326
+ return false;
327
+ return true;
328
+ });
329
+ // Sort by tier strategy
330
+ switch (tier) {
331
+ case 'trivial':
332
+ // Cheapest first (total cost = input + output rate)
333
+ return eligible.sort((a, b) => (a.costPer1MInput + a.costPer1MOutput) - (b.costPer1MInput + b.costPer1MOutput));
334
+ case 'standard':
335
+ // Best value: capability (context * tool support) per dollar
336
+ return eligible.sort((a, b) => {
337
+ const costA = Math.max(a.costPer1MInput + a.costPer1MOutput, 0.01);
338
+ const costB = Math.max(b.costPer1MInput + b.costPer1MOutput, 0.01);
339
+ const valueA = (a.contextWindow / 1000) * (a.supportsTools ? 2 : 1) / costA;
340
+ const valueB = (b.contextWindow / 1000) * (b.supportsTools ? 2 : 1) / costB;
341
+ return valueB - valueA;
342
+ });
343
+ case 'complex':
344
+ // Most capable first: largest context, tool support, then cost as tiebreaker
345
+ return eligible.sort((a, b) => {
346
+ // Primary: context window (larger = more capable)
347
+ if (a.contextWindow !== b.contextWindow)
348
+ return b.contextWindow - a.contextWindow;
349
+ // Secondary: vision support
350
+ if (a.supportsVision !== b.supportsVision)
351
+ return a.supportsVision ? -1 : 1;
352
+ // Tertiary: cost (cheaper preferred among equals)
353
+ return (a.costPer1MInput + a.costPer1MOutput) - (b.costPer1MInput + b.costPer1MOutput);
354
+ });
355
+ }
356
+ }
357
+ /**
358
+ * Select the best model for a given complexity tier.
359
+ * Fully dynamic - queries the MODEL_CATALOG based on available providers.
360
+ * No hardcoded model IDs or provider assumptions.
361
+ */
362
+ export function selectModel(complexity, availableProviders, overrideModel) {
363
+ const effectiveDefault = config.defaultModel || resolveDefaultModel(availableProviders);
364
+ // If user explicitly chose a model, respect it
365
+ if (overrideModel) {
366
+ const modelInfo = findModelInCatalog(overrideModel);
367
+ if (modelInfo) {
368
+ const defaultCost = estimateConversationCost(effectiveDefault);
369
+ const actualCost = estimateConversationCost(overrideModel);
370
+ return {
371
+ complexity,
372
+ selectedModel: modelInfo,
373
+ alternativeModels: [],
374
+ estimatedCost: actualCost,
375
+ savedVsDefault: defaultCost - actualCost,
376
+ savingsPercent: defaultCost > 0 ? Math.round(((defaultCost - actualCost) / defaultCost) * 100) : 0,
377
+ };
378
+ }
379
+ }
380
+ // Dynamic model selection from catalog
381
+ let candidates = getCandidatesForTier(complexity.tier, availableProviders, complexity.signals);
382
+ // If no candidates at this tier, try relaxing to the next tier up
383
+ if (candidates.length === 0 && complexity.tier === 'trivial') {
384
+ candidates = getCandidatesForTier('standard', availableProviders, complexity.signals);
385
+ }
386
+ if (candidates.length === 0 && complexity.tier !== 'complex') {
387
+ candidates = getCandidatesForTier('complex', availableProviders, complexity.signals);
388
+ }
389
+ // Apply user provider preferences
390
+ if (config.preferredProviders.length > 0 && candidates.length > 1) {
391
+ candidates = [...candidates].sort((a, b) => {
392
+ const aPreferred = config.preferredProviders.includes(a.provider) ? 0 : 1;
393
+ const bPreferred = config.preferredProviders.includes(b.provider) ? 0 : 1;
394
+ return aPreferred - bPreferred; // preferred first, preserve existing order within
395
+ });
396
+ }
397
+ // Pick best match, fall back to any available model
398
+ const selected = candidates[0]
399
+ ?? findModelInCatalog(effectiveDefault)
400
+ ?? MODEL_CATALOG.find(m => availableProviders.has(m.provider))
401
+ ?? MODEL_CATALOG[0];
402
+ const alternatives = candidates.slice(1, 4);
403
+ const estimatedCost = estimateConversationCost(selected.id);
404
+ const defaultCost = estimateConversationCost(effectiveDefault);
405
+ // Check if a cheaper provider the user hasn't configured could help
406
+ const missingProviderHint = findMissingProviderHint(complexity.tier, availableProviders, estimatedCost);
407
+ const decision = {
408
+ complexity,
409
+ selectedModel: selected,
410
+ alternativeModels: alternatives,
411
+ estimatedCost,
412
+ savedVsDefault: defaultCost - estimatedCost,
413
+ savingsPercent: defaultCost > 0
414
+ ? Math.round(((defaultCost - estimatedCost) / defaultCost) * 100)
415
+ : 0,
416
+ missingProviderHint,
417
+ };
418
+ logger.info('[SmartRouter] Routed query', {
419
+ tier: complexity.tier,
420
+ score: Math.round(complexity.score * 100),
421
+ confidence: Math.round(complexity.confidence * 100),
422
+ model: selected.id,
423
+ provider: selected.provider,
424
+ estimatedCost: `$${estimatedCost.toFixed(4)}`,
425
+ savings: `${decision.savingsPercent}%`,
426
+ reasoning: complexity.reasoning,
427
+ });
428
+ return decision;
429
+ }
430
+ /**
431
+ * One-shot: classify + select in a single call.
432
+ */
433
+ export function routeQuery(prompt, availableProviders, context = {}, overrideModel) {
434
+ const complexity = classifyComplexity(prompt, context);
435
+ return selectModel(complexity, availableProviders, overrideModel);
436
+ }
437
+ // Provider Recommendations
438
+ /**
439
+ * Cheap providers that users should add for maximum savings.
440
+ * Ordered by value: free/cheapest first.
441
+ */
442
+ const RECOMMENDED_PROVIDERS = [
443
+ {
444
+ provider: 'groq',
445
+ model: 'llama-3.3-70b-versatile',
446
+ tier: 'trivial',
447
+ costPer1MInput: 0,
448
+ reason: 'Free tier handles trivial queries - greetings, lookups, simple Q&A',
449
+ signupUrl: 'https://console.groq.com',
450
+ freeTrialAvailable: true,
451
+ envVar: 'GROQ_API_KEY',
452
+ },
453
+ {
454
+ provider: 'google',
455
+ model: 'gemini-2.0-flash',
456
+ tier: 'trivial',
457
+ costPer1MInput: 0.50,
458
+ reason: 'Ultra-cheap for simple tasks, generous free tier',
459
+ signupUrl: 'https://aistudio.google.com/apikey',
460
+ freeTrialAvailable: true,
461
+ envVar: 'GOOGLE_API_KEY',
462
+ },
463
+ {
464
+ provider: 'cerebras',
465
+ model: 'llama3.1-8b',
466
+ tier: 'trivial',
467
+ costPer1MInput: 0.10,
468
+ reason: 'Fastest inference for trivial queries at $0.10/1M tokens',
469
+ signupUrl: 'https://cloud.cerebras.ai',
470
+ freeTrialAvailable: true,
471
+ envVar: 'CEREBRAS_API_KEY',
472
+ },
473
+ {
474
+ provider: 'deepseek',
475
+ model: 'deepseek-chat',
476
+ tier: 'standard',
477
+ costPer1MInput: 0.27,
478
+ reason: 'Near-Sonnet quality at 1/10th the price for coding tasks',
479
+ signupUrl: 'https://platform.deepseek.com',
480
+ freeTrialAvailable: true,
481
+ envVar: 'DEEPSEEK_API_KEY',
482
+ },
483
+ {
484
+ provider: 'anthropic',
485
+ model: 'claude-haiku-4-5-20251001',
486
+ tier: 'trivial',
487
+ costPer1MInput: 0.80,
488
+ reason: 'Best quality for the price in the Anthropic family',
489
+ signupUrl: 'https://console.anthropic.com',
490
+ freeTrialAvailable: false,
491
+ envVar: 'ANTHROPIC_API_KEY',
492
+ },
493
+ ];
494
+ /**
495
+ * Check if a cheaper provider exists that the user hasn't configured.
496
+ */
497
+ function findMissingProviderHint(tier, availableProviders, currentCost) {
498
+ for (const rec of RECOMMENDED_PROVIDERS) {
499
+ // Skip if user already has this provider
500
+ if (availableProviders.has(rec.provider))
501
+ continue;
502
+ // Only recommend providers relevant to the query's tier
503
+ if (rec.tier !== tier && tier !== 'trivial')
504
+ continue;
505
+ // Only recommend if it would be meaningfully cheaper
506
+ const recCost = estimateConversationCost(rec.model);
507
+ if (recCost >= currentCost * 0.7)
508
+ continue; // need at least 30% savings to recommend
509
+ const savingsPercent = currentCost > 0
510
+ ? Math.round(((currentCost - recCost) / currentCost) * 100)
511
+ : 0;
512
+ return {
513
+ provider: rec.provider,
514
+ model: rec.model,
515
+ reason: rec.reason,
516
+ estimatedSavings: `${savingsPercent}% cheaper ($${recCost.toFixed(4)} vs $${currentCost.toFixed(4)}/conv)`,
517
+ signupUrl: rec.signupUrl,
518
+ freeTrialAvailable: rec.freeTrialAvailable,
519
+ };
520
+ }
521
+ return undefined;
522
+ }
523
+ /**
524
+ * Analyze usage patterns and generate cost optimization recommendations.
525
+ * Call this from the dashboard or CLI doctor command.
526
+ */
527
+ export function getCostOptimizationAdvice(availableProviders) {
528
+ const advice = [];
529
+ const stats = getRoutingStats();
530
+ // If most queries are trivial but no free/cheap provider is configured
531
+ if (!availableProviders.has('groq') && !availableProviders.has('cerebras')) {
532
+ const trivialPercent = stats.totalRouted > 0
533
+ ? Math.round((stats.byTier.trivial / stats.totalRouted) * 100)
534
+ : 40; // estimate 40% trivial if no data yet
535
+ if (trivialPercent > 20) {
536
+ advice.push({
537
+ priority: 'high',
538
+ action: `Add Groq (free tier) - set GROQ_API_KEY`,
539
+ reason: `${trivialPercent}% of your queries are trivial and could run for free on Groq's Llama 70B`,
540
+ estimatedMonthlySavings: stats.totalRouted > 0
541
+ ? `$${(stats.byTier.trivial * 0.015).toFixed(2)}/month`
542
+ : '$5-15/month (estimated)',
543
+ envVar: 'GROQ_API_KEY',
544
+ signupUrl: 'https://console.groq.com',
545
+ });
546
+ }
547
+ }
548
+ // If no Google provider (Gemini Flash is excellent value)
549
+ if (!availableProviders.has('google')) {
550
+ advice.push({
551
+ priority: 'medium',
552
+ action: 'Add Google Gemini - set GOOGLE_API_KEY',
553
+ reason: 'Gemini Flash at $0.50/1M is 6x cheaper than Haiku for trivial tasks, with generous free tier',
554
+ estimatedMonthlySavings: '$3-10/month',
555
+ envVar: 'GOOGLE_API_KEY',
556
+ signupUrl: 'https://aistudio.google.com/apikey',
557
+ });
558
+ }
559
+ // If no DeepSeek (best value for coding)
560
+ if (!availableProviders.has('deepseek') && availableProviders.size <= 2) {
561
+ advice.push({
562
+ priority: 'medium',
563
+ action: 'Add DeepSeek - set DEEPSEEK_API_KEY',
564
+ reason: 'DeepSeek Chat at $0.27/1M handles coding tasks nearly as well as Sonnet at 1/10th the price',
565
+ estimatedMonthlySavings: '$5-20/month',
566
+ envVar: 'DEEPSEEK_API_KEY',
567
+ signupUrl: 'https://platform.deepseek.com',
568
+ });
569
+ }
570
+ // If only one provider configured
571
+ if (availableProviders.size === 1) {
572
+ const single = [...availableProviders][0];
573
+ advice.push({
574
+ priority: 'high',
575
+ action: 'Add at least one more provider for failover and cost optimization',
576
+ reason: `You only have ${single} configured. If it goes down or rate-limits you, profClaw can't fall back. Adding a second provider also unlocks smart routing savings.`,
577
+ estimatedMonthlySavings: '$10-30/month (plus reliability)',
578
+ envVar: single === 'anthropic' ? 'GROQ_API_KEY' : 'ANTHROPIC_API_KEY',
579
+ signupUrl: single === 'anthropic' ? 'https://console.groq.com' : 'https://console.anthropic.com',
580
+ });
581
+ }
582
+ // If Ollama is available, suggest using it for trivial queries
583
+ if (availableProviders.has('ollama') && !availableProviders.has('groq')) {
584
+ advice.push({
585
+ priority: 'low',
586
+ action: 'Smart routing will use Ollama for trivial queries (free)',
587
+ reason: 'Your local Ollama models handle greetings and simple lookups at zero API cost',
588
+ estimatedMonthlySavings: '$2-5/month',
589
+ envVar: '',
590
+ signupUrl: '',
591
+ });
592
+ }
593
+ // Optimal setup check
594
+ const optimalProviders = ['groq', 'anthropic', 'google'];
595
+ const hasOptimal = optimalProviders.every(p => availableProviders.has(p));
596
+ if (hasOptimal && advice.length === 0) {
597
+ advice.push({
598
+ priority: 'low',
599
+ action: 'Your provider setup is cost-optimal',
600
+ reason: 'Groq (free trivial) + Gemini (cheap standard) + Anthropic (quality complex) covers all tiers efficiently',
601
+ estimatedMonthlySavings: 'Already optimized',
602
+ envVar: '',
603
+ signupUrl: '',
604
+ });
605
+ }
606
+ return advice;
607
+ }
608
+ // Helpers
609
+ function findModelInCatalog(modelId) {
610
+ return MODEL_CATALOG.find(m => m.id === modelId);
611
+ }
612
+ /**
613
+ * Estimate cost for a typical 6-turn conversation.
614
+ * Assumes ~1,800 total tokens (900 in, 900 out) per conversation.
615
+ */
616
+ function estimateConversationCost(modelId) {
617
+ const info = findModelInCatalog(modelId);
618
+ if (!info)
619
+ return 0.03; // fallback estimate
620
+ const inputCost = (900 / 1_000_000) * info.costPer1MInput;
621
+ const outputCost = (900 / 1_000_000) * info.costPer1MOutput;
622
+ return inputCost + outputCost;
623
+ }
624
+ /**
625
+ * Get a human-readable cost comparison for display.
626
+ */
627
+ export function getCostComparison(decision) {
628
+ const { complexity, selectedModel, estimatedCost, savingsPercent } = decision;
629
+ if (savingsPercent <= 0) {
630
+ return `${selectedModel.name} ($${estimatedCost.toFixed(4)}/conv)`;
631
+ }
632
+ return [
633
+ `Routed to ${selectedModel.name} (${complexity.tier})`,
634
+ `Est. cost: $${estimatedCost.toFixed(4)}/conv`,
635
+ `Saving ${savingsPercent}% vs default model`,
636
+ ].join(' | ');
637
+ }
638
+ /**
639
+ * Get cumulative savings stats for display in dashboard.
640
+ */
641
+ export function getRoutingStats() {
642
+ return { ...routingStats };
643
+ }
644
+ // Stats tracking (in-memory, persisted via cost history)
645
+ const routingStats = {
646
+ totalRouted: 0,
647
+ totalSaved: 0,
648
+ byTier: { trivial: 0, standard: 0, complex: 0 },
649
+ avgSavingsPercent: 0,
650
+ };
651
+ /**
652
+ * Record a routing decision for stats.
653
+ */
654
+ export function recordRoutingDecision(decision) {
655
+ routingStats.totalRouted++;
656
+ routingStats.byTier[decision.complexity.tier]++;
657
+ routingStats.totalSaved += decision.savedVsDefault;
658
+ // Rolling average
659
+ routingStats.avgSavingsPercent =
660
+ routingStats.avgSavingsPercent +
661
+ (decision.savingsPercent - routingStats.avgSavingsPercent) / routingStats.totalRouted;
662
+ }
663
+ // Configuration
664
+ export function configureSmartRouter(newConfig) {
665
+ config = { ...config, ...newConfig };
666
+ // Allow env override
667
+ if (process.env['SMART_ROUTER_ENABLED'] === 'false') {
668
+ config.enabled = false;
669
+ }
670
+ if (process.env['SMART_ROUTER_MAX_COST']) {
671
+ config.maxCostPerRequest = parseFloat(process.env['SMART_ROUTER_MAX_COST']);
672
+ }
673
+ logger.info('[SmartRouter] Configuration updated', {
674
+ enabled: config.enabled,
675
+ defaultModel: config.defaultModel,
676
+ maxCost: config.maxCostPerRequest,
677
+ });
678
+ }
679
+ export function getSmartRouterConfig() {
680
+ return { ...config };
681
+ }
682
+ export function isSmartRouterEnabled() {
683
+ return config.enabled;
684
+ }
685
+ //# sourceMappingURL=smart-router.js.map