profclaw 2.0.0 → 2.4.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 (519) hide show
  1. package/.env.example +14 -2
  2. package/CHANGELOG.md +52 -0
  3. package/CONTRIBUTING.md +20 -0
  4. package/Dockerfile +39 -23
  5. package/README.md +178 -41
  6. package/config/pricing.yml +26 -2
  7. package/dist/agents/agent-summary.d.ts +44 -0
  8. package/dist/agents/agent-summary.js +176 -0
  9. package/dist/agents/auto-memory.d.ts +59 -0
  10. package/dist/agents/auto-memory.js +310 -0
  11. package/dist/agents/checkpoint-manager.d.ts +76 -0
  12. package/dist/agents/checkpoint-manager.js +156 -0
  13. package/dist/agents/circuit-breaker.d.ts +54 -0
  14. package/dist/agents/circuit-breaker.js +153 -0
  15. package/dist/agents/command-sandbox.d.ts +93 -0
  16. package/dist/agents/command-sandbox.js +235 -0
  17. package/dist/agents/context-compactor.d.ts +70 -0
  18. package/dist/agents/context-compactor.js +361 -0
  19. package/dist/agents/conversation-branch.d.ts +55 -0
  20. package/dist/agents/conversation-branch.js +115 -0
  21. package/dist/agents/error-recovery.d.ts +44 -0
  22. package/dist/agents/error-recovery.js +197 -0
  23. package/dist/agents/events.d.ts +76 -0
  24. package/dist/agents/events.js +8 -0
  25. package/dist/agents/executor-runner.d.ts +135 -0
  26. package/dist/agents/executor-runner.js +303 -0
  27. package/dist/agents/executor.d.ts +37 -1
  28. package/dist/agents/executor.js +606 -124
  29. package/dist/agents/file-snapshots.d.ts +72 -0
  30. package/dist/agents/file-snapshots.js +182 -0
  31. package/dist/agents/index.d.ts +2 -0
  32. package/dist/agents/orchestrator.d.ts +183 -0
  33. package/dist/agents/orchestrator.js +495 -0
  34. package/dist/agents/output-truncator.d.ts +59 -0
  35. package/dist/agents/output-truncator.js +128 -0
  36. package/dist/agents/permissions.d.ts +63 -0
  37. package/dist/agents/permissions.js +138 -0
  38. package/dist/agents/plan-mode.d.ts +71 -0
  39. package/dist/agents/plan-mode.js +186 -0
  40. package/dist/agents/prompt-suggestions.d.ts +33 -0
  41. package/dist/agents/prompt-suggestions.js +208 -0
  42. package/dist/agents/rate-limit-monitor.d.ts +56 -0
  43. package/dist/agents/rate-limit-monitor.js +224 -0
  44. package/dist/agents/receipt/receipt-cli.d.ts +43 -0
  45. package/dist/agents/receipt/receipt-cli.js +82 -0
  46. package/dist/agents/receipt/receipt-types.d.ts +136 -0
  47. package/dist/agents/receipt/receipt-types.js +19 -0
  48. package/dist/agents/receipt/receipt-verify.d.ts +19 -0
  49. package/dist/agents/receipt/receipt-verify.js +176 -0
  50. package/dist/agents/receipt/receipt-view.d.ts +29 -0
  51. package/dist/agents/receipt/receipt-view.js +227 -0
  52. package/dist/agents/receipt/receipt-writer.d.ts +47 -0
  53. package/dist/agents/receipt/receipt-writer.js +186 -0
  54. package/dist/agents/result-store.d.ts +43 -0
  55. package/dist/agents/result-store.js +145 -0
  56. package/dist/agents/session-diff.d.ts +49 -0
  57. package/dist/agents/session-diff.js +249 -0
  58. package/dist/agents/tool-loader.d.ts +65 -0
  59. package/dist/agents/tool-loader.js +273 -0
  60. package/dist/agents/tool-selector.d.ts +123 -0
  61. package/dist/agents/tool-selector.js +321 -0
  62. package/dist/agents/transcript.d.ts +96 -0
  63. package/dist/agents/transcript.js +267 -0
  64. package/dist/agents/verified-run-git.d.ts +49 -0
  65. package/dist/agents/verified-run-git.js +82 -0
  66. package/dist/agents/verified-run.d.ts +149 -0
  67. package/dist/agents/verified-run.js +402 -0
  68. package/dist/agents/verifier.d.ts +64 -0
  69. package/dist/agents/verifier.js +186 -0
  70. package/dist/agents/worktree-manager.d.ts +80 -0
  71. package/dist/agents/worktree-manager.js +203 -0
  72. package/dist/agents/worktree-tools.d.ts +47 -0
  73. package/dist/agents/worktree-tools.js +246 -0
  74. package/dist/auth/middleware.js +14 -0
  75. package/dist/chat/agentic-executor.d.ts +7 -0
  76. package/dist/chat/agentic-executor.js +263 -32
  77. package/dist/chat/conversations.d.ts +2 -0
  78. package/dist/chat/conversations.js +8 -0
  79. package/dist/chat/execution/executor.d.ts +8 -0
  80. package/dist/chat/execution/executor.js +157 -103
  81. package/dist/chat/execution/index.js +5 -1
  82. package/dist/chat/execution/sandbox.d.ts +8 -0
  83. package/dist/chat/execution/sandbox.js +25 -1
  84. package/dist/chat/execution/tool-router.js +40 -14
  85. package/dist/chat/execution/tools/canvas.d.ts +2 -2
  86. package/dist/chat/execution/tools/code-tools.d.ts +66 -0
  87. package/dist/chat/execution/tools/code-tools.js +353 -0
  88. package/dist/chat/execution/tools/complete-task.d.ts +2 -2
  89. package/dist/chat/execution/tools/cron-tool.d.ts +26 -10
  90. package/dist/chat/execution/tools/cron-tool.js +48 -3
  91. package/dist/chat/execution/tools/decompose-task.d.ts +58 -0
  92. package/dist/chat/execution/tools/decompose-task.js +166 -0
  93. package/dist/chat/execution/tools/exec.d.ts +5 -2
  94. package/dist/chat/execution/tools/exec.js +294 -155
  95. package/dist/chat/execution/tools/feed-tools.d.ts +77 -0
  96. package/dist/chat/execution/tools/feed-tools.js +142 -0
  97. package/dist/chat/execution/tools/file-ops.d.ts +1 -1
  98. package/dist/chat/execution/tools/github.d.ts +2 -2
  99. package/dist/chat/execution/tools/index.d.ts +14 -2
  100. package/dist/chat/execution/tools/index.js +86 -12
  101. package/dist/chat/execution/tools/integrations.d.ts +8 -8
  102. package/dist/chat/execution/tools/multi-patch.d.ts +53 -0
  103. package/dist/chat/execution/tools/multi-patch.js +373 -0
  104. package/dist/chat/execution/tools/openai-image-gen.d.ts +2 -2
  105. package/dist/chat/execution/tools/plan-tools.d.ts +80 -0
  106. package/dist/chat/execution/tools/plan-tools.js +134 -0
  107. package/dist/chat/execution/tools/profclaw-ops.d.ts +6 -6
  108. package/dist/chat/execution/tools/repl-tool.d.ts +46 -0
  109. package/dist/chat/execution/tools/repl-tool.js +207 -0
  110. package/dist/chat/execution/tools/sessions-send.d.ts +2 -2
  111. package/dist/chat/execution/tools/slack-actions.d.ts +2 -2
  112. package/dist/chat/execution/tools/system.d.ts +4 -4
  113. package/dist/chat/execution/tools/todo-tool.d.ts +72 -0
  114. package/dist/chat/execution/tools/todo-tool.js +152 -0
  115. package/dist/chat/execution/tools/tool-search.d.ts +50 -0
  116. package/dist/chat/execution/tools/tool-search.js +155 -0
  117. package/dist/chat/execution/tools/tts-speak.d.ts +2 -2
  118. package/dist/chat/execution/types.d.ts +21 -14
  119. package/dist/chat/execution/workflows/executor.d.ts +11 -0
  120. package/dist/chat/execution/workflows/executor.js +99 -1
  121. package/dist/chat/execution/workflows/types.d.ts +35 -2
  122. package/dist/chat/format/index.d.ts +1 -0
  123. package/dist/chat/format/index.js +1 -0
  124. package/dist/chat/format/markdown-convert.d.ts +23 -0
  125. package/dist/chat/format/markdown-convert.js +149 -0
  126. package/dist/chat/index.d.ts +1 -1
  127. package/dist/chat/index.js +1 -1
  128. package/dist/chat/message-handler.js +58 -6
  129. package/dist/chat/meta-tool-handlers.d.ts +18 -0
  130. package/dist/chat/meta-tool-handlers.js +51 -0
  131. package/dist/chat/proactive/index.d.ts +62 -0
  132. package/dist/chat/proactive/index.js +147 -0
  133. package/dist/chat/prompt-adapter.js +2 -0
  134. package/dist/chat/providers/irc/index.d.ts +2 -2
  135. package/dist/chat/providers/telegram/index.d.ts +2 -0
  136. package/dist/chat/providers/telegram/index.js +21 -2
  137. package/dist/chat/system-prompts.d.ts +3 -3
  138. package/dist/chat/system-prompts.js +121 -61
  139. package/dist/chat/tool-handler.d.ts +2 -0
  140. package/dist/chat/tool-handler.js +47 -3
  141. package/dist/chat/tools.d.ts +6 -6
  142. package/dist/cli/commands/auth.js +64 -1
  143. package/dist/cli/commands/chat.js +1327 -138
  144. package/dist/cli/commands/doctor.js +222 -16
  145. package/dist/cli/commands/history.d.ts +3 -0
  146. package/dist/cli/commands/history.js +138 -0
  147. package/dist/cli/commands/import.d.ts +44 -0
  148. package/dist/cli/commands/import.js +526 -0
  149. package/dist/cli/commands/init.d.ts +13 -0
  150. package/dist/cli/commands/init.js +381 -0
  151. package/dist/cli/commands/onboard.js +210 -30
  152. package/dist/cli/commands/plan.d.ts +13 -0
  153. package/dist/cli/commands/plan.js +184 -0
  154. package/dist/cli/commands/receipt.d.ts +7 -0
  155. package/dist/cli/commands/receipt.js +90 -0
  156. package/dist/cli/commands/run.d.ts +28 -0
  157. package/dist/cli/commands/run.js +204 -0
  158. package/dist/cli/commands/serve.d.ts +7 -0
  159. package/dist/cli/commands/serve.js +77 -18
  160. package/dist/cli/commands/setup.js +84 -3
  161. package/dist/cli/commands/status.js +54 -0
  162. package/dist/cli/commands/tools.js +13 -13
  163. package/dist/cli/commands/tui.js +26 -1
  164. package/dist/cli/index.js +23 -0
  165. package/dist/cli/ink/App.d.ts +13 -0
  166. package/dist/cli/ink/App.js +6 -0
  167. package/dist/cli/ink/ChatApp.d.ts +60 -0
  168. package/dist/cli/ink/ChatApp.js +325 -0
  169. package/dist/cli/ink/DashboardApp.d.ts +3 -0
  170. package/dist/cli/ink/DashboardApp.js +143 -0
  171. package/dist/cli/ink/components/AgentStatus.d.ts +17 -0
  172. package/dist/cli/ink/components/AgentStatus.js +33 -0
  173. package/dist/cli/ink/components/ChannelList.d.ts +17 -0
  174. package/dist/cli/ink/components/ChannelList.js +33 -0
  175. package/dist/cli/ink/components/ConnectionStatus.d.ts +19 -0
  176. package/dist/cli/ink/components/ConnectionStatus.js +50 -0
  177. package/dist/cli/ink/components/CostBar.d.ts +19 -0
  178. package/dist/cli/ink/components/CostBar.js +35 -0
  179. package/dist/cli/ink/components/HookStatus.d.ts +15 -0
  180. package/dist/cli/ink/components/HookStatus.js +7 -0
  181. package/dist/cli/ink/components/PermissionPrompt.d.ts +21 -0
  182. package/dist/cli/ink/components/PermissionPrompt.js +51 -0
  183. package/dist/cli/ink/components/PlanView.d.ts +23 -0
  184. package/dist/cli/ink/components/PlanView.js +26 -0
  185. package/dist/cli/ink/components/ProviderList.d.ts +16 -0
  186. package/dist/cli/ink/components/ProviderList.js +16 -0
  187. package/dist/cli/ink/components/SessionHeader.d.ts +15 -0
  188. package/dist/cli/ink/components/SessionHeader.js +15 -0
  189. package/dist/cli/ink/components/StreamingMessage.d.ts +20 -0
  190. package/dist/cli/ink/components/StreamingMessage.js +27 -0
  191. package/dist/cli/ink/components/SuggestionBar.d.ts +15 -0
  192. package/dist/cli/ink/components/SuggestionBar.js +28 -0
  193. package/dist/cli/ink/components/SystemHealth.d.ts +20 -0
  194. package/dist/cli/ink/components/SystemHealth.js +39 -0
  195. package/dist/cli/ink/components/TaskList.d.ts +17 -0
  196. package/dist/cli/ink/components/TaskList.js +59 -0
  197. package/dist/cli/ink/components/ToolCall.d.ts +17 -0
  198. package/dist/cli/ink/components/ToolCall.js +43 -0
  199. package/dist/cli/ink/index.d.ts +23 -0
  200. package/dist/cli/ink/index.js +15 -0
  201. package/dist/cli/ink/keybindings.d.ts +35 -0
  202. package/dist/cli/ink/keybindings.js +105 -0
  203. package/dist/cli/ink/markdown-renderer.d.ts +47 -0
  204. package/dist/cli/ink/markdown-renderer.js +380 -0
  205. package/dist/cli/ink/output-styles.d.ts +39 -0
  206. package/dist/cli/ink/output-styles.js +119 -0
  207. package/dist/cli/interactive/auto-serve.d.ts +38 -0
  208. package/dist/cli/interactive/auto-serve.js +209 -0
  209. package/dist/cli/interactive/index.d.ts +25 -0
  210. package/dist/cli/interactive/index.js +23 -0
  211. package/dist/cli/interactive/picker.d.ts +28 -0
  212. package/dist/cli/interactive/picker.js +153 -0
  213. package/dist/cli/interactive/renderer.d.ts +26 -0
  214. package/dist/cli/interactive/renderer.js +555 -0
  215. package/dist/cli/interactive/repl.d.ts +14 -0
  216. package/dist/cli/interactive/repl.js +1516 -0
  217. package/dist/cli/interactive/stream-client.d.ts +59 -0
  218. package/dist/cli/interactive/stream-client.js +506 -0
  219. package/dist/cli/interactive/types.d.ts +121 -0
  220. package/dist/cli/interactive/types.js +10 -0
  221. package/dist/cli/utils/api.d.ts +9 -0
  222. package/dist/cli/utils/api.js +58 -2
  223. package/dist/costs/token-tracker.d.ts +10 -1
  224. package/dist/costs/token-tracker.js +37 -5
  225. package/dist/cron/natural-language.d.ts +54 -0
  226. package/dist/cron/natural-language.js +569 -0
  227. package/dist/cron/scheduler.d.ts +34 -4
  228. package/dist/cron/scheduler.js +141 -9
  229. package/dist/cron/templates.d.ts +1 -1
  230. package/dist/cron/templates.js +219 -0
  231. package/dist/feeds/feed-engine.d.ts +149 -0
  232. package/dist/feeds/feed-engine.js +600 -0
  233. package/dist/feeds/index.d.ts +9 -0
  234. package/dist/feeds/index.js +8 -0
  235. package/dist/gateway/router.js +6 -1
  236. package/dist/hooks/built-in/audit-log.d.ts +11 -0
  237. package/dist/hooks/built-in/audit-log.js +84 -0
  238. package/dist/hooks/built-in/cost-warning.d.ts +10 -0
  239. package/dist/hooks/built-in/cost-warning.js +59 -0
  240. package/dist/hooks/built-in/dangerous-tool.d.ts +32 -0
  241. package/dist/hooks/built-in/dangerous-tool.js +80 -0
  242. package/dist/hooks/built-in/index.d.ts +14 -0
  243. package/dist/hooks/built-in/index.js +20 -0
  244. package/dist/hooks/index.d.ts +3 -0
  245. package/dist/hooks/index.js +6 -0
  246. package/dist/hooks/loader.d.ts +17 -0
  247. package/dist/hooks/loader.js +142 -0
  248. package/dist/hooks/registry.d.ts +62 -0
  249. package/dist/hooks/registry.js +127 -0
  250. package/dist/integrations/web-search.js +6 -3
  251. package/dist/memory/index.d.ts +1 -0
  252. package/dist/memory/index.js +2 -0
  253. package/dist/memory/memory-service.js +14 -13
  254. package/dist/memory/observational.d.ts +62 -0
  255. package/dist/memory/observational.js +396 -0
  256. package/dist/middleware/content-filter.d.ts +22 -0
  257. package/dist/middleware/content-filter.js +69 -0
  258. package/dist/middleware/request-validator.d.ts +159 -0
  259. package/dist/middleware/request-validator.js +93 -0
  260. package/dist/plugins/sdk.d.ts +22 -0
  261. package/dist/plugins/sdk.js +17 -1
  262. package/dist/projects/types.d.ts +10 -10
  263. package/dist/providers/ai-sdk.d.ts +11 -0
  264. package/dist/providers/ai-sdk.js +111 -56
  265. package/dist/providers/core/models.js +114 -6
  266. package/dist/providers/core/types.d.ts +7 -0
  267. package/dist/providers/index.d.ts +1 -0
  268. package/dist/providers/index.js +1 -0
  269. package/dist/providers/prompt-cache.d.ts +68 -0
  270. package/dist/providers/prompt-cache.js +165 -0
  271. package/dist/providers/smart-router.d.ts +118 -0
  272. package/dist/providers/smart-router.js +693 -0
  273. package/dist/providers/types.d.ts +16 -16
  274. package/dist/routes/agents.js +50 -0
  275. package/dist/routes/auth.js +8 -1
  276. package/dist/routes/chat.js +280 -18
  277. package/dist/routes/costs.js +32 -0
  278. package/dist/routes/cron.js +80 -5
  279. package/dist/routes/feeds.d.ts +10 -0
  280. package/dist/routes/feeds.js +168 -0
  281. package/dist/routes/mcp.js +21 -0
  282. package/dist/routes/memory.js +130 -0
  283. package/dist/routes/skills.js +129 -0
  284. package/dist/routes/teams.d.ts +9 -0
  285. package/dist/routes/teams.js +210 -0
  286. package/dist/routes/tools.js +14 -0
  287. package/dist/routes/users.js +2 -0
  288. package/dist/server/route-loader.js +3 -1
  289. package/dist/server/stream-bridge.d.ts +19 -0
  290. package/dist/server/stream-bridge.js +38 -0
  291. package/dist/server.js +313 -36
  292. package/dist/skills/marketplace.d.ts +87 -0
  293. package/dist/skills/marketplace.js +268 -0
  294. package/dist/storage/index.d.ts +14 -0
  295. package/dist/storage/index.js +64 -3
  296. package/dist/storage/schema.d.ts +4 -4
  297. package/dist/storage/schema.js +1 -1
  298. package/dist/teams/index.d.ts +110 -0
  299. package/dist/teams/index.js +365 -0
  300. package/dist/tickets/types.d.ts +39 -39
  301. package/dist/types/summary.d.ts +5 -5
  302. package/dist/utils/auto-update.d.ts +23 -0
  303. package/dist/utils/auto-update.js +157 -0
  304. package/dist/utils/offline-detect.d.ts +55 -0
  305. package/dist/utils/offline-detect.js +121 -0
  306. package/dist/utils/pid-file.d.ts +36 -0
  307. package/dist/utils/pid-file.js +125 -0
  308. package/dist/utils/prevent-sleep.d.ts +27 -0
  309. package/dist/utils/prevent-sleep.js +95 -0
  310. package/dist/utils/sanitizer.d.ts +57 -0
  311. package/dist/utils/sanitizer.js +174 -0
  312. package/docker-compose.yml +13 -137
  313. package/package.json +31 -6
  314. package/skills/deploy-cloudflare/SKILL.md +62 -0
  315. package/skills/deploy-vercel/SKILL.md +67 -0
  316. package/skills/docker-deploy/SKILL.md +158 -0
  317. package/skills/full-stack-builder/SKILL.md +92 -0
  318. package/skills/humanizer/SKILL.md +488 -0
  319. package/skills/profclaw-assistant/SKILL.md +85 -56
  320. package/ui/dist/assets/ActivityView-Da6HQ8ws.js +1 -0
  321. package/ui/dist/assets/AgentList-H58R0kX3.js +1 -0
  322. package/ui/dist/assets/AnalyticsDashboard-B28AeRsc.js +1 -0
  323. package/ui/dist/assets/AreaChart-DwLzeAQ8.js +1 -0
  324. package/ui/dist/assets/CartesianChart-piKQsTAR.js +36 -0
  325. package/ui/dist/assets/ChatView-Dbj8rxU5.js +10 -0
  326. package/ui/dist/assets/CostsDashboard-C6F5df0F.js +1 -0
  327. package/ui/dist/assets/EstimateSelect-wga0rUKX.js +1 -0
  328. package/ui/dist/assets/FeedDashboard-Ef6FEzEf.js +1 -0
  329. package/ui/dist/assets/FloatingChatbot-0yhkUeCq.js +2 -0
  330. package/ui/dist/assets/InviteCodeManagement-tUXfx4Bc.js +2 -0
  331. package/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  332. package/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  333. package/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  334. package/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  335. package/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  336. package/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  337. package/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  338. package/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  339. package/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  340. package/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  341. package/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  342. package/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  343. package/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  344. package/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  345. package/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  346. package/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  347. package/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  348. package/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  349. package/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  350. package/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  351. package/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  352. package/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  353. package/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  354. package/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  355. package/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  356. package/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  357. package/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  358. package/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  359. package/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  360. package/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  361. package/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  362. package/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  363. package/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  364. package/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  365. package/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  366. package/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  367. package/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  368. package/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  369. package/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  370. package/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  371. package/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  372. package/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  373. package/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  374. package/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  375. package/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  376. package/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  377. package/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  378. package/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  379. package/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  380. package/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  381. package/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  382. package/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  383. package/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  384. package/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  385. package/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  386. package/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  387. package/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  388. package/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  389. package/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  390. package/ui/dist/assets/MarketplacePage-DGrLtolR.js +1 -0
  391. package/ui/dist/assets/{ProjectDetail--gPfvATB.js → ProjectDetail-BgWvA6UP.js} +2 -2
  392. package/ui/dist/assets/ProjectIcon-TDuliqwg.js +1 -0
  393. package/ui/dist/assets/ProjectList-DXgM4187.js +1 -0
  394. package/ui/dist/assets/{Settings-1kaf7VHE.js → Settings-xA8Zhi3p.js} +3 -3
  395. package/ui/dist/assets/StatusIndicator-B8vfFAxg.js +1 -0
  396. package/ui/dist/assets/{SummaryDetail-DV3AmONk.js → SummaryDetail-eedRfTtF.js} +2 -2
  397. package/ui/dist/assets/SummaryList-CLNX2P84.js +1 -0
  398. package/ui/dist/assets/TaskDetail-BtWclNiR.js +1 -0
  399. package/ui/dist/assets/TaskList-JjODAgSc.js +1 -0
  400. package/ui/dist/assets/TeamsPage-CBK5iAyx.js +1 -0
  401. package/ui/dist/assets/TicketBoard-C9aJNNYp.js +1 -0
  402. package/ui/dist/assets/TicketDetail-CV5JmL31.js +1 -0
  403. package/ui/dist/assets/TicketList-68V4lzUz.js +1 -0
  404. package/ui/dist/assets/UserManagement-DKTb6UpE.js +2 -0
  405. package/ui/dist/assets/ViewSwitcher-BvdQht2Y.js +1 -0
  406. package/ui/dist/assets/alert-dialog-O6qBOv_f.js +1 -0
  407. package/ui/dist/assets/{archive-BsLa7brF.js → archive-CX7UvZpf.js} +1 -1
  408. package/ui/dist/assets/badge-BbfnwDJP.js +1 -0
  409. package/ui/dist/assets/{bug-CmZI7jqM.js → bug-3WX0394-.js} +1 -1
  410. package/ui/dist/assets/{calendar-Bf0t2B82.js → calendar-buq77TaS.js} +1 -1
  411. package/ui/dist/assets/{chevron-up-CoUsOvZj.js → chevron-up-4ymsBJRo.js} +1 -1
  412. package/ui/dist/assets/{circle-x-Dgw11LgT.js → circle-x-ByXtwR6Y.js} +1 -1
  413. package/ui/dist/assets/constants-CVdDh7p0.js +1 -0
  414. package/ui/dist/assets/{cpu-DiPMQICV.js → cpu-CiuoCeDY.js} +1 -1
  415. package/ui/dist/assets/{database-BwTfupDi.js → database-BWXSBg5a.js} +1 -1
  416. package/ui/dist/assets/dialog-lDg4NvMV.js +1 -0
  417. package/ui/dist/assets/dollar-sign-Dmijd6DP.js +1 -0
  418. package/ui/dist/assets/{ellipsis-UJx4Xnf6.js → ellipsis-vT-5SVIp.js} +1 -1
  419. package/ui/dist/assets/{ellipsis-vertical-DoULYEKn.js → ellipsis-vertical-B4tAX_bL.js} +1 -1
  420. package/ui/dist/assets/feature-markdown-DBo5Q7uq.js +289 -0
  421. package/ui/dist/assets/{file-code-B2UrJLwi.js → file-code-ChOWdKXf.js} +1 -1
  422. package/ui/dist/assets/{folder-open-BJi577JH.js → folder-open-CkOUbIiP.js} +1 -1
  423. package/ui/dist/assets/{funnel-PkaD_HSR.js → funnel-uZkoSPP-.js} +1 -1
  424. package/ui/dist/assets/{git-branch-C_K30am0.js → git-branch-CwvUxj7C.js} +1 -1
  425. package/ui/dist/assets/{git-commit-horizontal-IT6ecxWQ.js → git-commit-horizontal-lxPzCvhD.js} +1 -1
  426. package/ui/dist/assets/{globe-f-jjCa_g.js → globe-D_WpJdsj.js} +1 -1
  427. package/ui/dist/assets/{hash-DNEv8Elo.js → hash-CA0hJ7vh.js} +1 -1
  428. package/ui/dist/assets/{history-BQSBo_1Q.js → history-CEWMLELs.js} +1 -1
  429. package/ui/dist/assets/inbox-ehtOptTV.js +1 -0
  430. package/ui/dist/assets/{index-nS3EErDg.js → index-C1rhbml4.js} +3 -3
  431. package/ui/dist/assets/index-CR8gfCHv.js +1 -0
  432. package/ui/dist/assets/index-DVXMpWai.js +1 -0
  433. package/ui/dist/assets/index-DaDJMM2v.js +1 -0
  434. package/ui/dist/assets/index-Djdq_0xu.js +1 -0
  435. package/ui/dist/assets/index-M46Gcb8K.css +1 -0
  436. package/ui/dist/assets/index-sEBN7w8J.js +109 -0
  437. package/ui/dist/assets/layers-C6Mhf8Nk.js +1 -0
  438. package/ui/dist/assets/{layout-dashboard-CcKt1hXw.js → layout-dashboard-BRUM0kpT.js} +1 -1
  439. package/ui/dist/assets/{lightbulb-Jhp7FvbU.js → lightbulb-CN2q1O5l.js} +1 -1
  440. package/ui/dist/assets/{link-2-DdO7ostF.js → link-2-CC4E28yJ.js} +1 -1
  441. package/ui/dist/assets/markdown-renderer-C6ENbB0u.js +10 -0
  442. package/ui/dist/assets/markdown-renderer-CfVKi3_s.css +1 -0
  443. package/ui/dist/assets/{message-square-CBgH65X7.js → message-square-Bbb241am.js} +1 -1
  444. package/ui/dist/assets/package-CwT3IzFx.js +1 -0
  445. package/ui/dist/assets/{pencil-CYXCEPQh.js → pencil-CDmiHzoj.js} +1 -1
  446. package/ui/dist/assets/{play-d1ONaH0-.js → play-tjy5jMQX.js} +1 -1
  447. package/ui/dist/assets/popover-CFHZf3pE.js +1 -0
  448. package/ui/dist/assets/progress-BboUHuLf.js +1 -0
  449. package/ui/dist/assets/{rocket-DhvUetsk.js → rocket--8ZHgdXJ.js} +1 -1
  450. package/ui/dist/assets/select-n108rXny.js +1 -0
  451. package/ui/dist/assets/{send-P0r5D7td.js → send-Cu7MR2Qh.js} +1 -1
  452. package/ui/dist/assets/server-DmodxZp-.js +1 -0
  453. package/ui/dist/assets/{settings-2-ocj5uHGx.js → settings-2-CkRNqkxj.js} +1 -1
  454. package/ui/dist/assets/sheet-7aUJkyzU.js +1 -0
  455. package/ui/dist/assets/{shield-check-DfVdE34l.js → shield-check-ViI3nMKE.js} +1 -1
  456. package/ui/dist/assets/{shield-off-8l0J6R-8.js → shield-off-DN3tagkn.js} +1 -1
  457. package/ui/dist/assets/skeleton-C0E1Nfro.js +1 -0
  458. package/ui/dist/assets/{smartphone-77_g3SQn.js → smartphone-5FghiXpA.js} +1 -1
  459. package/ui/dist/assets/{square-Ckzsr3Rn.js → square-DiNSx8K7.js} +1 -1
  460. package/ui/dist/assets/star-DhdFVidS.js +1 -0
  461. package/ui/dist/assets/switch-CIgVbZqQ.js +1 -0
  462. package/ui/dist/assets/table-BC56FVMV.js +1 -0
  463. package/ui/dist/assets/{tag-C35mgoEv.js → tag-CH_vQliw.js} +1 -1
  464. package/ui/dist/assets/textarea-CxDiKFXZ.js +1 -0
  465. package/ui/dist/assets/{timer-DHJBS2w1.js → timer-CdGVTb60.js} +1 -1
  466. package/ui/dist/assets/tooltip-BuW9A5UO.js +1 -0
  467. package/ui/dist/assets/{trash-2-keO5tKPE.js → trash-2-CucZJL0N.js} +1 -1
  468. package/ui/dist/assets/trending-down-BCezC1z2.js +1 -0
  469. package/ui/dist/assets/{trending-up-BBv8KbrY.js → trending-up-C5OYX2k2.js} +1 -1
  470. package/ui/dist/assets/vendor-query-C0p7b53x.js +4 -0
  471. package/ui/dist/assets/vendor-react-Mc5_kfYG.js +12 -0
  472. package/ui/dist/assets/vendor-ui-CXPAUBFv.js +51 -0
  473. package/ui/dist/assets/{wand-sparkles-BusQ52ZK.js → wand-sparkles-BaH6pJnc.js} +1 -1
  474. package/ui/dist/assets/{wrench-02YaOSiK.js → wrench-CjxjP6ok.js} +1 -1
  475. package/ui/dist/index.html +5 -2
  476. package/ui/dist/assets/ActivityView-BeshZXmE.js +0 -1
  477. package/ui/dist/assets/AgentList-QVHeldPi.js +0 -1
  478. package/ui/dist/assets/AnalyticsDashboard-C9R97dXE.js +0 -1
  479. package/ui/dist/assets/AreaChart-B73gaiVQ.js +0 -1
  480. package/ui/dist/assets/CartesianChart-CXYL2R-A.js +0 -36
  481. package/ui/dist/assets/ChatView-qjZErlrp.js +0 -10
  482. package/ui/dist/assets/CostsDashboard-O4DCLy1i.js +0 -1
  483. package/ui/dist/assets/EstimateSelect-1hQf4Osk.js +0 -1
  484. package/ui/dist/assets/FloatingChatbot-BZBzrJxd.js +0 -2
  485. package/ui/dist/assets/InviteCodeManagement-LEoGqYeA.js +0 -2
  486. package/ui/dist/assets/ProjectIcon-Chd_ofsw.js +0 -1
  487. package/ui/dist/assets/ProjectList-BvUfVB6c.js +0 -4
  488. package/ui/dist/assets/StatusIndicator-pRfkNcdV.js +0 -1
  489. package/ui/dist/assets/SummaryList-B6kXiTxL.js +0 -1
  490. package/ui/dist/assets/TaskDetail-CV6CzbOS.js +0 -1
  491. package/ui/dist/assets/TaskList-DL7brQKd.js +0 -1
  492. package/ui/dist/assets/TicketBoard-CrkkFj7C.js +0 -1
  493. package/ui/dist/assets/TicketDetail-hnnYclpD.js +0 -1
  494. package/ui/dist/assets/TicketList-CsDAyQCL.js +0 -1
  495. package/ui/dist/assets/UserManagement-BGFQyTSr.js +0 -2
  496. package/ui/dist/assets/ViewSwitcher-CnL0OqA8.js +0 -1
  497. package/ui/dist/assets/alert-dialog-SKM6Gjz4.js +0 -7
  498. package/ui/dist/assets/badge-CVHH1a-v.js +0 -1
  499. package/ui/dist/assets/brain-6o_RHVpe.js +0 -1
  500. package/ui/dist/assets/constants-Q5Zivl-a.js +0 -1
  501. package/ui/dist/assets/dialog-BUqN9Psj.js +0 -1
  502. package/ui/dist/assets/index-BUmnwPQr.js +0 -1
  503. package/ui/dist/assets/index-BfF5G4Cw.js +0 -1
  504. package/ui/dist/assets/index-C8N375c2.js +0 -1
  505. package/ui/dist/assets/index-CuxnqVVz.css +0 -1
  506. package/ui/dist/assets/index-CzMM3QOk.js +0 -1
  507. package/ui/dist/assets/index-lJlNPFVJ.js +0 -67
  508. package/ui/dist/assets/layers-jFqVrWMD.js +0 -1
  509. package/ui/dist/assets/markdown-renderer-n1ToC4XQ.js +0 -36
  510. package/ui/dist/assets/popover-CrmZgoWu.js +0 -1
  511. package/ui/dist/assets/progress-uv4SPkL5.js +0 -1
  512. package/ui/dist/assets/select-CeQ8AHGy.js +0 -1
  513. package/ui/dist/assets/server-CVHTZB72.js +0 -1
  514. package/ui/dist/assets/sheet-DuQExSho.js +0 -1
  515. package/ui/dist/assets/skeleton-TFiFJ3nB.js +0 -1
  516. package/ui/dist/assets/switch-CAePkd6k.js +0 -1
  517. package/ui/dist/assets/table-ZjAZ7hkt.js +0 -1
  518. package/ui/dist/assets/textarea-DsVPnSgR.js +0 -1
  519. package/ui/dist/assets/tooltip-BYmQ331W.js +0 -1
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Checkpoint Manager
3
+ *
4
+ * Saves and restores executor state so long-running agentic sessions can be
5
+ * interrupted and resumed without losing progress. Checkpoints live at
6
+ * .profclaw/checkpoints/<sessionId>.json relative to the project root (or the
7
+ * current working directory when no root is supplied).
8
+ */
9
+ import { mkdir, readFile, writeFile, unlink, readdir } from 'node:fs/promises';
10
+ import { existsSync } from 'node:fs';
11
+ import { join, resolve } from 'node:path';
12
+ import { logger } from '../utils/logger.js';
13
+ // ─── CheckpointManager ────────────────────────────────────────────────────────
14
+ export class CheckpointManager {
15
+ checkpointsDir;
16
+ constructor(projectRoot) {
17
+ const root = projectRoot ? resolve(projectRoot) : process.cwd();
18
+ this.checkpointsDir = join(root, '.profclaw', 'checkpoints');
19
+ }
20
+ // ── Private helpers ──────────────────────────────────────────────────────
21
+ filePath(sessionId) {
22
+ return join(this.checkpointsDir, `${sessionId}.json`);
23
+ }
24
+ async ensureDir() {
25
+ if (!existsSync(this.checkpointsDir)) {
26
+ await mkdir(this.checkpointsDir, { recursive: true });
27
+ }
28
+ }
29
+ // ── Public API ───────────────────────────────────────────────────────────
30
+ /**
31
+ * Persist a checkpoint to disk. Creates .profclaw/checkpoints/ if needed.
32
+ */
33
+ async save(checkpoint) {
34
+ await this.ensureDir();
35
+ const path = this.filePath(checkpoint.sessionId);
36
+ const payload = JSON.stringify({ ...checkpoint, updatedAt: Date.now() }, null, 2);
37
+ await writeFile(path, payload, 'utf-8');
38
+ logger.debug('[CheckpointManager] Saved checkpoint', {
39
+ sessionId: checkpoint.sessionId,
40
+ step: checkpoint.currentStep,
41
+ path,
42
+ });
43
+ }
44
+ /**
45
+ * Load a previously saved checkpoint. Returns null when none exists.
46
+ */
47
+ async load(sessionId) {
48
+ const path = this.filePath(sessionId);
49
+ try {
50
+ const raw = await readFile(path, 'utf-8');
51
+ const parsed = JSON.parse(raw);
52
+ logger.debug('[CheckpointManager] Loaded checkpoint', {
53
+ sessionId,
54
+ step: parsed.currentStep,
55
+ });
56
+ return parsed;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ /**
63
+ * List all checkpoints, sorted newest first.
64
+ */
65
+ async list() {
66
+ if (!existsSync(this.checkpointsDir))
67
+ return [];
68
+ let files;
69
+ try {
70
+ files = await readdir(this.checkpointsDir);
71
+ }
72
+ catch {
73
+ return [];
74
+ }
75
+ const summaries = [];
76
+ for (const file of files) {
77
+ if (!file.endsWith('.json'))
78
+ continue;
79
+ try {
80
+ const raw = await readFile(join(this.checkpointsDir, file), 'utf-8');
81
+ const cp = JSON.parse(raw);
82
+ summaries.push({
83
+ sessionId: cp.sessionId,
84
+ taskDescription: cp.taskDescription,
85
+ step: cp.currentStep,
86
+ updatedAt: cp.updatedAt,
87
+ });
88
+ }
89
+ catch {
90
+ // Skip malformed files
91
+ }
92
+ }
93
+ return summaries.sort((a, b) => b.updatedAt - a.updatedAt);
94
+ }
95
+ /**
96
+ * Delete a checkpoint after the session has completed successfully.
97
+ */
98
+ async remove(sessionId) {
99
+ const path = this.filePath(sessionId);
100
+ try {
101
+ await unlink(path);
102
+ logger.debug('[CheckpointManager] Removed checkpoint', { sessionId });
103
+ }
104
+ catch {
105
+ // Already gone — not an error
106
+ }
107
+ }
108
+ /**
109
+ * Find a resumable checkpoint whose taskDescription loosely matches the
110
+ * given description (case-insensitive substring + word overlap heuristic).
111
+ * Returns the most recently updated match, or null if nothing qualifies.
112
+ */
113
+ async findResumable(taskDescription) {
114
+ const all = await this.list();
115
+ if (all.length === 0)
116
+ return null;
117
+ const needle = taskDescription.toLowerCase();
118
+ const needleWords = new Set(needle.split(/\W+/).filter(Boolean));
119
+ let bestScore = 0;
120
+ let bestId = null;
121
+ for (const summary of all) {
122
+ if (!summary.taskDescription)
123
+ continue;
124
+ const hay = summary.taskDescription.toLowerCase();
125
+ // Substring match is a strong signal
126
+ if (hay.includes(needle) || needle.includes(hay)) {
127
+ // Prefer the most recently updated substring match
128
+ if (summary.updatedAt > (bestScore === 2 ? 0 : -1)) {
129
+ bestScore = 2;
130
+ bestId = summary.sessionId;
131
+ }
132
+ continue;
133
+ }
134
+ // Word-overlap fallback
135
+ const hayWords = hay.split(/\W+/).filter(Boolean);
136
+ const overlap = hayWords.filter((w) => needleWords.has(w)).length;
137
+ const ratio = needleWords.size > 0 ? overlap / needleWords.size : 0;
138
+ if (ratio >= 0.5 && ratio > bestScore) {
139
+ bestScore = ratio;
140
+ bestId = summary.sessionId;
141
+ }
142
+ }
143
+ if (!bestId)
144
+ return null;
145
+ return this.load(bestId);
146
+ }
147
+ }
148
+ // ─── Singleton ────────────────────────────────────────────────────────────────
149
+ let _instance;
150
+ export function getCheckpointManager(projectRoot) {
151
+ if (!_instance) {
152
+ _instance = new CheckpointManager(projectRoot);
153
+ }
154
+ return _instance;
155
+ }
156
+ //# sourceMappingURL=checkpoint-manager.js.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Tool Circuit Breaker
3
+ *
4
+ * Prevents tools from being called repeatedly when they are failing.
5
+ * Implements the standard circuit breaker pattern:
6
+ * closed → (3 failures in 2 min) → open → (cooldown expires) → half-open
7
+ * half-open → (1 success) → closed
8
+ * half-open → (1 failure) → open (doubled cooldown)
9
+ */
10
+ export type CircuitBreakerStateValue = 'closed' | 'open' | 'half-open';
11
+ export interface CircuitBreakerState {
12
+ state: CircuitBreakerStateValue;
13
+ failureCount: number;
14
+ lastFailureAt: number;
15
+ cooldownMs: number;
16
+ }
17
+ export interface CanExecuteResult {
18
+ allowed: boolean;
19
+ reason?: string;
20
+ }
21
+ export declare class ToolCircuitBreaker {
22
+ private breakers;
23
+ private readonly failureThreshold;
24
+ private readonly windowMs;
25
+ constructor(failureThreshold?: number, windowMs?: number);
26
+ /**
27
+ * Check whether a tool is allowed to execute.
28
+ * Transitions open → half-open when the cooldown has expired.
29
+ */
30
+ canExecute(toolName: string): CanExecuteResult;
31
+ /**
32
+ * Record a successful tool execution.
33
+ * In half-open state this closes the circuit.
34
+ */
35
+ recordSuccess(toolName: string): void;
36
+ /**
37
+ * Record a failed tool execution.
38
+ * Counts failures within the time window and trips the breaker when the
39
+ * threshold is reached. In half-open state it immediately re-opens with
40
+ * a doubled cooldown.
41
+ */
42
+ recordFailure(toolName: string): void;
43
+ /**
44
+ * Reset the circuit breaker for a specific tool, or all tools if no name is given.
45
+ */
46
+ reset(toolName?: string): void;
47
+ /**
48
+ * Return a deep snapshot of all breaker states.
49
+ * Mutations to the live breakers do not affect the returned map.
50
+ */
51
+ getStatus(): Map<string, CircuitBreakerState>;
52
+ private getOrCreate;
53
+ }
54
+ //# sourceMappingURL=circuit-breaker.d.ts.map
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Tool Circuit Breaker
3
+ *
4
+ * Prevents tools from being called repeatedly when they are failing.
5
+ * Implements the standard circuit breaker pattern:
6
+ * closed → (3 failures in 2 min) → open → (cooldown expires) → half-open
7
+ * half-open → (1 success) → closed
8
+ * half-open → (1 failure) → open (doubled cooldown)
9
+ */
10
+ import { logger } from '../utils/logger.js';
11
+ // Constants
12
+ const DEFAULT_FAILURE_THRESHOLD = 3;
13
+ const DEFAULT_WINDOW_MS = 120_000; // 2 minutes
14
+ const INITIAL_COOLDOWN_MS = 5_000;
15
+ const MAX_COOLDOWN_MS = 60_000;
16
+ function defaultBreakerState() {
17
+ return {
18
+ state: 'closed',
19
+ failureCount: 0,
20
+ lastFailureAt: 0,
21
+ cooldownMs: INITIAL_COOLDOWN_MS,
22
+ };
23
+ }
24
+ // Circuit Breaker
25
+ export class ToolCircuitBreaker {
26
+ breakers;
27
+ failureThreshold;
28
+ windowMs;
29
+ constructor(failureThreshold = DEFAULT_FAILURE_THRESHOLD, windowMs = DEFAULT_WINDOW_MS) {
30
+ this.breakers = new Map();
31
+ this.failureThreshold = failureThreshold;
32
+ this.windowMs = windowMs;
33
+ }
34
+ /**
35
+ * Check whether a tool is allowed to execute.
36
+ * Transitions open → half-open when the cooldown has expired.
37
+ */
38
+ canExecute(toolName) {
39
+ const breaker = this.getOrCreate(toolName);
40
+ const now = Date.now();
41
+ if (breaker.state === 'closed') {
42
+ return { allowed: true };
43
+ }
44
+ if (breaker.state === 'open') {
45
+ const elapsed = now - breaker.lastFailureAt;
46
+ if (elapsed >= breaker.cooldownMs) {
47
+ // Transition to half-open to allow a probe call
48
+ breaker.state = 'half-open';
49
+ logger.debug('[CircuitBreaker] Transitioning to half-open', {
50
+ tool: toolName,
51
+ elapsed,
52
+ cooldownMs: breaker.cooldownMs,
53
+ });
54
+ return { allowed: true };
55
+ }
56
+ const remainingMs = breaker.cooldownMs - elapsed;
57
+ return {
58
+ allowed: false,
59
+ reason: `Circuit breaker open for ${toolName}: too many recent failures. Retry in ${Math.ceil(remainingMs / 1000)}s.`,
60
+ };
61
+ }
62
+ // half-open: allow one probe through
63
+ return { allowed: true };
64
+ }
65
+ /**
66
+ * Record a successful tool execution.
67
+ * In half-open state this closes the circuit.
68
+ */
69
+ recordSuccess(toolName) {
70
+ const breaker = this.getOrCreate(toolName);
71
+ if (breaker.state === 'half-open') {
72
+ logger.info('[CircuitBreaker] Probe succeeded, closing circuit', { tool: toolName });
73
+ breaker.state = 'closed';
74
+ breaker.failureCount = 0;
75
+ breaker.cooldownMs = INITIAL_COOLDOWN_MS;
76
+ }
77
+ else if (breaker.state === 'closed') {
78
+ // Reset failure count on success within the window
79
+ breaker.failureCount = 0;
80
+ }
81
+ }
82
+ /**
83
+ * Record a failed tool execution.
84
+ * Counts failures within the time window and trips the breaker when the
85
+ * threshold is reached. In half-open state it immediately re-opens with
86
+ * a doubled cooldown.
87
+ */
88
+ recordFailure(toolName) {
89
+ const breaker = this.getOrCreate(toolName);
90
+ const now = Date.now();
91
+ if (breaker.state === 'half-open') {
92
+ // Probe failed — re-open with doubled cooldown
93
+ breaker.cooldownMs = Math.min(breaker.cooldownMs * 2, MAX_COOLDOWN_MS);
94
+ breaker.state = 'open';
95
+ breaker.lastFailureAt = now;
96
+ logger.warn('[CircuitBreaker] Probe failed, re-opening circuit', {
97
+ tool: toolName,
98
+ cooldownMs: breaker.cooldownMs,
99
+ });
100
+ return;
101
+ }
102
+ // In closed state, check if previous failures are still within the window
103
+ const withinWindow = breaker.lastFailureAt > 0 && now - breaker.lastFailureAt <= this.windowMs;
104
+ if (withinWindow) {
105
+ breaker.failureCount++;
106
+ }
107
+ else {
108
+ // Failures outside the window don't count; start a fresh count
109
+ breaker.failureCount = 1;
110
+ }
111
+ breaker.lastFailureAt = now;
112
+ if (breaker.failureCount >= this.failureThreshold) {
113
+ breaker.state = 'open';
114
+ logger.warn('[CircuitBreaker] Threshold reached, opening circuit', {
115
+ tool: toolName,
116
+ failureCount: breaker.failureCount,
117
+ cooldownMs: breaker.cooldownMs,
118
+ });
119
+ }
120
+ }
121
+ /**
122
+ * Reset the circuit breaker for a specific tool, or all tools if no name is given.
123
+ */
124
+ reset(toolName) {
125
+ if (toolName !== undefined) {
126
+ this.breakers.set(toolName, defaultBreakerState());
127
+ }
128
+ else {
129
+ this.breakers.clear();
130
+ }
131
+ }
132
+ /**
133
+ * Return a deep snapshot of all breaker states.
134
+ * Mutations to the live breakers do not affect the returned map.
135
+ */
136
+ getStatus() {
137
+ const snapshot = new Map();
138
+ for (const [key, value] of this.breakers) {
139
+ snapshot.set(key, { ...value });
140
+ }
141
+ return snapshot;
142
+ }
143
+ // Private helpers
144
+ getOrCreate(toolName) {
145
+ let breaker = this.breakers.get(toolName);
146
+ if (!breaker) {
147
+ breaker = defaultBreakerState();
148
+ this.breakers.set(toolName, breaker);
149
+ }
150
+ return breaker;
151
+ }
152
+ }
153
+ //# sourceMappingURL=circuit-breaker.js.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * OS-level confinement for shell commands run by `profclaw run`.
3
+ *
4
+ * Wraps a command so the kernel, not pattern matching, enforces the limits:
5
+ * - writes only inside the worktree, a per-run temp dir, the git metadata the
6
+ * worktree needs and a few device nodes (plus opt-in extras)
7
+ * - reads are broad (node, pnpm, git and tsc need many system paths) but
8
+ * credential directories such as ~/.ssh and ~/.aws are denied
9
+ * - network is denied unless PROFCLAW_RUN_ALLOW_NETWORK is set
10
+ *
11
+ * Backends: macOS Seatbelt (`/usr/bin/sandbox-exec`) and Linux `bwrap`.
12
+ * Modes (PROFCLAW_RUN_SANDBOX): `auto` uses a sandbox when available and
13
+ * otherwise falls back to pattern screening with a one-time warning, `on`
14
+ * fails closed, `off` disables.
15
+ *
16
+ * Known limits: a writable git dir can be used to plant state outside the
17
+ * worktree (hooks and config are denied, other refs are not), and a sandboxed
18
+ * process can still read anything not in the deny list.
19
+ */
20
+ export type SandboxMode = 'auto' | 'on' | 'off';
21
+ export type SandboxBackend = 'seatbelt' | 'bwrap' | 'none';
22
+ export interface SandboxAvailability {
23
+ backend: SandboxBackend;
24
+ /** Absolute path of the sandbox binary */
25
+ binary: string;
26
+ }
27
+ export interface SandboxOptions {
28
+ workdir: string;
29
+ /** Per-run scratch directory that is writable */
30
+ tempDir: string;
31
+ /** Home directory whose credential dirs are denied (default: os.homedir()) */
32
+ home?: string;
33
+ allowNetwork?: boolean;
34
+ /** Extra writable paths (e.g. the git dir of a linked worktree) */
35
+ extraWritable?: string[];
36
+ /** Paths inside an extraWritable tree that stay read-only (e.g. hooks) */
37
+ extraReadOnly?: string[];
38
+ /** Extra paths whose reads are denied */
39
+ extraDenyRead?: string[];
40
+ }
41
+ export interface SandboxPlan {
42
+ backend: SandboxBackend;
43
+ file: string;
44
+ args: string[];
45
+ /** Environment overrides the caller must merge in (TMPDIR) */
46
+ env: Record<string, string>;
47
+ }
48
+ export type DetectFn = () => SandboxAvailability;
49
+ /** Home-relative locations holding credentials. Reads are denied. */
50
+ export declare const SENSITIVE_HOME_PATHS: string[];
51
+ /** Absolute credential locations outside of HOME. */
52
+ export declare const SENSITIVE_ABSOLUTE_PATHS: string[];
53
+ export declare function resolveSandboxMode(env?: NodeJS.ProcessEnv): SandboxMode;
54
+ export declare function resolveAllowNetwork(env?: NodeJS.ProcessEnv): boolean;
55
+ /** Extra writable paths from PROFCLAW_RUN_SANDBOX_WRITABLE (path-delimiter separated). */
56
+ export declare function resolveExtraWritable(env?: NodeJS.ProcessEnv): string[];
57
+ /** Resolve symlinks (Seatbelt matches real paths, e.g. /tmp is /private/tmp). */
58
+ export declare function canonicalPath(path: string): string;
59
+ /**
60
+ * Quote a path as an SBPL string literal. Backslash and double quote are
61
+ * escaped; control characters cannot be represented safely and are rejected,
62
+ * so a crafted path can never terminate the literal and inject rules.
63
+ */
64
+ export declare function escapeSeatbeltString(value: string): string;
65
+ /**
66
+ * Build the Seatbelt profile. SBPL applies the last matching rule, so the
67
+ * broad allow comes first and the specific denies and re-allows follow.
68
+ */
69
+ export declare function buildSeatbeltProfile(options: SandboxOptions): string;
70
+ export declare function buildBwrapArgs(options: SandboxOptions): string[];
71
+ /**
72
+ * Detect a working sandbox. A probe run confirms it functions, since
73
+ * sandbox-exec fails when already inside a sandbox and bwrap can be blocked
74
+ * by unprivileged user namespace policy.
75
+ */
76
+ export declare function detectSandbox(): SandboxAvailability;
77
+ /** Test hook: allow the one-time warning to fire again. */
78
+ export declare function resetSandboxWarning(): void;
79
+ /** One-line description for CLI output. */
80
+ export declare function describeSandbox(mode: SandboxMode, detect?: DetectFn, allowNetwork?: boolean): string;
81
+ export interface PlanOptions {
82
+ mode?: SandboxMode;
83
+ detect?: DetectFn;
84
+ /** Receives the one-time fallback warning (default: stderr) */
85
+ warn?: (message: string) => void;
86
+ shell?: string;
87
+ }
88
+ /**
89
+ * Turn a shell command into the process to spawn. Throws in `on` mode when no
90
+ * sandbox is available. In `auto` mode falls back to the plain shell and warns once.
91
+ */
92
+ export declare function planSandboxedCommand(command: string, options: SandboxOptions, plan?: PlanOptions): SandboxPlan;
93
+ //# sourceMappingURL=command-sandbox.d.ts.map
@@ -0,0 +1,235 @@
1
+ /**
2
+ * OS-level confinement for shell commands run by `profclaw run`.
3
+ *
4
+ * Wraps a command so the kernel, not pattern matching, enforces the limits:
5
+ * - writes only inside the worktree, a per-run temp dir, the git metadata the
6
+ * worktree needs and a few device nodes (plus opt-in extras)
7
+ * - reads are broad (node, pnpm, git and tsc need many system paths) but
8
+ * credential directories such as ~/.ssh and ~/.aws are denied
9
+ * - network is denied unless PROFCLAW_RUN_ALLOW_NETWORK is set
10
+ *
11
+ * Backends: macOS Seatbelt (`/usr/bin/sandbox-exec`) and Linux `bwrap`.
12
+ * Modes (PROFCLAW_RUN_SANDBOX): `auto` uses a sandbox when available and
13
+ * otherwise falls back to pattern screening with a one-time warning, `on`
14
+ * fails closed, `off` disables.
15
+ *
16
+ * Known limits: a writable git dir can be used to plant state outside the
17
+ * worktree (hooks and config are denied, other refs are not), and a sandboxed
18
+ * process can still read anything not in the deny list.
19
+ */
20
+ import { spawnSync } from 'node:child_process';
21
+ import { existsSync, realpathSync, statSync } from 'node:fs';
22
+ import { homedir } from 'node:os';
23
+ import { delimiter, join, resolve } from 'node:path';
24
+ /** Home-relative locations holding credentials. Reads are denied. */
25
+ export const SENSITIVE_HOME_PATHS = [
26
+ '.ssh',
27
+ '.aws',
28
+ '.config/gh',
29
+ '.gnupg',
30
+ '.netrc',
31
+ '.git-credentials',
32
+ '.kube',
33
+ '.azure',
34
+ '.config/gcloud',
35
+ '.docker/config.json',
36
+ 'Library/Keychains',
37
+ ];
38
+ /** Absolute credential locations outside of HOME. */
39
+ export const SENSITIVE_ABSOLUTE_PATHS = ['/Library/Keychains', '/private/var/db/dslocal'];
40
+ // Environment parsing
41
+ export function resolveSandboxMode(env = process.env) {
42
+ const raw = (env.PROFCLAW_RUN_SANDBOX ?? 'auto').trim().toLowerCase();
43
+ if (raw === 'on' || raw === 'off' || raw === 'auto')
44
+ return raw;
45
+ if (raw === '1' || raw === 'true')
46
+ return 'on';
47
+ if (raw === '0' || raw === 'false')
48
+ return 'off';
49
+ throw new Error(`Invalid PROFCLAW_RUN_SANDBOX "${raw}" (expected auto, on or off)`);
50
+ }
51
+ export function resolveAllowNetwork(env = process.env) {
52
+ const raw = (env.PROFCLAW_RUN_ALLOW_NETWORK ?? '').trim().toLowerCase();
53
+ return raw === '1' || raw === 'true' || raw === 'yes';
54
+ }
55
+ /** Extra writable paths from PROFCLAW_RUN_SANDBOX_WRITABLE (path-delimiter separated). */
56
+ export function resolveExtraWritable(env = process.env) {
57
+ return (env.PROFCLAW_RUN_SANDBOX_WRITABLE ?? '')
58
+ .split(delimiter)
59
+ .map((p) => p.trim())
60
+ .filter((p) => p.length > 0);
61
+ }
62
+ // Path handling
63
+ /** Resolve symlinks (Seatbelt matches real paths, e.g. /tmp is /private/tmp). */
64
+ export function canonicalPath(path) {
65
+ const absolute = resolve(path);
66
+ try {
67
+ return realpathSync(absolute);
68
+ }
69
+ catch {
70
+ return absolute;
71
+ }
72
+ }
73
+ /**
74
+ * Quote a path as an SBPL string literal. Backslash and double quote are
75
+ * escaped; control characters cannot be represented safely and are rejected,
76
+ * so a crafted path can never terminate the literal and inject rules.
77
+ */
78
+ export function escapeSeatbeltString(value) {
79
+ if (/[\u0000-\u001f\u007f]/.test(value))
80
+ throw new Error('Path contains control characters and cannot be sandboxed');
81
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
82
+ }
83
+ function subpath(path) {
84
+ return `(subpath ${escapeSeatbeltString(path)})`;
85
+ }
86
+ function deniedReadPaths(options) {
87
+ const home = canonicalPath(options.home ?? homedir());
88
+ return [
89
+ ...SENSITIVE_HOME_PATHS.map((p) => join(home, p)),
90
+ ...SENSITIVE_ABSOLUTE_PATHS,
91
+ ...(options.extraDenyRead ?? []).map(canonicalPath),
92
+ ];
93
+ }
94
+ // Seatbelt
95
+ /**
96
+ * Build the Seatbelt profile. SBPL applies the last matching rule, so the
97
+ * broad allow comes first and the specific denies and re-allows follow.
98
+ */
99
+ export function buildSeatbeltProfile(options) {
100
+ const writable = [options.workdir, options.tempDir, ...(options.extraWritable ?? [])].map(canonicalPath);
101
+ const readOnly = (options.extraReadOnly ?? []).map(canonicalPath);
102
+ const lines = [
103
+ '(version 1)',
104
+ '(allow default)',
105
+ '(deny file-write*)',
106
+ `(allow file-write* ${writable.map(subpath).join(' ')})`,
107
+ '(allow file-write* (literal "/dev/null") (literal "/dev/zero") (literal "/dev/tty") (literal "/dev/dtracehelper"))',
108
+ ];
109
+ if (readOnly.length > 0)
110
+ lines.push(`(deny file-write* ${readOnly.map(subpath).join(' ')})`);
111
+ lines.push(`(deny file-read* ${deniedReadPaths(options).map(subpath).join(' ')})`);
112
+ if (options.allowNetwork !== true)
113
+ lines.push('(deny network*)');
114
+ return `${lines.join('\n')}\n`;
115
+ }
116
+ // bubblewrap
117
+ function isDirectory(path) {
118
+ try {
119
+ return statSync(path).isDirectory();
120
+ }
121
+ catch {
122
+ return false;
123
+ }
124
+ }
125
+ export function buildBwrapArgs(options) {
126
+ const workdir = canonicalPath(options.workdir);
127
+ const tempDir = canonicalPath(options.tempDir);
128
+ const args = ['--ro-bind', '/', '/', '--dev', '/dev', '--proc', '/proc', '--die-with-parent', '--new-session'];
129
+ for (const path of [workdir, tempDir, ...(options.extraWritable ?? []).map(canonicalPath)]) {
130
+ args.push('--bind', path, path);
131
+ }
132
+ for (const path of (options.extraReadOnly ?? []).map(canonicalPath)) {
133
+ if (existsSync(path))
134
+ args.push('--ro-bind', path, path);
135
+ }
136
+ for (const path of deniedReadPaths(options)) {
137
+ if (!existsSync(path))
138
+ continue;
139
+ // Directories are hidden behind an empty tmpfs, files behind /dev/null.
140
+ args.push(...(isDirectory(path) ? ['--tmpfs', path] : ['--ro-bind', '/dev/null', path]));
141
+ }
142
+ if (options.allowNetwork !== true)
143
+ args.push('--unshare-net');
144
+ args.push('--chdir', workdir);
145
+ return args;
146
+ }
147
+ // Detection
148
+ let cachedAvailability;
149
+ function findOnPath(name) {
150
+ for (const dir of (process.env.PATH ?? '').split(delimiter)) {
151
+ if (dir.length > 0 && existsSync(join(dir, name)))
152
+ return join(dir, name);
153
+ }
154
+ return undefined;
155
+ }
156
+ /**
157
+ * Detect a working sandbox. A probe run confirms it functions, since
158
+ * sandbox-exec fails when already inside a sandbox and bwrap can be blocked
159
+ * by unprivileged user namespace policy.
160
+ */
161
+ export function detectSandbox() {
162
+ if (cachedAvailability)
163
+ return cachedAvailability;
164
+ let found = { backend: 'none', binary: '' };
165
+ if (process.platform === 'darwin' && existsSync('/usr/bin/sandbox-exec')) {
166
+ const probe = spawnSync('/usr/bin/sandbox-exec', ['-p', '(version 1)(allow default)', '/usr/bin/true']);
167
+ if (probe.status === 0)
168
+ found = { backend: 'seatbelt', binary: '/usr/bin/sandbox-exec' };
169
+ }
170
+ else if (process.platform === 'linux') {
171
+ const bwrap = findOnPath('bwrap');
172
+ if (bwrap) {
173
+ const probe = spawnSync(bwrap, ['--ro-bind', '/', '/', '--unshare-net', 'true']);
174
+ if (probe.status === 0)
175
+ found = { backend: 'bwrap', binary: bwrap };
176
+ }
177
+ }
178
+ cachedAvailability = found;
179
+ return found;
180
+ }
181
+ // Planning
182
+ let warned = false;
183
+ /** Test hook: allow the one-time warning to fire again. */
184
+ export function resetSandboxWarning() {
185
+ warned = false;
186
+ }
187
+ /** One-line description for CLI output. */
188
+ export function describeSandbox(mode, detect = detectSandbox, allowNetwork = resolveAllowNetwork()) {
189
+ if (mode === 'off')
190
+ return 'Sandbox: off (pattern screening only)';
191
+ const { backend } = detect();
192
+ if (backend === 'none') {
193
+ return mode === 'on'
194
+ ? 'Sandbox: required but unavailable, bash commands will fail'
195
+ : 'Sandbox: unavailable, bash commands are pattern screened only';
196
+ }
197
+ return `Sandbox: ${mode} (${backend}, network ${allowNetwork ? 'allowed' : 'denied'})`;
198
+ }
199
+ /**
200
+ * Turn a shell command into the process to spawn. Throws in `on` mode when no
201
+ * sandbox is available. In `auto` mode falls back to the plain shell and warns once.
202
+ */
203
+ export function planSandboxedCommand(command, options, plan = {}) {
204
+ const mode = plan.mode ?? resolveSandboxMode();
205
+ const shell = plan.shell ?? '/bin/sh';
206
+ const plain = { backend: 'none', file: shell, args: ['-c', command], env: {} };
207
+ if (mode === 'off')
208
+ return plain;
209
+ const available = (plan.detect ?? detectSandbox)();
210
+ if (available.backend === 'none') {
211
+ if (mode === 'on') {
212
+ throw new Error('PROFCLAW_RUN_SANDBOX=on but no sandbox is available (need sandbox-exec on macOS or bwrap on Linux)');
213
+ }
214
+ if (!warned) {
215
+ warned = true;
216
+ const message = '[profclaw] No OS sandbox available, shell commands are only pattern screened (set PROFCLAW_RUN_SANDBOX=on to require one).\n';
217
+ if (plan.warn)
218
+ plan.warn(message);
219
+ else
220
+ process.stderr.write(message);
221
+ }
222
+ return plain;
223
+ }
224
+ const env = { TMPDIR: canonicalPath(options.tempDir) };
225
+ if (available.backend === 'seatbelt') {
226
+ return {
227
+ backend: 'seatbelt',
228
+ file: available.binary,
229
+ args: ['-p', buildSeatbeltProfile(options), shell, '-c', command],
230
+ env,
231
+ };
232
+ }
233
+ return { backend: 'bwrap', file: available.binary, args: [...buildBwrapArgs(options), shell, '-c', command], env };
234
+ }
235
+ //# sourceMappingURL=command-sandbox.js.map