patchwork-os 0.2.0-alpha.9 → 0.2.0-beta.1

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 (618) hide show
  1. package/README.bridge.md +6 -0
  2. package/README.md +318 -35
  3. package/deploy/bootstrap-new-vps.sh +12 -12
  4. package/deploy/bootstrap-vps.sh +187 -0
  5. package/deploy/deploy-dashboard.sh +174 -0
  6. package/deploy/deploy-landing.sh +136 -0
  7. package/dist/activationMetrics.d.ts +67 -0
  8. package/dist/activationMetrics.js +255 -0
  9. package/dist/activationMetrics.js.map +1 -0
  10. package/dist/activityLog.d.ts +49 -0
  11. package/dist/activityLog.js +78 -0
  12. package/dist/activityLog.js.map +1 -1
  13. package/dist/analyticsAggregator.d.ts +5 -1
  14. package/dist/analyticsAggregator.js +15 -4
  15. package/dist/analyticsAggregator.js.map +1 -1
  16. package/dist/analyticsPrefs.d.ts +11 -0
  17. package/dist/analyticsPrefs.js +33 -0
  18. package/dist/analyticsPrefs.js.map +1 -1
  19. package/dist/approvalHttp.d.ts +49 -2
  20. package/dist/approvalHttp.js +217 -21
  21. package/dist/approvalHttp.js.map +1 -1
  22. package/dist/approvalInsights.d.ts +49 -0
  23. package/dist/approvalInsights.js +97 -0
  24. package/dist/approvalInsights.js.map +1 -0
  25. package/dist/approvalQueue.d.ts +27 -1
  26. package/dist/approvalQueue.js +123 -3
  27. package/dist/approvalQueue.js.map +1 -1
  28. package/dist/approvalSignals.d.ts +124 -0
  29. package/dist/approvalSignals.js +512 -0
  30. package/dist/approvalSignals.js.map +1 -0
  31. package/dist/automation.d.ts +57 -0
  32. package/dist/automation.js +156 -59
  33. package/dist/automation.js.map +1 -1
  34. package/dist/automationSuggestions.d.ts +79 -0
  35. package/dist/automationSuggestions.js +150 -0
  36. package/dist/automationSuggestions.js.map +1 -0
  37. package/dist/bridge.d.ts +3 -0
  38. package/dist/bridge.js +194 -153
  39. package/dist/bridge.js.map +1 -1
  40. package/dist/bridgeToken.js +57 -19
  41. package/dist/bridgeToken.js.map +1 -1
  42. package/dist/ccPermissions.d.ts +15 -0
  43. package/dist/ccPermissions.js +21 -4
  44. package/dist/ccPermissions.js.map +1 -1
  45. package/dist/claudeDriver.d.ts +0 -16
  46. package/dist/claudeDriver.js +93 -36
  47. package/dist/claudeDriver.js.map +1 -1
  48. package/dist/claudeMdPatch.d.ts +9 -3
  49. package/dist/claudeMdPatch.js +79 -13
  50. package/dist/claudeMdPatch.js.map +1 -1
  51. package/dist/claudeOrchestrator.d.ts +13 -1
  52. package/dist/claudeOrchestrator.js +16 -8
  53. package/dist/claudeOrchestrator.js.map +1 -1
  54. package/dist/commands/dashboard.js +1 -1
  55. package/dist/commands/dashboard.js.map +1 -1
  56. package/dist/commands/launchd.d.ts +2 -0
  57. package/dist/commands/launchd.js +94 -0
  58. package/dist/commands/launchd.js.map +1 -0
  59. package/dist/commands/marketplace.d.ts +15 -10
  60. package/dist/commands/marketplace.js +27 -115
  61. package/dist/commands/marketplace.js.map +1 -1
  62. package/dist/commands/patchworkInit.d.ts +8 -0
  63. package/dist/commands/patchworkInit.js +77 -11
  64. package/dist/commands/patchworkInit.js.map +1 -1
  65. package/dist/commands/recipe.d.ts +289 -0
  66. package/dist/commands/recipe.js +1359 -0
  67. package/dist/commands/recipe.js.map +1 -0
  68. package/dist/commands/recipeInstall.d.ts +150 -0
  69. package/dist/commands/recipeInstall.js +647 -0
  70. package/dist/commands/recipeInstall.js.map +1 -0
  71. package/dist/commands/tracesExport.d.ts +83 -0
  72. package/dist/commands/tracesExport.js +269 -0
  73. package/dist/commands/tracesExport.js.map +1 -0
  74. package/dist/commands/tracesImport.d.ts +56 -0
  75. package/dist/commands/tracesImport.js +161 -0
  76. package/dist/commands/tracesImport.js.map +1 -0
  77. package/dist/commitIssueLinkLog.d.ts +8 -0
  78. package/dist/commitIssueLinkLog.js +53 -1
  79. package/dist/commitIssueLinkLog.js.map +1 -1
  80. package/dist/config.d.ts +23 -2
  81. package/dist/config.js +119 -9
  82. package/dist/config.js.map +1 -1
  83. package/dist/connectorRoutes.d.ts +43 -0
  84. package/dist/connectorRoutes.js +1300 -0
  85. package/dist/connectorRoutes.js.map +1 -0
  86. package/dist/connectors/asana.d.ts +198 -0
  87. package/dist/connectors/asana.js +679 -0
  88. package/dist/connectors/asana.js.map +1 -0
  89. package/dist/connectors/baseConnector.d.ts +153 -0
  90. package/dist/connectors/baseConnector.js +336 -0
  91. package/dist/connectors/baseConnector.js.map +1 -0
  92. package/dist/connectors/confluence.d.ts +111 -0
  93. package/dist/connectors/confluence.js +406 -0
  94. package/dist/connectors/confluence.js.map +1 -0
  95. package/dist/connectors/datadog.d.ts +116 -0
  96. package/dist/connectors/datadog.js +385 -0
  97. package/dist/connectors/datadog.js.map +1 -0
  98. package/dist/connectors/discord.d.ts +150 -0
  99. package/dist/connectors/discord.js +543 -0
  100. package/dist/connectors/discord.js.map +1 -0
  101. package/dist/connectors/fixtureLibrary.d.ts +21 -0
  102. package/dist/connectors/fixtureLibrary.js +70 -0
  103. package/dist/connectors/fixtureLibrary.js.map +1 -0
  104. package/dist/connectors/fixtureRecorder.d.ts +1 -0
  105. package/dist/connectors/fixtureRecorder.js +35 -0
  106. package/dist/connectors/fixtureRecorder.js.map +1 -0
  107. package/dist/connectors/github.js +17 -18
  108. package/dist/connectors/github.js.map +1 -1
  109. package/dist/connectors/gitlab.d.ts +180 -0
  110. package/dist/connectors/gitlab.js +582 -0
  111. package/dist/connectors/gitlab.js.map +1 -0
  112. package/dist/connectors/gmail.d.ts +4 -1
  113. package/dist/connectors/gmail.js +149 -27
  114. package/dist/connectors/gmail.js.map +1 -1
  115. package/dist/connectors/googleCalendar.d.ts +4 -1
  116. package/dist/connectors/googleCalendar.js +88 -25
  117. package/dist/connectors/googleCalendar.js.map +1 -1
  118. package/dist/connectors/googleDrive.d.ts +34 -0
  119. package/dist/connectors/googleDrive.js +321 -0
  120. package/dist/connectors/googleDrive.js.map +1 -0
  121. package/dist/connectors/htmlEscape.d.ts +5 -0
  122. package/dist/connectors/htmlEscape.js +13 -0
  123. package/dist/connectors/htmlEscape.js.map +1 -0
  124. package/dist/connectors/hubspot.d.ts +112 -0
  125. package/dist/connectors/hubspot.js +408 -0
  126. package/dist/connectors/hubspot.js.map +1 -0
  127. package/dist/connectors/intercom.d.ts +102 -0
  128. package/dist/connectors/intercom.js +402 -0
  129. package/dist/connectors/intercom.js.map +1 -0
  130. package/dist/connectors/jira.d.ts +98 -0
  131. package/dist/connectors/jira.js +396 -0
  132. package/dist/connectors/jira.js.map +1 -0
  133. package/dist/connectors/linear.js +30 -19
  134. package/dist/connectors/linear.js.map +1 -1
  135. package/dist/connectors/mcpOAuth.d.ts +3 -0
  136. package/dist/connectors/mcpOAuth.js +64 -10
  137. package/dist/connectors/mcpOAuth.js.map +1 -1
  138. package/dist/connectors/mockConnector.d.ts +28 -0
  139. package/dist/connectors/mockConnector.js +81 -0
  140. package/dist/connectors/mockConnector.js.map +1 -0
  141. package/dist/connectors/notion.d.ts +143 -0
  142. package/dist/connectors/notion.js +424 -0
  143. package/dist/connectors/notion.js.map +1 -0
  144. package/dist/connectors/oauthStateStore.d.ts +31 -0
  145. package/dist/connectors/oauthStateStore.js +52 -0
  146. package/dist/connectors/oauthStateStore.js.map +1 -0
  147. package/dist/connectors/pagerduty.d.ts +160 -0
  148. package/dist/connectors/pagerduty.js +464 -0
  149. package/dist/connectors/pagerduty.js.map +1 -0
  150. package/dist/connectors/sentry.js +5 -13
  151. package/dist/connectors/sentry.js.map +1 -1
  152. package/dist/connectors/slack.d.ts +16 -1
  153. package/dist/connectors/slack.js +155 -32
  154. package/dist/connectors/slack.js.map +1 -1
  155. package/dist/connectors/stripe.d.ts +116 -0
  156. package/dist/connectors/stripe.js +379 -0
  157. package/dist/connectors/stripe.js.map +1 -0
  158. package/dist/connectors/tokenStorage.d.ts +35 -0
  159. package/dist/connectors/tokenStorage.js +484 -0
  160. package/dist/connectors/tokenStorage.js.map +1 -0
  161. package/dist/connectors/zendesk.d.ts +104 -0
  162. package/dist/connectors/zendesk.js +442 -0
  163. package/dist/connectors/zendesk.js.map +1 -0
  164. package/dist/cors.d.ts +10 -0
  165. package/dist/cors.js +29 -0
  166. package/dist/cors.js.map +1 -0
  167. package/dist/decisionReplay.d.ts +72 -0
  168. package/dist/decisionReplay.js +92 -0
  169. package/dist/decisionReplay.js.map +1 -0
  170. package/dist/decisionTraceLog.d.ts +6 -0
  171. package/dist/decisionTraceLog.js +54 -2
  172. package/dist/decisionTraceLog.js.map +1 -1
  173. package/dist/drivers/claude/subprocess.d.ts +12 -2
  174. package/dist/drivers/claude/subprocess.js +79 -6
  175. package/dist/drivers/claude/subprocess.js.map +1 -1
  176. package/dist/drivers/gemini/api.d.ts +18 -0
  177. package/dist/drivers/gemini/api.js +29 -0
  178. package/dist/drivers/gemini/api.js.map +1 -0
  179. package/dist/drivers/gemini/index.d.ts +5 -1
  180. package/dist/drivers/gemini/index.js +39 -5
  181. package/dist/drivers/gemini/index.js.map +1 -1
  182. package/dist/drivers/index.d.ts +8 -1
  183. package/dist/drivers/index.js +10 -2
  184. package/dist/drivers/index.js.map +1 -1
  185. package/dist/drivers/local/index.d.ts +26 -0
  186. package/dist/drivers/local/index.js +41 -0
  187. package/dist/drivers/local/index.js.map +1 -0
  188. package/dist/featureFlags.d.ts +79 -0
  189. package/dist/featureFlags.js +208 -0
  190. package/dist/featureFlags.js.map +1 -0
  191. package/dist/fp/automationInterpreter.js +26 -21
  192. package/dist/fp/automationInterpreter.js.map +1 -1
  193. package/dist/fp/automationProgram.d.ts +1 -1
  194. package/dist/fp/automationProgram.js.map +1 -1
  195. package/dist/fp/automationState.js +4 -1
  196. package/dist/fp/automationState.js.map +1 -1
  197. package/dist/fp/policyParser.js +21 -1
  198. package/dist/fp/policyParser.js.map +1 -1
  199. package/dist/httpErrorResponse.d.ts +36 -0
  200. package/dist/httpErrorResponse.js +46 -0
  201. package/dist/httpErrorResponse.js.map +1 -0
  202. package/dist/inboxRoutes.d.ts +22 -0
  203. package/dist/inboxRoutes.js +193 -0
  204. package/dist/inboxRoutes.js.map +1 -0
  205. package/dist/index.d.ts +1 -1
  206. package/dist/index.js +1403 -203
  207. package/dist/index.js.map +1 -1
  208. package/dist/installGuard.d.ts +25 -0
  209. package/dist/installGuard.js +48 -0
  210. package/dist/installGuard.js.map +1 -0
  211. package/dist/mcpRoutes.d.ts +37 -0
  212. package/dist/mcpRoutes.js +76 -0
  213. package/dist/mcpRoutes.js.map +1 -0
  214. package/dist/oauth.d.ts +20 -1
  215. package/dist/oauth.js +214 -39
  216. package/dist/oauth.js.map +1 -1
  217. package/dist/oauthRoutes.d.ts +32 -0
  218. package/dist/oauthRoutes.js +119 -0
  219. package/dist/oauthRoutes.js.map +1 -0
  220. package/dist/orchestrator/orchestratorBridge.js +2 -2
  221. package/dist/orchestrator/orchestratorBridge.js.map +1 -1
  222. package/dist/patchworkConfig.d.ts +29 -0
  223. package/dist/patchworkConfig.js +100 -5
  224. package/dist/patchworkConfig.js.map +1 -1
  225. package/dist/pluginLoader.d.ts +28 -0
  226. package/dist/pluginLoader.js +77 -11
  227. package/dist/pluginLoader.js.map +1 -1
  228. package/dist/pluginWatcher.js +8 -3
  229. package/dist/pluginWatcher.js.map +1 -1
  230. package/dist/preToolUseHook.d.ts +12 -0
  231. package/dist/preToolUseHook.js +30 -1
  232. package/dist/preToolUseHook.js.map +1 -1
  233. package/dist/prompts.js +4 -0
  234. package/dist/prompts.js.map +1 -1
  235. package/dist/recipeOrchestration.d.ts +121 -0
  236. package/dist/recipeOrchestration.js +965 -0
  237. package/dist/recipeOrchestration.js.map +1 -0
  238. package/dist/recipeRoutes.d.ts +185 -0
  239. package/dist/recipeRoutes.js +1369 -0
  240. package/dist/recipeRoutes.js.map +1 -0
  241. package/dist/recipes/RecipeOrchestrator.d.ts +40 -0
  242. package/dist/recipes/RecipeOrchestrator.js +51 -0
  243. package/dist/recipes/RecipeOrchestrator.js.map +1 -0
  244. package/dist/recipes/agentExecutor.d.ts +38 -0
  245. package/dist/recipes/agentExecutor.js +50 -0
  246. package/dist/recipes/agentExecutor.js.map +1 -0
  247. package/dist/recipes/chainedRunner.d.ts +191 -0
  248. package/dist/recipes/chainedRunner.js +759 -0
  249. package/dist/recipes/chainedRunner.js.map +1 -0
  250. package/dist/recipes/compiler.js +3 -3
  251. package/dist/recipes/compiler.js.map +1 -1
  252. package/dist/recipes/dependencyGraph.d.ts +39 -0
  253. package/dist/recipes/dependencyGraph.js +199 -0
  254. package/dist/recipes/dependencyGraph.js.map +1 -0
  255. package/dist/recipes/disabledMarkers.d.ts +48 -0
  256. package/dist/recipes/disabledMarkers.js +52 -0
  257. package/dist/recipes/disabledMarkers.js.map +1 -0
  258. package/dist/recipes/installer.js +3 -3
  259. package/dist/recipes/installer.js.map +1 -1
  260. package/dist/recipes/legacyRecipeCompat.d.ts +10 -0
  261. package/dist/recipes/legacyRecipeCompat.js +131 -0
  262. package/dist/recipes/legacyRecipeCompat.js.map +1 -0
  263. package/dist/recipes/manifest.d.ts +47 -0
  264. package/dist/recipes/manifest.js +156 -0
  265. package/dist/recipes/manifest.js.map +1 -0
  266. package/dist/recipes/migrationWarnings.d.ts +12 -0
  267. package/dist/recipes/migrationWarnings.js +44 -0
  268. package/dist/recipes/migrationWarnings.js.map +1 -0
  269. package/dist/recipes/migrations/index.d.ts +24 -0
  270. package/dist/recipes/migrations/index.js +55 -0
  271. package/dist/recipes/migrations/index.js.map +1 -0
  272. package/dist/recipes/migrations/types.d.ts +28 -0
  273. package/dist/recipes/migrations/types.js +2 -0
  274. package/dist/recipes/migrations/types.js.map +1 -0
  275. package/dist/recipes/migrations/v1.d.ts +11 -0
  276. package/dist/recipes/migrations/v1.js +18 -0
  277. package/dist/recipes/migrations/v1.js.map +1 -0
  278. package/dist/recipes/names.d.ts +40 -0
  279. package/dist/recipes/names.js +66 -0
  280. package/dist/recipes/names.js.map +1 -0
  281. package/dist/recipes/nestedRecipeStep.d.ts +58 -0
  282. package/dist/recipes/nestedRecipeStep.js +95 -0
  283. package/dist/recipes/nestedRecipeStep.js.map +1 -0
  284. package/dist/recipes/outputRegistry.d.ts +28 -0
  285. package/dist/recipes/outputRegistry.js +52 -0
  286. package/dist/recipes/outputRegistry.js.map +1 -0
  287. package/dist/recipes/parser.js +4 -1
  288. package/dist/recipes/parser.js.map +1 -1
  289. package/dist/recipes/replayRun.d.ts +62 -0
  290. package/dist/recipes/replayRun.js +97 -0
  291. package/dist/recipes/replayRun.js.map +1 -0
  292. package/dist/recipes/resolveRecipePath.d.ts +69 -0
  293. package/dist/recipes/resolveRecipePath.js +202 -0
  294. package/dist/recipes/resolveRecipePath.js.map +1 -0
  295. package/dist/recipes/scheduler.d.ts +23 -7
  296. package/dist/recipes/scheduler.js +225 -45
  297. package/dist/recipes/scheduler.js.map +1 -1
  298. package/dist/recipes/schema.d.ts +17 -2
  299. package/dist/recipes/schemaGenerator.d.ts +28 -0
  300. package/dist/recipes/schemaGenerator.js +565 -0
  301. package/dist/recipes/schemaGenerator.js.map +1 -0
  302. package/dist/recipes/stepObservation.d.ts +44 -0
  303. package/dist/recipes/stepObservation.js +232 -0
  304. package/dist/recipes/stepObservation.js.map +1 -0
  305. package/dist/recipes/templateEngine.d.ts +62 -0
  306. package/dist/recipes/templateEngine.js +201 -0
  307. package/dist/recipes/templateEngine.js.map +1 -0
  308. package/dist/recipes/toolRegistry.d.ts +186 -0
  309. package/dist/recipes/toolRegistry.js +309 -0
  310. package/dist/recipes/toolRegistry.js.map +1 -0
  311. package/dist/recipes/tools/asana.d.ts +16 -0
  312. package/dist/recipes/tools/asana.js +524 -0
  313. package/dist/recipes/tools/asana.js.map +1 -0
  314. package/dist/recipes/tools/calendar.d.ts +6 -0
  315. package/dist/recipes/tools/calendar.js +61 -0
  316. package/dist/recipes/tools/calendar.js.map +1 -0
  317. package/dist/recipes/tools/confluence.d.ts +6 -0
  318. package/dist/recipes/tools/confluence.js +254 -0
  319. package/dist/recipes/tools/confluence.js.map +1 -0
  320. package/dist/recipes/tools/datadog.d.ts +6 -0
  321. package/dist/recipes/tools/datadog.js +239 -0
  322. package/dist/recipes/tools/datadog.js.map +1 -0
  323. package/dist/recipes/tools/diagnostics.d.ts +6 -0
  324. package/dist/recipes/tools/diagnostics.js +36 -0
  325. package/dist/recipes/tools/diagnostics.js.map +1 -0
  326. package/dist/recipes/tools/discord.d.ts +18 -0
  327. package/dist/recipes/tools/discord.js +254 -0
  328. package/dist/recipes/tools/discord.js.map +1 -0
  329. package/dist/recipes/tools/file.d.ts +12 -0
  330. package/dist/recipes/tools/file.js +174 -0
  331. package/dist/recipes/tools/file.js.map +1 -0
  332. package/dist/recipes/tools/git.d.ts +6 -0
  333. package/dist/recipes/tools/git.js +63 -0
  334. package/dist/recipes/tools/git.js.map +1 -0
  335. package/dist/recipes/tools/github.d.ts +6 -0
  336. package/dist/recipes/tools/github.js +116 -0
  337. package/dist/recipes/tools/github.js.map +1 -0
  338. package/dist/recipes/tools/gitlab.d.ts +11 -0
  339. package/dist/recipes/tools/gitlab.js +285 -0
  340. package/dist/recipes/tools/gitlab.js.map +1 -0
  341. package/dist/recipes/tools/gmail.d.ts +6 -0
  342. package/dist/recipes/tools/gmail.js +451 -0
  343. package/dist/recipes/tools/gmail.js.map +1 -0
  344. package/dist/recipes/tools/googleDrive.d.ts +1 -0
  345. package/dist/recipes/tools/googleDrive.js +55 -0
  346. package/dist/recipes/tools/googleDrive.js.map +1 -0
  347. package/dist/recipes/tools/hubspot.d.ts +6 -0
  348. package/dist/recipes/tools/hubspot.js +232 -0
  349. package/dist/recipes/tools/hubspot.js.map +1 -0
  350. package/dist/recipes/tools/index.d.ts +30 -0
  351. package/dist/recipes/tools/index.js +33 -0
  352. package/dist/recipes/tools/index.js.map +1 -0
  353. package/dist/recipes/tools/intercom.d.ts +6 -0
  354. package/dist/recipes/tools/intercom.js +226 -0
  355. package/dist/recipes/tools/intercom.js.map +1 -0
  356. package/dist/recipes/tools/jira.d.ts +14 -0
  357. package/dist/recipes/tools/jira.js +369 -0
  358. package/dist/recipes/tools/jira.js.map +1 -0
  359. package/dist/recipes/tools/linear.d.ts +7 -0
  360. package/dist/recipes/tools/linear.js +307 -0
  361. package/dist/recipes/tools/linear.js.map +1 -0
  362. package/dist/recipes/tools/meetingNotes.d.ts +21 -0
  363. package/dist/recipes/tools/meetingNotes.js +701 -0
  364. package/dist/recipes/tools/meetingNotes.js.map +1 -0
  365. package/dist/recipes/tools/notion.d.ts +6 -0
  366. package/dist/recipes/tools/notion.js +278 -0
  367. package/dist/recipes/tools/notion.js.map +1 -0
  368. package/dist/recipes/tools/pagerduty.d.ts +15 -0
  369. package/dist/recipes/tools/pagerduty.js +451 -0
  370. package/dist/recipes/tools/pagerduty.js.map +1 -0
  371. package/dist/recipes/tools/sentry.d.ts +12 -0
  372. package/dist/recipes/tools/sentry.js +73 -0
  373. package/dist/recipes/tools/sentry.js.map +1 -0
  374. package/dist/recipes/tools/slack.d.ts +6 -0
  375. package/dist/recipes/tools/slack.js +82 -0
  376. package/dist/recipes/tools/slack.js.map +1 -0
  377. package/dist/recipes/tools/stripe.d.ts +6 -0
  378. package/dist/recipes/tools/stripe.js +265 -0
  379. package/dist/recipes/tools/stripe.js.map +1 -0
  380. package/dist/recipes/tools/zendesk.d.ts +6 -0
  381. package/dist/recipes/tools/zendesk.js +245 -0
  382. package/dist/recipes/tools/zendesk.js.map +1 -0
  383. package/dist/recipes/validation.d.ts +13 -0
  384. package/dist/recipes/validation.js +617 -0
  385. package/dist/recipes/validation.js.map +1 -0
  386. package/dist/recipes/yamlRunner.d.ts +130 -2
  387. package/dist/recipes/yamlRunner.js +1009 -402
  388. package/dist/recipes/yamlRunner.js.map +1 -1
  389. package/dist/recipesHttp.d.ts +151 -6
  390. package/dist/recipesHttp.js +999 -29
  391. package/dist/recipesHttp.js.map +1 -1
  392. package/dist/riskTier.js +7 -1
  393. package/dist/riskTier.js.map +1 -1
  394. package/dist/runLog.d.ts +100 -1
  395. package/dist/runLog.js +258 -5
  396. package/dist/runLog.js.map +1 -1
  397. package/dist/schemas/dry-run-plan.v1.json +139 -0
  398. package/dist/schemas/recipe.v1.json +684 -0
  399. package/dist/server.d.ts +127 -8
  400. package/dist/server.js +740 -933
  401. package/dist/server.js.map +1 -1
  402. package/dist/ssrfGuard.d.ts +54 -0
  403. package/dist/ssrfGuard.js +122 -0
  404. package/dist/ssrfGuard.js.map +1 -0
  405. package/dist/streamableHttp.d.ts +39 -1
  406. package/dist/streamableHttp.js +128 -17
  407. package/dist/streamableHttp.js.map +1 -1
  408. package/dist/tokenUsageTracker.d.ts +33 -0
  409. package/dist/tokenUsageTracker.js +146 -0
  410. package/dist/tokenUsageTracker.js.map +1 -0
  411. package/dist/tools/activityLog.d.ts +2 -0
  412. package/dist/tools/addLinearComment.d.ts +1 -0
  413. package/dist/tools/addLinearComment.js +4 -2
  414. package/dist/tools/addLinearComment.js.map +1 -1
  415. package/dist/tools/batchLsp.d.ts +3 -0
  416. package/dist/tools/bridgeDoctor.d.ts +1 -0
  417. package/dist/tools/bridgeDoctor.js +2 -2
  418. package/dist/tools/bridgeDoctor.js.map +1 -1
  419. package/dist/tools/bridgeStatus.d.ts +1 -0
  420. package/dist/tools/cancelClaudeTask.d.ts +2 -0
  421. package/dist/tools/cancelClaudeTask.js +1 -0
  422. package/dist/tools/cancelClaudeTask.js.map +1 -1
  423. package/dist/tools/checkDocumentDirty.d.ts +1 -0
  424. package/dist/tools/clipboard.d.ts +2 -0
  425. package/dist/tools/closeTabs.d.ts +2 -0
  426. package/dist/tools/codeLens.d.ts +1 -0
  427. package/dist/tools/contextBundle.d.ts +1 -0
  428. package/dist/tools/createIssueFromAIComment.d.ts +1 -0
  429. package/dist/tools/createLinearIssue.d.ts +1 -0
  430. package/dist/tools/ctxGetTaskContext.d.ts +1 -0
  431. package/dist/tools/ctxQueryTraces.d.ts +1 -0
  432. package/dist/tools/ctxSaveTrace.d.ts +1 -0
  433. package/dist/tools/debug.d.ts +4 -0
  434. package/dist/tools/decorations.d.ts +2 -0
  435. package/dist/tools/documentLinks.d.ts +1 -0
  436. package/dist/tools/editText.d.ts +1 -0
  437. package/dist/tools/enrichCommit.d.ts +1 -0
  438. package/dist/tools/enrichStackTrace.d.ts +1 -0
  439. package/dist/tools/explainDiagnostic.d.ts +1 -0
  440. package/dist/tools/explainSymbol.d.ts +1 -0
  441. package/dist/tools/fetchCalendarEvents.d.ts +1 -0
  442. package/dist/tools/fetchGithubIssue.d.ts +1 -0
  443. package/dist/tools/fetchGithubPR.d.ts +1 -0
  444. package/dist/tools/fetchLinearIssue.d.ts +1 -0
  445. package/dist/tools/fetchSentryIssue.d.ts +1 -0
  446. package/dist/tools/fetchSlackProfile.d.ts +1 -0
  447. package/dist/tools/fetchSlackProfile.js +4 -1
  448. package/dist/tools/fetchSlackProfile.js.map +1 -1
  449. package/dist/tools/fileOperations.d.ts +3 -0
  450. package/dist/tools/fileWatcher.d.ts +2 -0
  451. package/dist/tools/findFiles.d.ts +1 -0
  452. package/dist/tools/findRelatedTests.d.ts +1 -0
  453. package/dist/tools/fixAllLintErrors.d.ts +1 -0
  454. package/dist/tools/foldingRanges.d.ts +1 -0
  455. package/dist/tools/formatDocument.d.ts +1 -0
  456. package/dist/tools/generateTests.d.ts +1 -0
  457. package/dist/tools/getAIComments.d.ts +1 -0
  458. package/dist/tools/getAnalyticsReport.d.ts +1 -0
  459. package/dist/tools/getArchitectureContext.d.ts +1 -0
  460. package/dist/tools/getBufferContent.d.ts +1 -0
  461. package/dist/tools/getChangeImpact.d.ts +1 -0
  462. package/dist/tools/getClaudeTaskStatus.d.ts +2 -0
  463. package/dist/tools/getClaudeTaskStatus.js +1 -0
  464. package/dist/tools/getClaudeTaskStatus.js.map +1 -1
  465. package/dist/tools/getCodeCoverage.d.ts +1 -0
  466. package/dist/tools/getCommitsForIssue.d.ts +1 -0
  467. package/dist/tools/getConnectorStatus.d.ts +1 -0
  468. package/dist/tools/getCurrentSelection.d.ts +2 -0
  469. package/dist/tools/getDebugState.d.ts +1 -0
  470. package/dist/tools/getDependencyTree.d.ts +1 -0
  471. package/dist/tools/getDiagnostics.d.ts +1 -0
  472. package/dist/tools/getDiffFromHandoff.d.ts +1 -0
  473. package/dist/tools/getDocumentSymbols.d.ts +25 -0
  474. package/dist/tools/getDocumentSymbols.js +74 -8
  475. package/dist/tools/getDocumentSymbols.js.map +1 -1
  476. package/dist/tools/getFileTree.d.ts +1 -0
  477. package/dist/tools/getGitDiff.d.ts +1 -0
  478. package/dist/tools/getGitHotspots.d.ts +1 -0
  479. package/dist/tools/getGitLog.d.ts +1 -0
  480. package/dist/tools/getGitStatus.d.ts +1 -0
  481. package/dist/tools/getImportTree.d.ts +1 -0
  482. package/dist/tools/getImportedSignatures.d.ts +1 -0
  483. package/dist/tools/getOpenEditors.d.ts +1 -0
  484. package/dist/tools/getPRTemplate.d.ts +1 -0
  485. package/dist/tools/getProjectContext.d.ts +1 -0
  486. package/dist/tools/getProjectInfo.d.ts +1 -0
  487. package/dist/tools/getSecurityAdvisories.d.ts +1 -0
  488. package/dist/tools/getSecurityAdvisories.js +10 -1
  489. package/dist/tools/getSecurityAdvisories.js.map +1 -1
  490. package/dist/tools/getSessionUsage.d.ts +4 -0
  491. package/dist/tools/getSessionUsage.js +3 -0
  492. package/dist/tools/getSessionUsage.js.map +1 -1
  493. package/dist/tools/getSymbolHistory.d.ts +1 -0
  494. package/dist/tools/getToolCapabilities.d.ts +1 -0
  495. package/dist/tools/getTypeSignature.d.ts +1 -0
  496. package/dist/tools/getWorkspaceFolders.d.ts +1 -0
  497. package/dist/tools/getWorkspaceSettings.d.ts +1 -0
  498. package/dist/tools/gitHistory.d.ts +2 -0
  499. package/dist/tools/gitWrite.d.ts +11 -0
  500. package/dist/tools/github/actions.d.ts +2 -0
  501. package/dist/tools/github/actions.js +4 -2
  502. package/dist/tools/github/actions.js.map +1 -1
  503. package/dist/tools/github/composite.d.ts +342 -0
  504. package/dist/tools/github/composite.js +343 -0
  505. package/dist/tools/github/composite.js.map +1 -0
  506. package/dist/tools/github/index.d.ts +1 -0
  507. package/dist/tools/github/index.js +1 -0
  508. package/dist/tools/github/index.js.map +1 -1
  509. package/dist/tools/github/issues.d.ts +4 -0
  510. package/dist/tools/github/issues.js +8 -4
  511. package/dist/tools/github/issues.js.map +1 -1
  512. package/dist/tools/github/pr.d.ts +7 -0
  513. package/dist/tools/github/pr.js +50 -12
  514. package/dist/tools/github/pr.js.map +1 -1
  515. package/dist/tools/handoffNote.d.ts +4 -0
  516. package/dist/tools/handoffNote.js +2 -0
  517. package/dist/tools/handoffNote.js.map +1 -1
  518. package/dist/tools/hoverAtCursor.d.ts +1 -0
  519. package/dist/tools/httpClient.d.ts +2 -0
  520. package/dist/tools/index.d.ts +8 -0
  521. package/dist/tools/index.js +47 -8
  522. package/dist/tools/index.js.map +1 -1
  523. package/dist/tools/inlayHints.d.ts +1 -0
  524. package/dist/tools/launchQuickTask.d.ts +2 -0
  525. package/dist/tools/launchQuickTask.js +1 -0
  526. package/dist/tools/launchQuickTask.js.map +1 -1
  527. package/dist/tools/listClaudeTasks.d.ts +2 -0
  528. package/dist/tools/listClaudeTasks.js +1 -0
  529. package/dist/tools/listClaudeTasks.js.map +1 -1
  530. package/dist/tools/listTerminals.d.ts +1 -0
  531. package/dist/tools/lsp.d.ts +14 -0
  532. package/dist/tools/navigateToSymbolByName.d.ts +1 -0
  533. package/dist/tools/openDiff.d.ts +1 -0
  534. package/dist/tools/openFile.d.ts +1 -0
  535. package/dist/tools/openInBrowser.d.ts +1 -0
  536. package/dist/tools/organizeImports.d.ts +1 -0
  537. package/dist/tools/performanceReport.d.ts +1 -0
  538. package/dist/tools/planPersistence.d.ts +5 -0
  539. package/dist/tools/previewEdit.d.ts +1 -0
  540. package/dist/tools/refactorAnalyze.d.ts +1 -0
  541. package/dist/tools/refactorPreview.d.ts +2 -0
  542. package/dist/tools/refactorPreview.js +1 -0
  543. package/dist/tools/refactorPreview.js.map +1 -1
  544. package/dist/tools/replaceBlock.d.ts +1 -0
  545. package/dist/tools/resumeClaudeTask.d.ts +2 -0
  546. package/dist/tools/resumeClaudeTask.js +1 -0
  547. package/dist/tools/resumeClaudeTask.js.map +1 -1
  548. package/dist/tools/runClaudeTask.d.ts +2 -0
  549. package/dist/tools/runClaudeTask.js +1 -0
  550. package/dist/tools/runClaudeTask.js.map +1 -1
  551. package/dist/tools/runCommand.d.ts +1 -0
  552. package/dist/tools/runCommand.js +5 -0
  553. package/dist/tools/runCommand.js.map +1 -1
  554. package/dist/tools/runTests.d.ts +1 -0
  555. package/dist/tools/saveDocument.d.ts +1 -0
  556. package/dist/tools/screenshotAndAnnotate.d.ts +1 -0
  557. package/dist/tools/searchAndReplace.d.ts +1 -0
  558. package/dist/tools/searchTools.d.ts +1 -0
  559. package/dist/tools/searchTools.js +1 -1
  560. package/dist/tools/searchTools.js.map +1 -1
  561. package/dist/tools/searchWorkspace.d.ts +1 -0
  562. package/dist/tools/selectionRanges.d.ts +1 -0
  563. package/dist/tools/semanticTokens.d.ts +1 -0
  564. package/dist/tools/setActiveWorkspaceFolder.d.ts +1 -0
  565. package/dist/tools/signatureHelp.d.ts +1 -0
  566. package/dist/tools/slackListChannels.d.ts +1 -0
  567. package/dist/tools/slackListChannels.js.map +1 -1
  568. package/dist/tools/slackPostMessage.d.ts +1 -0
  569. package/dist/tools/slackPostMessage.js +11 -6
  570. package/dist/tools/slackPostMessage.js.map +1 -1
  571. package/dist/tools/terminal.d.ts +6 -0
  572. package/dist/tools/terminal.js +4 -0
  573. package/dist/tools/terminal.js.map +1 -1
  574. package/dist/tools/testTraceToSource.d.ts +1 -0
  575. package/dist/tools/testTraceToSource.js +2 -2
  576. package/dist/tools/testTraceToSource.js.map +1 -1
  577. package/dist/tools/transaction.d.ts +23 -0
  578. package/dist/tools/transaction.js +29 -0
  579. package/dist/tools/transaction.js.map +1 -1
  580. package/dist/tools/typeHierarchy.d.ts +1 -0
  581. package/dist/tools/updateLinearIssue.d.ts +1 -0
  582. package/dist/tools/updateLinearIssue.js +20 -6
  583. package/dist/tools/updateLinearIssue.js.map +1 -1
  584. package/dist/tools/utils.d.ts +6 -0
  585. package/dist/tools/utils.js +59 -0
  586. package/dist/tools/utils.js.map +1 -1
  587. package/dist/tools/vscodeCommands.d.ts +2 -0
  588. package/dist/tools/vscodeTasks.d.ts +2 -0
  589. package/dist/tools/workspaceSettings.d.ts +1 -0
  590. package/dist/traceEncryption.d.ts +46 -0
  591. package/dist/traceEncryption.js +124 -0
  592. package/dist/traceEncryption.js.map +1 -0
  593. package/dist/transport.d.ts +46 -1
  594. package/dist/transport.js +173 -19
  595. package/dist/transport.js.map +1 -1
  596. package/package.json +30 -8
  597. package/scripts/mcp-stdio-shim.cjs +19 -3
  598. package/scripts/start-all.sh +34 -3
  599. package/templates/automation-policies/recipe-authoring.json +25 -0
  600. package/templates/automation-policy.example.json +6 -0
  601. package/templates/co.patchwork-os.bridge.plist +34 -0
  602. package/templates/policies/README.md +72 -0
  603. package/templates/policies/conservative.json +14 -0
  604. package/templates/policies/developer.json +14 -0
  605. package/templates/policies/headless-ci.json +24 -0
  606. package/templates/policies/personal-assistant.json +15 -0
  607. package/templates/policies/regulated-industry.json +18 -0
  608. package/templates/recipes/approval-queue-ui-test.yaml +205 -0
  609. package/templates/recipes/lint-on-save.yaml +1 -2
  610. package/templates/recipes/morning-brief-slack.yaml +57 -0
  611. package/templates/recipes/morning-brief.yaml +2 -2
  612. package/templates/recipes/project-health-check.yaml +50 -0
  613. package/templates/recipes/webhook/README.md +70 -0
  614. package/templates/recipes/webhook/capture-thought.yaml +26 -0
  615. package/templates/recipes/webhook/customer-escalation.yaml +49 -0
  616. package/templates/recipes/webhook/incident-intake.yaml +46 -0
  617. package/templates/recipes/webhook/meeting-prep.yaml +48 -0
  618. package/templates/recipes/webhook/morning-brief.yaml +57 -0
package/dist/bridge.js CHANGED
@@ -5,33 +5,38 @@ import path from "node:path";
5
5
  import { WebSocket } from "ws";
6
6
  import { ActivityLog } from "./activityLog.js";
7
7
  import { buildSummary } from "./analyticsAggregator.js";
8
- import { getAnalyticsPref } from "./analyticsPrefs.js";
8
+ import { getAnalyticsPref, getAnalyticsSalt } from "./analyticsPrefs.js";
9
9
  import { sendAnalytics } from "./analyticsSend.js";
10
10
  import { getApprovalQueue } from "./approvalQueue.js";
11
11
  import { AutomationHooks, loadPolicy } from "./automation.js";
12
12
  import { loadOrCreateBridgeToken } from "./bridgeToken.js";
13
13
  import { repairBridgeToolsRulesIfStale } from "./bridgeToolsRules.js";
14
- import { createDriver } from "./claudeDriver.js";
15
14
  import { ClaudeOrchestrator } from "./claudeOrchestrator.js";
16
15
  import { CommitIssueLinkLog } from "./commitIssueLinkLog.js";
17
16
  import { DecisionTraceLog } from "./decisionTraceLog.js";
17
+ import { createDriver } from "./drivers/index.js";
18
18
  import { ExtensionClient } from "./extensionClient.js";
19
+ import { lockKillSwitchEnv } from "./featureFlags.js";
19
20
  import { FileLock } from "./fileLock.js";
20
21
  import { buildEnforcementReminder } from "./instructionsUtils.js";
21
22
  import { LockFileManager } from "./lockfile.js";
22
23
  import { Logger } from "./logger.js";
23
24
  import { OAuthServerImpl } from "./oauth.js";
25
+ import { defaultConfigPath, getApiKeysPresent, loadConfig as loadPatchworkConfig, } from "./patchworkConfig.js";
24
26
  import { loadPlugins, loadPluginsFull } from "./pluginLoader.js";
25
27
  import { PluginWatcher } from "./pluginWatcher.js";
28
+ import { isPreToolUseHookRegistered } from "./preToolUseHook.js";
26
29
  import { probeAll } from "./probe.js";
27
- import { RecipeScheduler } from "./recipes/scheduler.js";
28
- import { findWebhookRecipe, listInstalledRecipes, loadRecipePrompt, renderWebhookPrompt, saveRecipe, } from "./recipesHttp.js";
30
+ import { RecipeOrchestration } from "./recipeOrchestration.js";
31
+ import { warnAboutLegacyPermissionsSidecars } from "./recipes/migrationWarnings.js";
32
+ import { RecipeOrchestrator } from "./recipes/RecipeOrchestrator.js";
29
33
  import { classifyTool } from "./riskTier.js";
30
34
  import { RecipeRunLog } from "./runLog.js";
31
35
  import { Server } from "./server.js";
32
36
  import { SessionCheckpoint } from "./sessionCheckpoint.js";
33
37
  import { StreamableHttpHandler } from "./streamableHttp.js";
34
38
  import { initTelemetry, shutdownTelemetry } from "./telemetry.js";
39
+ import { TokenUsageTracker } from "./tokenUsageTracker.js";
35
40
  import { createCtxQueryTracesTool } from "./tools/ctxQueryTraces.js";
36
41
  import { readNote, writeNote } from "./tools/handoffNote.js";
37
42
  import { registerAllTools } from "./tools/index.js";
@@ -112,8 +117,10 @@ export class Bridge {
112
117
  pluginTools = [];
113
118
  pluginWatcher = null;
114
119
  automationHooks = undefined;
120
+ recipeOrchestration = null;
115
121
  recipeScheduler = null;
116
122
  recipeRunLog = null;
123
+ recipeOrchestrator = null;
117
124
  commitIssueLinkLog = null;
118
125
  decisionTraceLog = null;
119
126
  /** Pre-computed digest of recent decisions, refreshed on each session connect. */
@@ -129,6 +136,7 @@ export class Bridge {
129
136
  /** ISO timestamp of last getProjectContext cache write — drives status-bar "context X min ago". */
130
137
  _lastContextCachedAt = null;
131
138
  wsHeartbeatInterval = null;
139
+ tokenUsageTracker;
132
140
  constructor(config) {
133
141
  this.config = config;
134
142
  this.logger = new Logger(config.verbose, config.jsonl);
@@ -136,6 +144,7 @@ export class Bridge {
136
144
  const configDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
137
145
  this.authToken = config.fixedToken ?? loadOrCreateBridgeToken(configDir);
138
146
  this.server = new Server(this.authToken, this.logger, config.corsOrigins);
147
+ this.server.bridgeConfigPath = config.configFilePath ?? undefined;
139
148
  if (config.issuerUrl) {
140
149
  this.oauthServer = new OAuthServerImpl(this.authToken, config.issuerUrl, {
141
150
  configDir,
@@ -150,6 +159,10 @@ export class Bridge {
150
159
  this.logger.info(`Audit log: ${config.auditLogPath}`);
151
160
  }
152
161
  this.extensionClient = new ExtensionClient(this.logger);
162
+ this.tokenUsageTracker = new TokenUsageTracker({
163
+ workspace: config.workspace,
164
+ logger: { warn: (m) => this.logger.warn(m) },
165
+ });
153
166
  // Handle new Claude Code connections
154
167
  this.server.on("connection", async (ws) => {
155
168
  // Reject connections before probes are ready
@@ -181,6 +194,15 @@ export class Bridge {
181
194
  existing.graceTimer = null;
182
195
  existing.ws = ws;
183
196
  existing.wsAlive = true;
197
+ // Clean up old WS listener + reject stale pending elicitations
198
+ // before attaching the new socket. Without this, the previous
199
+ // listener accumulates and pending elicitations from the old
200
+ // client can resolve against the new connection's `activeWs`,
201
+ // surfacing prompts to a client that didn't request them.
202
+ // Intentionally NOT calling `detach()` — that would abort
203
+ // in-flight tool calls, but grace-period semantics preserve
204
+ // those across the reconnect.
205
+ existing.transport.detachSoft();
184
206
  existing.transport.attach(ws);
185
207
  ws.on("pong", () => {
186
208
  existing.wsAlive = true;
@@ -239,8 +261,26 @@ export class Bridge {
239
261
  transport.sessionId = sessionId;
240
262
  transport.setActivityLog(this.activityLog);
241
263
  transport.setToolRateLimit(this.config.toolRateLimit);
264
+ if (this.config.lazyTools)
265
+ transport.setLazyTools(true);
242
266
  if (this.server.approvalGate !== "off") {
243
267
  this.logger.info(`[patchwork] approval gate active: ${this.server.approvalGate} tier(s) require dashboard approval`);
268
+ // The approval gate only sees traffic if Claude Code's PreToolUse
269
+ // hook is registered to POST into /approvals. If it isn't, every
270
+ // CC tool call bypasses the bridge entirely — the queue stays
271
+ // empty, no `approval_decision` rows accumulate, and the entire
272
+ // personalSignals catalog has no input data. Silent foot-gun
273
+ // that wasted hours of investigation; surface it loudly.
274
+ try {
275
+ const ccSettingsPath = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "settings.json");
276
+ if (!isPreToolUseHookRegistered(ccSettingsPath)) {
277
+ this.logger.warn?.(`[patchwork] WARNING: approval gate is on but no PreToolUse hook found in ${ccSettingsPath}. ` +
278
+ `Claude Code will bypass the approval queue. Run 'patchwork-init' to register the hook.`);
279
+ }
280
+ }
281
+ catch {
282
+ // Best-effort warning — never block startup on a hook check failure.
283
+ }
244
284
  transport.setApprovalGate(async ({ toolName, params, sessionId }) => {
245
285
  const tier = classifyTool(toolName);
246
286
  if (this.server.approvalGate === "off")
@@ -677,7 +717,11 @@ export class Bridge {
677
717
  async start() {
678
718
  // 0. Initialize OpenTelemetry (no-op when OTEL_EXPORTER_OTLP_ENDPOINT is unset)
679
719
  initTelemetry();
680
- // 0a. Auto-repair .claude/rules/bridge-tools.md if stale (present but missing the
720
+ // 0a. Snapshot env-derived kill-switch flags. After this, plugins or
721
+ // recipe steps mutating `process.env.PATCHWORK_FLAG_*` cannot disable an
722
+ // active emergency stop. Closes MED-2 from the 2026-04-28 audit.
723
+ lockKillSwitchEnv();
724
+ // 0b. Auto-repair .claude/rules/bridge-tools.md if stale (present but missing the
681
725
  // current version sentinel). Older package versions write stale files that may
682
726
  // lack new tool substitution rules. Repair is silent on success; falls back to
683
727
  // warn-only if the template is missing or the write fails.
@@ -685,6 +729,7 @@ export class Bridge {
685
729
  // 1. Probe available CLI tools (pass workspace so local node_modules/.bin is checked)
686
730
  this.probes = await probeAll(this.config.workspace);
687
731
  this.ready = true;
732
+ this.tokenUsageTracker.start();
688
733
  // 2. Load plugins (after probes, before accepting sessions)
689
734
  this.pluginTools = await loadPlugins(this.config.plugins, this.config, this.logger);
690
735
  if (this.config.pluginWatch && this.config.plugins.length > 0) {
@@ -703,8 +748,21 @@ export class Bridge {
703
748
  this.logger.info(`Available linters: ${probeList(["tsc", "eslint", "pyright", "ruff", "cargo", "go", "biome"])}`);
704
749
  this.logger.info(`Available test runners: ${probeList(["vitest", "jest", "pytest", "cargo", "go"])}`);
705
750
  // 2. Initialize Claude driver and orchestrator (if configured)
706
- if (this.config.claudeDriver !== "none") {
707
- const driver = createDriver(this.config.claudeDriver, this.config.claudeBinary, this.config.antBinary, (msg) => this.logger.info(msg));
751
+ if (this.config.driver !== "none") {
752
+ const driver = createDriver(this.config.driver, {
753
+ binary: this.config.claudeBinary,
754
+ antBinary: this.config.antBinary,
755
+ // Provided to all subprocess-style drivers (claude, gemini). The
756
+ // claude SubprocessDriver only injects --mcp-config when the spawned
757
+ // task sets providerOptions.mcpAccess=true (per-recipe-step opt-in).
758
+ // Gemini wires it unconditionally into ~/.gemini/settings.json.
759
+ bridgeMcp: () => this.port > 0
760
+ ? {
761
+ url: `http://127.0.0.1:${this.port}/mcp`,
762
+ authToken: this.authToken,
763
+ }
764
+ : undefined,
765
+ }, (msg) => this.logger.info(msg));
708
766
  // Patchwork: enrichment link log is useful regardless of orchestrator.
709
767
  const patchworkDir = path.join(os.homedir(), ".patchwork");
710
768
  this.commitIssueLinkLog = new CommitIssueLinkLog({
@@ -764,17 +822,33 @@ export class Bridge {
764
822
  },
765
823
  });
766
824
  this.logger.info(`[bridge] Claude driver: ${driver.name}`);
767
- // Patchwork: start cron-trigger scheduler once the orchestrator exists.
825
+ // Recipe orchestrator — owns in-flight dedup across all entry paths.
826
+ this.recipeOrchestrator = new RecipeOrchestrator({
827
+ workdir: this.config.workspace,
828
+ });
829
+ // Patchwork: wire recipe server fns + build cron scheduler.
768
830
  const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
769
- this.recipeScheduler = new RecipeScheduler({
831
+ // One-shot scan for legacy `<name>.permissions.json` sidecars (alpha.36+
832
+ // no longer generates them). Skipped under NODE_ENV=test.
833
+ warnAboutLegacyPermissionsSidecars(recipesDir);
834
+ this.recipeOrchestration = new RecipeOrchestration({
835
+ server: this.server,
836
+ getOrchestrator: () => this.orchestrator,
837
+ recipeOrchestrator: this.recipeOrchestrator,
838
+ recipeRunLog: this.recipeRunLog,
839
+ activityLog: this.activityLog,
840
+ workdir: this.config.workspace,
841
+ logger: this.logger,
842
+ });
843
+ this.recipeOrchestration.wireServerFns();
844
+ this.recipeScheduler = RecipeOrchestration.buildScheduler({
770
845
  recipesDir,
771
- enqueue: (opts) => this.orchestrator.enqueue(opts),
846
+ runRecipeFn: async (name) => this.server.runRecipeFn?.(name),
847
+ enqueue: (opts) => this.orchestrator?.enqueue(opts) ?? "",
772
848
  logger: this.logger,
773
849
  });
774
- const scheduled = this.recipeScheduler.start();
775
- if (scheduled.length > 0) {
776
- this.logger.info(`[patchwork] scheduled ${scheduled.length} cron recipe${scheduled.length === 1 ? "" : "s"}`);
777
- }
850
+ // scheduler.start() deferred to after this.port is set (see below)
851
+ // so bridgeMcp callback has a valid port when first cron fires.
778
852
  }
779
853
  }
780
854
  if (this.config.automationEnabled) {
@@ -808,6 +882,7 @@ export class Bridge {
808
882
  extensionCircuitBreaker: this.extensionClient.getCircuitBreakerState(),
809
883
  extensionDisconnectCount: this.extensionDisconnectCount,
810
884
  recentActivity: this.activityLog.query({ last: 10 }),
885
+ tokens: this.tokenUsageTracker.getTotals(),
811
886
  };
812
887
  };
813
888
  this.server.metricsFn = () => this.activityLog.toPrometheus({
@@ -974,6 +1049,37 @@ export class Bridge {
974
1049
  };
975
1050
  };
976
1051
  this.server.streamFn = (listener) => this.activityLog.subscribe(listener);
1052
+ this.server.cancelTaskFn = (id) => this.orchestrator?.cancel(id, "user") ?? false;
1053
+ // Wire `/sessions` for the dashboard's Sessions page. The same Map is
1054
+ // already iterated by `statusFn` for the bridge-status payload — exposing
1055
+ // it as a list-only endpoint lets the dashboard render per-session cards
1056
+ // without coupling to the rest of the status payload. Without this the
1057
+ // page returns 404 forever (sessionsFn was declared but never assigned),
1058
+ // even when sessions are connected.
1059
+ this.server.sessionsFn = () => {
1060
+ const approvals = getApprovalQueue().list();
1061
+ const pendingBySession = new Map();
1062
+ for (const a of approvals) {
1063
+ if (!a.sessionId)
1064
+ continue;
1065
+ // Match by 8-char prefix because the dashboard's session id is
1066
+ // the same prefix shape (statusFn emits `s.id.slice(0, 8)`).
1067
+ const prefix = a.sessionId.slice(0, 8);
1068
+ pendingBySession.set(prefix, (pendingBySession.get(prefix) ?? 0) + 1);
1069
+ }
1070
+ const out = [];
1071
+ for (const s of this.sessions.values()) {
1072
+ const id = s.id.slice(0, 8);
1073
+ out.push({
1074
+ id,
1075
+ connectedAt: new Date(s.connectedAt).toISOString(),
1076
+ openedFileCount: s.openedFiles.size,
1077
+ pendingApprovals: pendingBySession.get(id) ?? 0,
1078
+ ...(s.remoteAddr ? { remoteAddr: s.remoteAddr } : {}),
1079
+ });
1080
+ }
1081
+ return out;
1082
+ };
977
1083
  this.server.tasksFn = () => ({
978
1084
  tasks: (this.orchestrator?.list() ?? []).map((t) => ({
979
1085
  taskId: t.id,
@@ -1000,134 +1106,21 @@ export class Bridge {
1000
1106
  this.server.approvalWebhookUrl =
1001
1107
  this.config.approvalWebhookUrl ?? undefined;
1002
1108
  this.server.onApprovalDecision = (event, meta) => this.activityLog.recordEvent(event, meta);
1003
- this.server.recipesFn = () => {
1004
- const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
1005
- return listInstalledRecipes(recipesDir);
1006
- };
1007
- this.server.saveRecipeFn = (draft) => {
1008
- const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
1009
- return saveRecipe(recipesDir, draft);
1010
- };
1011
- this.server.runsFn = (q) => {
1012
- if (!this.recipeRunLog)
1013
- return [];
1014
- return this.recipeRunLog.query({
1015
- ...(q.limit !== undefined && { limit: q.limit }),
1016
- ...(q.trigger !== undefined && {
1017
- trigger: q.trigger,
1018
- }),
1019
- ...(q.status !== undefined && {
1020
- status: q.status,
1021
- }),
1022
- ...(q.recipe !== undefined && { recipe: q.recipe }),
1023
- ...(q.after !== undefined && { after: q.after }),
1024
- });
1025
- };
1026
- this.server.sessionsFn = () => [...this.sessions.values()].map((s) => {
1027
- const tools = this.activityLog.querySessionTools(s.id, 1);
1028
- return {
1029
- id: s.id,
1030
- connectedAt: new Date(s.connectedAt).toISOString(),
1031
- openedFileCount: s.openedFiles.size,
1032
- pendingApprovals: getApprovalQueue()
1033
- .list()
1034
- .filter((a) => a.sessionId === s.id).length,
1035
- firstTool: tools[0]?.tool,
1036
- remoteAddr: s.remoteAddr,
1037
- };
1038
- });
1039
- this.server.sessionDetailFn = (id) => {
1040
- const s = this.sessions.get(id);
1041
- const summary = s
1042
- ? {
1043
- id: s.id,
1044
- connectedAt: new Date(s.connectedAt).toISOString(),
1045
- openedFileCount: s.openedFiles.size,
1046
- pendingApprovals: getApprovalQueue()
1047
- .list()
1048
- .filter((a) => a.sessionId === s.id).length,
1049
- }
1050
- : null;
1051
- const lifecycle = this.activityLog.querySessionLifecycle(id, 100);
1052
- const tools = this.activityLog.querySessionTools(id, 100);
1053
- const decisions = this.decisionTraceLog?.query({ sessionId: id, limit: 50 }) ?? [];
1054
- const approvals = getApprovalQueue()
1055
- .list()
1056
- .filter((a) => a.sessionId === id);
1057
- return {
1058
- summary,
1059
- lifecycle: lifecycle,
1060
- tools: tools,
1061
- decisions: decisions,
1062
- approvals: approvals,
1063
- };
1064
- };
1065
- this.server.webhookFn = async (hookPath, payload) => {
1066
- if (!this.orchestrator) {
1067
- return {
1068
- ok: false,
1069
- error: "orchestrator_unavailable",
1070
- };
1071
- }
1072
- const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
1073
- const match = findWebhookRecipe(recipesDir, hookPath);
1074
- if (!match) {
1075
- return { ok: false, error: "not_found" };
1076
- }
1077
- const loaded = loadRecipePrompt(recipesDir, match.name);
1078
- if (!loaded) {
1079
- return { ok: false, error: "recipe_file_missing" };
1080
- }
1081
- try {
1082
- const taskId = this.orchestrator.enqueue({
1083
- prompt: renderWebhookPrompt(loaded.prompt, payload),
1084
- triggerSource: `webhook:${match.name}`,
1085
- });
1086
- return { ok: true, taskId, name: match.name };
1087
- }
1088
- catch (err) {
1089
- return {
1090
- ok: false,
1091
- error: err instanceof Error ? err.message : String(err),
1092
- };
1093
- }
1094
- };
1095
- this.server.runRecipeFn = async (name, vars) => {
1096
- if (!this.orchestrator) {
1097
- return {
1098
- ok: false,
1099
- error: "Orchestrator unavailable — start bridge with --claude-driver subprocess",
1100
- };
1101
- }
1102
- const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
1103
- const loaded = loadRecipePrompt(recipesDir, name);
1104
- if (!loaded) {
1105
- return {
1106
- ok: false,
1107
- error: `Recipe "${name}" not found in ${recipesDir}`,
1108
- };
1109
- }
1110
- try {
1111
- let prompt = loaded.prompt;
1112
- if (vars && Object.keys(vars).length > 0) {
1113
- const varLines = Object.entries(vars)
1114
- .map(([k, v]) => `${k}=${v}`)
1115
- .join("\n");
1116
- prompt = `Variables:\n${varLines}\n\n${prompt}`;
1117
- }
1118
- const taskId = this.orchestrator.enqueue({
1119
- prompt,
1120
- triggerSource: `recipe:${name}`,
1121
- });
1122
- return { ok: true, taskId };
1123
- }
1124
- catch (err) {
1125
- return {
1126
- ok: false,
1127
- error: err instanceof Error ? err.message : String(err),
1128
- };
1129
- }
1130
- };
1109
+ // Wire activityLog into approvalHttp so passive risk personalization
1110
+ // signals (src/approvalSignals.ts) actually compute against the
1111
+ // user's history. Without this, the personalSignals catalog is
1112
+ // defined but inert — every approval queue entry has
1113
+ // personalSignals: undefined.
1114
+ this.server.activityLog = this.activityLog;
1115
+ // Same wire pattern for h6 (recipe-step trust). recipeRunLog may be
1116
+ // undefined when no orchestrator is configured; that's fine — h6
1117
+ // silently skips and the other 11 heuristics still compute.
1118
+ this.server.recipeRunLog = this.recipeRunLog ?? undefined;
1119
+ // Opt-in switch for personalSignals h10 (time-of-day anomaly).
1120
+ // Default false; user enables via --enable-time-of-day-anomaly or
1121
+ // ~/.patchwork/config.json's enableTimeOfDayAnomaly field.
1122
+ this.server.enableTimeOfDayAnomaly =
1123
+ this.config.enableTimeOfDayAnomaly ?? false;
1131
1124
  this.server.readyFn = () => {
1132
1125
  // Count tools from the first active session (all sessions share the same tool set)
1133
1126
  const anySession = [...this.sessions.values()][0];
@@ -1165,15 +1158,29 @@ export class Bridge {
1165
1158
  extension: this.extensionClient.isConnected(),
1166
1159
  extensionCircuitBreaker: this.extensionClient.getCircuitBreakerState(),
1167
1160
  timeline: this.activityLog.queryTimeline({ last: 50 }),
1168
- patchwork: {
1169
- workspace: this.config.workspace,
1170
- approvalGate: this.server.approvalGate,
1171
- fullMode: this.config.fullMode,
1172
- claudeDriver: this.config.claudeDriver,
1173
- automationEnabled: this.config.automationEnabled,
1174
- port: this.port,
1175
- webhookUrl: this.server.approvalWebhookUrl ?? null,
1176
- },
1161
+ patchwork: (() => {
1162
+ const cfg = loadPatchworkConfig();
1163
+ return {
1164
+ workspace: this.config.workspace,
1165
+ approvalGate: this.server.approvalGate,
1166
+ enableTimeOfDayAnomaly: this.server.enableTimeOfDayAnomaly,
1167
+ fullMode: this.config.fullMode,
1168
+ driver: this.config.driver,
1169
+ model: cfg.model,
1170
+ localEndpoint: cfg.localEndpoint,
1171
+ localModel: cfg.localModel,
1172
+ automationEnabled: this.config.automationEnabled,
1173
+ port: this.port,
1174
+ httpPort: this.port,
1175
+ inboxDir: path.join(os.homedir(), ".patchwork", "inbox"),
1176
+ configPath: defaultConfigPath(),
1177
+ apiKeysPresent: getApiKeysPresent(),
1178
+ webhookUrl: this.server.approvalWebhookUrl ?? null,
1179
+ pushServiceUrl: this.server.pushServiceUrl ?? null,
1180
+ pushServiceToken: this.server.pushServiceToken ? "***" : null,
1181
+ pushServiceBaseUrl: this.server.pushServiceBaseUrl ?? null,
1182
+ };
1183
+ })(),
1177
1184
  };
1178
1185
  };
1179
1186
  // 3b-notify. Wire CC hook notify endpoint
@@ -1259,6 +1266,31 @@ export class Bridge {
1259
1266
  : null, async () => {
1260
1267
  await this.refreshRecentTracesDigest();
1261
1268
  return this.buildInstructions();
1269
+ },
1270
+ // Tail-end deps so `registerAllTools` registers the same tool set
1271
+ // on Streamable-HTTP sessions as on WebSocket. Without this object,
1272
+ // `ctxSaveTrace`, `ctxQueryTraces`, and any tool gated on the
1273
+ // remaining deps silently failed to register over Streamable HTTP
1274
+ // (caught dogfooding `ctx-loop-test` from a remote MCP client).
1275
+ {
1276
+ automationHooks: this.automationHooks,
1277
+ getDisconnectInfo: () => ({
1278
+ at: this.lastDisconnectAt,
1279
+ code: this.lastDisconnectCode,
1280
+ reason: this.lastDisconnectReason,
1281
+ }),
1282
+ onContextCacheUpdated: (generatedAt) => {
1283
+ this._lastContextCachedAt = generatedAt;
1284
+ this._emitLiveState();
1285
+ },
1286
+ getExtensionDisconnectCount: () => this.extensionDisconnectCount,
1287
+ ...(this.commitIssueLinkLog && {
1288
+ commitIssueLinkLog: this.commitIssueLinkLog,
1289
+ }),
1290
+ ...(this.recipeRunLog && { recipeRunLog: this.recipeRunLog }),
1291
+ ...(this.decisionTraceLog && {
1292
+ decisionTraceLog: this.decisionTraceLog,
1293
+ }),
1262
1294
  });
1263
1295
  this.server.httpMcpHandler = (req, res) => this.httpMcpHandler?.handle(req, res) ?? Promise.resolve();
1264
1296
  // 3. Check for stale lock files
@@ -1275,6 +1307,13 @@ export class Bridge {
1275
1307
  throw err;
1276
1308
  }
1277
1309
  this.port = port;
1310
+ // 4a-deferred. Start recipe scheduler now that port is known (bridgeMcp needs a valid port).
1311
+ if (this.recipeScheduler) {
1312
+ const scheduled = this.recipeScheduler.start();
1313
+ if (scheduled.length > 0) {
1314
+ this.logger.info(`[patchwork] scheduled ${scheduled.length} cron recipe${scheduled.length === 1 ? "" : "s"}`);
1315
+ }
1316
+ }
1278
1317
  // 4b. Start WebSocket keepalive heartbeat (keeps MCP session alive during long idle periods)
1279
1318
  this._startWsHeartbeat();
1280
1319
  // 4c. Load persisted tasks from previous sessions (best-effort)
@@ -1438,6 +1477,7 @@ export class Bridge {
1438
1477
  this.stopped = true;
1439
1478
  this.logger.info("Shutting down...");
1440
1479
  this._stopPeriodicSnapshots();
1480
+ this.tokenUsageTracker.stop();
1441
1481
  this.automationHooks?.destroy();
1442
1482
  this.recipeScheduler?.stop();
1443
1483
  this.recipeScheduler = null;
@@ -1471,7 +1511,7 @@ export class Bridge {
1471
1511
  if (this.checkpoint && this.port > 0) {
1472
1512
  try {
1473
1513
  await Promise.race([
1474
- Promise.resolve().then(() => this.checkpoint.write(this._buildCheckpoint(this.port))),
1514
+ Promise.resolve().then(() => this.checkpoint?.write(this._buildCheckpoint(this.port))),
1475
1515
  new Promise((resolve) => setTimeout(resolve, 3000)),
1476
1516
  ]);
1477
1517
  }
@@ -1495,7 +1535,8 @@ export class Bridge {
1495
1535
  if (analyticsOn === true && totalSessions > 0) {
1496
1536
  try {
1497
1537
  const entries = this.activityLog.query({ last: 500 });
1498
- const summary = buildSummary(entries, maxDurationMs, PACKAGE_VERSION);
1538
+ const salt = getAnalyticsSalt();
1539
+ const summary = buildSummary(entries, maxDurationMs, PACKAGE_VERSION, salt);
1499
1540
  await Promise.race([
1500
1541
  sendAnalytics(summary),
1501
1542
  new Promise((resolve) => setTimeout(resolve, 2000)),