patchwork-os 0.2.0-beta.9.canary.93 → 1.1.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 (739) hide show
  1. package/README.bridge.md +15 -16
  2. package/README.md +46 -458
  3. package/dist/activationMetrics.js +2 -3
  4. package/dist/activationMetrics.js.map +1 -1
  5. package/dist/activityLog.d.ts +1 -0
  6. package/dist/activityLog.js +8 -3
  7. package/dist/activityLog.js.map +1 -1
  8. package/dist/adapters/gemini.js +2 -2
  9. package/dist/adapters/gemini.js.map +1 -1
  10. package/dist/adapters/grok.js +6 -1
  11. package/dist/adapters/grok.js.map +1 -1
  12. package/dist/adapters/openai.js +4 -0
  13. package/dist/adapters/openai.js.map +1 -1
  14. package/dist/approvalHttp.d.ts +9 -0
  15. package/dist/approvalHttp.js +210 -171
  16. package/dist/approvalHttp.js.map +1 -1
  17. package/dist/approvalKpi.d.ts +102 -0
  18. package/dist/approvalKpi.js +282 -0
  19. package/dist/approvalKpi.js.map +1 -0
  20. package/dist/approvalQueue.d.ts +1 -1
  21. package/dist/approvalQueue.js.map +1 -1
  22. package/dist/automation.d.ts +45 -4
  23. package/dist/automation.js +123 -15
  24. package/dist/automation.js.map +1 -1
  25. package/dist/bridge.d.ts +10 -0
  26. package/dist/bridge.js +225 -46
  27. package/dist/bridge.js.map +1 -1
  28. package/dist/bridgeLockDiscovery.d.ts +16 -1
  29. package/dist/bridgeLockDiscovery.js +38 -4
  30. package/dist/bridgeLockDiscovery.js.map +1 -1
  31. package/dist/bridgeToolsRules.js +3 -18
  32. package/dist/bridgeToolsRules.js.map +1 -1
  33. package/dist/claudeAuthHttp.d.ts +25 -0
  34. package/dist/claudeAuthHttp.js +219 -0
  35. package/dist/claudeAuthHttp.js.map +1 -0
  36. package/dist/claudeDriver.js +4 -1
  37. package/dist/claudeDriver.js.map +1 -1
  38. package/dist/claudeOrchestrator.d.ts +15 -0
  39. package/dist/claudeOrchestrator.js +44 -0
  40. package/dist/claudeOrchestrator.js.map +1 -1
  41. package/dist/commands/connect.d.ts +47 -0
  42. package/dist/commands/connect.js +419 -0
  43. package/dist/commands/connect.js.map +1 -0
  44. package/dist/commands/install.js +3 -10
  45. package/dist/commands/install.js.map +1 -1
  46. package/dist/commands/launchd.d.ts +7 -0
  47. package/dist/commands/launchd.js +20 -2
  48. package/dist/commands/launchd.js.map +1 -1
  49. package/dist/commands/patchworkInit.d.ts +7 -0
  50. package/dist/commands/patchworkInit.js +26 -0
  51. package/dist/commands/patchworkInit.js.map +1 -1
  52. package/dist/commands/recipe.d.ts +59 -4
  53. package/dist/commands/recipe.js +206 -5
  54. package/dist/commands/recipe.js.map +1 -1
  55. package/dist/commands/recipeInstall.d.ts +9 -0
  56. package/dist/commands/recipeInstall.js +56 -3
  57. package/dist/commands/recipeInstall.js.map +1 -1
  58. package/dist/commands/task.d.ts +25 -0
  59. package/dist/commands/task.js +61 -41
  60. package/dist/commands/task.js.map +1 -1
  61. package/dist/commands/tokenEfficiency.d.ts +4 -0
  62. package/dist/commands/tokenEfficiency.js +23 -26
  63. package/dist/commands/tokenEfficiency.js.map +1 -1
  64. package/dist/commands/tracesExport.d.ts +15 -1
  65. package/dist/commands/tracesExport.js +39 -5
  66. package/dist/commands/tracesExport.js.map +1 -1
  67. package/dist/config.js +16 -0
  68. package/dist/config.js.map +1 -1
  69. package/dist/connectorRoutes.js +613 -62
  70. package/dist/connectorRoutes.js.map +1 -1
  71. package/dist/connectors/asana.js +9 -3
  72. package/dist/connectors/asana.js.map +1 -1
  73. package/dist/connectors/baseConnector.d.ts +16 -0
  74. package/dist/connectors/baseConnector.js +2 -0
  75. package/dist/connectors/baseConnector.js.map +1 -1
  76. package/dist/connectors/caldiy.js +34 -0
  77. package/dist/connectors/caldiy.js.map +1 -1
  78. package/dist/connectors/confluence.js +9 -2
  79. package/dist/connectors/confluence.js.map +1 -1
  80. package/dist/connectors/connectorActivity.d.ts +24 -0
  81. package/dist/connectors/connectorActivity.js +32 -0
  82. package/dist/connectors/connectorActivity.js.map +1 -0
  83. package/dist/connectors/connectorRedirectUri.js +22 -2
  84. package/dist/connectors/connectorRedirectUri.js.map +1 -1
  85. package/dist/connectors/connectorRegistry.d.ts +0 -3
  86. package/dist/connectors/connectorRegistry.js +8 -8
  87. package/dist/connectors/connectorRegistry.js.map +1 -1
  88. package/dist/connectors/datadog.js +8 -1
  89. package/dist/connectors/datadog.js.map +1 -1
  90. package/dist/connectors/discord.js +4 -3
  91. package/dist/connectors/discord.js.map +1 -1
  92. package/dist/connectors/elasticsearch.js +22 -0
  93. package/dist/connectors/elasticsearch.js.map +1 -1
  94. package/dist/connectors/github.d.ts +38 -0
  95. package/dist/connectors/github.js +91 -5
  96. package/dist/connectors/github.js.map +1 -1
  97. package/dist/connectors/gitlab.js +19 -16
  98. package/dist/connectors/gitlab.js.map +1 -1
  99. package/dist/connectors/gmail.d.ts +5 -0
  100. package/dist/connectors/gmail.js +53 -11
  101. package/dist/connectors/gmail.js.map +1 -1
  102. package/dist/connectors/googleCalendar.js +5 -4
  103. package/dist/connectors/googleCalendar.js.map +1 -1
  104. package/dist/connectors/googleDocs.js +7 -6
  105. package/dist/connectors/googleDocs.js.map +1 -1
  106. package/dist/connectors/googleDrive.js +15 -8
  107. package/dist/connectors/googleDrive.js.map +1 -1
  108. package/dist/connectors/grafana.js +39 -3
  109. package/dist/connectors/grafana.js.map +1 -1
  110. package/dist/connectors/jira.d.ts +1 -1
  111. package/dist/connectors/jira.js +19 -4
  112. package/dist/connectors/jira.js.map +1 -1
  113. package/dist/connectors/linear.js +2 -2
  114. package/dist/connectors/linear.js.map +1 -1
  115. package/dist/connectors/mcpClient.d.ts +29 -1
  116. package/dist/connectors/mcpClient.js +114 -44
  117. package/dist/connectors/mcpClient.js.map +1 -1
  118. package/dist/connectors/mcpOAuth.d.ts +1 -0
  119. package/dist/connectors/mcpOAuth.js +49 -4
  120. package/dist/connectors/mcpOAuth.js.map +1 -1
  121. package/dist/connectors/monday.js +5 -4
  122. package/dist/connectors/monday.js.map +1 -1
  123. package/dist/connectors/mongodb.js +32 -5
  124. package/dist/connectors/mongodb.js.map +1 -1
  125. package/dist/connectors/notion.d.ts +1 -1
  126. package/dist/connectors/notion.js +22 -6
  127. package/dist/connectors/notion.js.map +1 -1
  128. package/dist/connectors/oauthError.d.ts +17 -0
  129. package/dist/connectors/oauthError.js +50 -0
  130. package/dist/connectors/oauthError.js.map +1 -0
  131. package/dist/connectors/postgres.d.ts +7 -0
  132. package/dist/connectors/postgres.js +32 -2
  133. package/dist/connectors/postgres.js.map +1 -1
  134. package/dist/connectors/posthog.js +26 -0
  135. package/dist/connectors/posthog.js.map +1 -1
  136. package/dist/connectors/redis.d.ts +1 -0
  137. package/dist/connectors/redis.js +47 -20
  138. package/dist/connectors/redis.js.map +1 -1
  139. package/dist/connectors/salesforce.js +66 -5
  140. package/dist/connectors/salesforce.js.map +1 -1
  141. package/dist/connectors/sentry.js +2 -2
  142. package/dist/connectors/sentry.js.map +1 -1
  143. package/dist/connectors/slack.js +37 -14
  144. package/dist/connectors/slack.js.map +1 -1
  145. package/dist/connectors/snowflake.js +24 -0
  146. package/dist/connectors/snowflake.js.map +1 -1
  147. package/dist/connectors/stripe.js +9 -2
  148. package/dist/connectors/stripe.js.map +1 -1
  149. package/dist/connectors/supabase.js +34 -0
  150. package/dist/connectors/supabase.js.map +1 -1
  151. package/dist/connectors/tokenStorage.d.ts +20 -0
  152. package/dist/connectors/tokenStorage.js +196 -64
  153. package/dist/connectors/tokenStorage.js.map +1 -1
  154. package/dist/connectors/woocommerce.js +34 -0
  155. package/dist/connectors/woocommerce.js.map +1 -1
  156. package/dist/copilot/parseIntent.d.ts +58 -0
  157. package/dist/copilot/parseIntent.js +149 -0
  158. package/dist/copilot/parseIntent.js.map +1 -0
  159. package/dist/decisionReplay.d.ts +8 -6
  160. package/dist/decisionReplay.js +35 -11
  161. package/dist/decisionReplay.js.map +1 -1
  162. package/dist/decisionTraceLog.d.ts +9 -0
  163. package/dist/decisionTraceLog.js +6 -0
  164. package/dist/decisionTraceLog.js.map +1 -1
  165. package/dist/drivers/claude/api.js +49 -7
  166. package/dist/drivers/claude/api.js.map +1 -1
  167. package/dist/drivers/claude/envSanitizer.d.ts +9 -1
  168. package/dist/drivers/claude/envSanitizer.js +41 -3
  169. package/dist/drivers/claude/envSanitizer.js.map +1 -1
  170. package/dist/drivers/claude/streamParser.d.ts +13 -0
  171. package/dist/drivers/claude/streamParser.js.map +1 -1
  172. package/dist/drivers/claude/subprocess.js +131 -16
  173. package/dist/drivers/claude/subprocess.js.map +1 -1
  174. package/dist/drivers/claude/subprocessSettings.d.ts +1 -1
  175. package/dist/drivers/claude/subprocessSettings.js +18 -4
  176. package/dist/drivers/claude/subprocessSettings.js.map +1 -1
  177. package/dist/drivers/gemini/index.d.ts +13 -0
  178. package/dist/drivers/gemini/index.js +211 -67
  179. package/dist/drivers/gemini/index.js.map +1 -1
  180. package/dist/drivers/local/index.d.ts +2 -17
  181. package/dist/drivers/local/index.js +5 -92
  182. package/dist/drivers/local/index.js.map +1 -1
  183. package/dist/drivers/openai/index.js +69 -9
  184. package/dist/drivers/openai/index.js.map +1 -1
  185. package/dist/drivers/outputCap.d.ts +27 -0
  186. package/dist/drivers/outputCap.js +50 -0
  187. package/dist/drivers/outputCap.js.map +1 -0
  188. package/dist/embeddings/cosine.d.ts +23 -0
  189. package/dist/embeddings/cosine.js +59 -0
  190. package/dist/embeddings/cosine.js.map +1 -0
  191. package/dist/embeddings/index.d.ts +19 -0
  192. package/dist/embeddings/index.js +35 -0
  193. package/dist/embeddings/index.js.map +1 -0
  194. package/dist/embeddings/localEmbeddings.d.ts +29 -0
  195. package/dist/embeddings/localEmbeddings.js +103 -0
  196. package/dist/embeddings/localEmbeddings.js.map +1 -0
  197. package/dist/embeddings/localEndpoints.d.ts +24 -0
  198. package/dist/embeddings/localEndpoints.js +37 -0
  199. package/dist/embeddings/localEndpoints.js.map +1 -0
  200. package/dist/embeddings/types.d.ts +32 -0
  201. package/dist/embeddings/types.js +12 -0
  202. package/dist/embeddings/types.js.map +1 -0
  203. package/dist/featureFlags.d.ts +18 -0
  204. package/dist/featureFlags.js +32 -0
  205. package/dist/featureFlags.js.map +1 -1
  206. package/dist/fp/automationInterpreter.js +118 -39
  207. package/dist/fp/automationInterpreter.js.map +1 -1
  208. package/dist/fp/automationProgram.d.ts +10 -0
  209. package/dist/fp/automationProgram.js.map +1 -1
  210. package/dist/fp/automationState.d.ts +6 -0
  211. package/dist/fp/automationState.js +38 -0
  212. package/dist/fp/automationState.js.map +1 -1
  213. package/dist/fp/commandDescription.d.ts +6 -0
  214. package/dist/fp/commandDescription.js +18 -5
  215. package/dist/fp/commandDescription.js.map +1 -1
  216. package/dist/fp/interpreterContext.d.ts +16 -5
  217. package/dist/fp/interpreterContext.js +59 -3
  218. package/dist/fp/interpreterContext.js.map +1 -1
  219. package/dist/fp/policyParser.js +18 -8
  220. package/dist/fp/policyParser.js.map +1 -1
  221. package/dist/haltPushDispatch.d.ts +4 -0
  222. package/dist/haltPushDispatch.js +7 -22
  223. package/dist/haltPushDispatch.js.map +1 -1
  224. package/dist/inboxRoutes.js +2 -2
  225. package/dist/inboxRoutes.js.map +1 -1
  226. package/dist/index.js +672 -135
  227. package/dist/index.js.map +1 -1
  228. package/dist/installGuard.js +6 -0
  229. package/dist/installGuard.js.map +1 -1
  230. package/dist/localEndpointGuard.d.ts +25 -0
  231. package/dist/localEndpointGuard.js +101 -0
  232. package/dist/localEndpointGuard.js.map +1 -0
  233. package/dist/mcpRoutes.js +1 -1
  234. package/dist/mcpRoutes.js.map +1 -1
  235. package/dist/oauth.d.ts +13 -0
  236. package/dist/oauth.js +53 -9
  237. package/dist/oauth.js.map +1 -1
  238. package/dist/orchestrator/childBridgeClient.d.ts +7 -0
  239. package/dist/orchestrator/childBridgeClient.js +42 -1
  240. package/dist/orchestrator/childBridgeClient.js.map +1 -1
  241. package/dist/orchestrator/orchestratorBridge.d.ts +33 -0
  242. package/dist/orchestrator/orchestratorBridge.js +75 -9
  243. package/dist/orchestrator/orchestratorBridge.js.map +1 -1
  244. package/dist/patchworkConfig.d.ts +4 -0
  245. package/dist/patchworkConfig.js +33 -4
  246. package/dist/patchworkConfig.js.map +1 -1
  247. package/dist/probe.js +39 -5
  248. package/dist/probe.js.map +1 -1
  249. package/dist/recipeOrchestration.d.ts +59 -0
  250. package/dist/recipeOrchestration.js +314 -60
  251. package/dist/recipeOrchestration.js.map +1 -1
  252. package/dist/recipeRoutes.d.ts +93 -0
  253. package/dist/recipeRoutes.js +664 -27
  254. package/dist/recipeRoutes.js.map +1 -1
  255. package/dist/recipes/RecipeOrchestrator.d.ts +14 -0
  256. package/dist/recipes/RecipeOrchestrator.js +42 -2
  257. package/dist/recipes/RecipeOrchestrator.js.map +1 -1
  258. package/dist/recipes/agentExecutor.d.ts +51 -2
  259. package/dist/recipes/agentExecutor.js +86 -13
  260. package/dist/recipes/agentExecutor.js.map +1 -1
  261. package/dist/recipes/chainedRunner.d.ts +111 -3
  262. package/dist/recipes/chainedRunner.js +440 -45
  263. package/dist/recipes/chainedRunner.js.map +1 -1
  264. package/dist/recipes/compiler.js +42 -29
  265. package/dist/recipes/compiler.js.map +1 -1
  266. package/dist/recipes/connectorPreflight.js +31 -0
  267. package/dist/recipes/connectorPreflight.js.map +1 -1
  268. package/dist/recipes/dependencyGraph.d.ts +16 -0
  269. package/dist/recipes/dependencyGraph.js +37 -2
  270. package/dist/recipes/dependencyGraph.js.map +1 -1
  271. package/dist/recipes/eventTriggerPrograms.d.ts +36 -0
  272. package/dist/recipes/eventTriggerPrograms.js +158 -0
  273. package/dist/recipes/eventTriggerPrograms.js.map +1 -0
  274. package/dist/recipes/githubInstallSource.d.ts +9 -2
  275. package/dist/recipes/githubInstallSource.js +36 -6
  276. package/dist/recipes/githubInstallSource.js.map +1 -1
  277. package/dist/recipes/haltCategory.d.ts +10 -0
  278. package/dist/recipes/haltCategory.js +11 -1
  279. package/dist/recipes/haltCategory.js.map +1 -1
  280. package/dist/recipes/idempotencyKey.d.ts +30 -3
  281. package/dist/recipes/idempotencyKey.js +89 -5
  282. package/dist/recipes/idempotencyKey.js.map +1 -1
  283. package/dist/recipes/installer.js +12 -35
  284. package/dist/recipes/installer.js.map +1 -1
  285. package/dist/recipes/judgeVerdict.d.ts +11 -1
  286. package/dist/recipes/judgeVerdict.js +47 -7
  287. package/dist/recipes/judgeVerdict.js.map +1 -1
  288. package/dist/recipes/names.d.ts +5 -0
  289. package/dist/recipes/names.js +10 -5
  290. package/dist/recipes/names.js.map +1 -1
  291. package/dist/recipes/parser.js +74 -10
  292. package/dist/recipes/parser.js.map +1 -1
  293. package/dist/recipes/pricing/costRouter.d.ts +43 -0
  294. package/dist/recipes/pricing/costRouter.js +44 -0
  295. package/dist/recipes/pricing/costRouter.js.map +1 -0
  296. package/dist/recipes/pricing/priceTable.d.ts +76 -0
  297. package/dist/recipes/pricing/priceTable.js +144 -0
  298. package/dist/recipes/pricing/priceTable.js.map +1 -0
  299. package/dist/recipes/replayRun.js +5 -2
  300. package/dist/recipes/replayRun.js.map +1 -1
  301. package/dist/recipes/resolveRecipePath.js +15 -1
  302. package/dist/recipes/resolveRecipePath.js.map +1 -1
  303. package/dist/recipes/runBudget.d.ts +93 -32
  304. package/dist/recipes/runBudget.js +213 -49
  305. package/dist/recipes/runBudget.js.map +1 -1
  306. package/dist/recipes/runRegistry.d.ts +32 -0
  307. package/dist/recipes/runRegistry.js +54 -0
  308. package/dist/recipes/runRegistry.js.map +1 -0
  309. package/dist/recipes/scheduler.js +6 -2
  310. package/dist/recipes/scheduler.js.map +1 -1
  311. package/dist/recipes/schema.d.ts +59 -6
  312. package/dist/recipes/schemaGenerator.d.ts +9 -0
  313. package/dist/recipes/schemaGenerator.js +446 -77
  314. package/dist/recipes/schemaGenerator.js.map +1 -1
  315. package/dist/recipes/simulation/aggregateRunRisk.d.ts +46 -0
  316. package/dist/recipes/simulation/aggregateRunRisk.js +120 -0
  317. package/dist/recipes/simulation/aggregateRunRisk.js.map +1 -0
  318. package/dist/recipes/simulation/costProjector.d.ts +32 -0
  319. package/dist/recipes/simulation/costProjector.js +194 -0
  320. package/dist/recipes/simulation/costProjector.js.map +1 -0
  321. package/dist/recipes/simulation/sideEffects.d.ts +32 -0
  322. package/dist/recipes/simulation/sideEffects.js +62 -0
  323. package/dist/recipes/simulation/sideEffects.js.map +1 -0
  324. package/dist/recipes/simulation/simulate.d.ts +36 -0
  325. package/dist/recipes/simulation/simulate.js +264 -0
  326. package/dist/recipes/simulation/simulate.js.map +1 -0
  327. package/dist/recipes/simulation/simulateMockedRun.d.ts +52 -0
  328. package/dist/recipes/simulation/simulateMockedRun.js +84 -0
  329. package/dist/recipes/simulation/simulateMockedRun.js.map +1 -0
  330. package/dist/recipes/simulation/synthesizeMockedOutputs.d.ts +31 -0
  331. package/dist/recipes/simulation/synthesizeMockedOutputs.js +50 -0
  332. package/dist/recipes/simulation/synthesizeMockedOutputs.js.map +1 -0
  333. package/dist/recipes/simulation/types.d.ts +198 -0
  334. package/dist/recipes/simulation/types.js +30 -0
  335. package/dist/recipes/simulation/types.js.map +1 -0
  336. package/dist/recipes/stepObservation.d.ts +12 -0
  337. package/dist/recipes/stepObservation.js +21 -1
  338. package/dist/recipes/stepObservation.js.map +1 -1
  339. package/dist/recipes/toolRegistry.js +61 -24
  340. package/dist/recipes/toolRegistry.js.map +1 -1
  341. package/dist/recipes/tools/airtable.d.ts +15 -0
  342. package/dist/recipes/tools/airtable.js +240 -0
  343. package/dist/recipes/tools/airtable.js.map +1 -0
  344. package/dist/recipes/tools/caldiy.d.ts +13 -0
  345. package/dist/recipes/tools/caldiy.js +215 -0
  346. package/dist/recipes/tools/caldiy.js.map +1 -0
  347. package/dist/recipes/tools/circleci.d.ts +10 -0
  348. package/dist/recipes/tools/circleci.js +204 -0
  349. package/dist/recipes/tools/circleci.js.map +1 -0
  350. package/dist/recipes/tools/cloudflare.d.ts +13 -0
  351. package/dist/recipes/tools/cloudflare.js +211 -0
  352. package/dist/recipes/tools/cloudflare.js.map +1 -0
  353. package/dist/recipes/tools/confluence.js +11 -10
  354. package/dist/recipes/tools/confluence.js.map +1 -1
  355. package/dist/recipes/tools/datadog.js +13 -12
  356. package/dist/recipes/tools/datadog.js.map +1 -1
  357. package/dist/recipes/tools/docs.d.ts +18 -0
  358. package/dist/recipes/tools/docs.js +95 -0
  359. package/dist/recipes/tools/docs.js.map +1 -0
  360. package/dist/recipes/tools/elasticsearch.d.ts +11 -0
  361. package/dist/recipes/tools/elasticsearch.js +157 -0
  362. package/dist/recipes/tools/elasticsearch.js.map +1 -0
  363. package/dist/recipes/tools/fanOut.js +4 -4
  364. package/dist/recipes/tools/fanOut.js.map +1 -1
  365. package/dist/recipes/tools/figma.d.ts +12 -0
  366. package/dist/recipes/tools/figma.js +195 -0
  367. package/dist/recipes/tools/figma.js.map +1 -0
  368. package/dist/recipes/tools/file.js +43 -22
  369. package/dist/recipes/tools/file.js.map +1 -1
  370. package/dist/recipes/tools/github.js +162 -5
  371. package/dist/recipes/tools/github.js.map +1 -1
  372. package/dist/recipes/tools/gmail.js +45 -33
  373. package/dist/recipes/tools/gmail.js.map +1 -1
  374. package/dist/recipes/tools/grafana.d.ts +11 -0
  375. package/dist/recipes/tools/grafana.js +216 -0
  376. package/dist/recipes/tools/grafana.js.map +1 -0
  377. package/dist/recipes/tools/http.d.ts +1 -1
  378. package/dist/recipes/tools/http.js +10 -28
  379. package/dist/recipes/tools/http.js.map +1 -1
  380. package/dist/recipes/tools/hubspot.js +13 -12
  381. package/dist/recipes/tools/hubspot.js.map +1 -1
  382. package/dist/recipes/tools/index.d.ts +28 -0
  383. package/dist/recipes/tools/index.js +28 -0
  384. package/dist/recipes/tools/index.js.map +1 -1
  385. package/dist/recipes/tools/intercom.js +11 -10
  386. package/dist/recipes/tools/intercom.js.map +1 -1
  387. package/dist/recipes/tools/jira.js +13 -0
  388. package/dist/recipes/tools/jira.js.map +1 -1
  389. package/dist/recipes/tools/meetingNotes.js +2 -2
  390. package/dist/recipes/tools/monday.d.ts +22 -0
  391. package/dist/recipes/tools/monday.js +242 -0
  392. package/dist/recipes/tools/monday.js.map +1 -0
  393. package/dist/recipes/tools/mongodb.d.ts +19 -0
  394. package/dist/recipes/tools/mongodb.js +194 -0
  395. package/dist/recipes/tools/mongodb.js.map +1 -0
  396. package/dist/recipes/tools/notion.js +11 -10
  397. package/dist/recipes/tools/notion.js.map +1 -1
  398. package/dist/recipes/tools/obsidian.d.ts +15 -0
  399. package/dist/recipes/tools/obsidian.js +173 -0
  400. package/dist/recipes/tools/obsidian.js.map +1 -0
  401. package/dist/recipes/tools/outcomes.d.ts +15 -0
  402. package/dist/recipes/tools/outcomes.js +111 -0
  403. package/dist/recipes/tools/outcomes.js.map +1 -0
  404. package/dist/recipes/tools/paystack.d.ts +11 -0
  405. package/dist/recipes/tools/paystack.js +212 -0
  406. package/dist/recipes/tools/paystack.js.map +1 -0
  407. package/dist/recipes/tools/pipedrive.d.ts +16 -0
  408. package/dist/recipes/tools/pipedrive.js +234 -0
  409. package/dist/recipes/tools/pipedrive.js.map +1 -0
  410. package/dist/recipes/tools/postgres.d.ts +15 -0
  411. package/dist/recipes/tools/postgres.js +186 -0
  412. package/dist/recipes/tools/postgres.js.map +1 -0
  413. package/dist/recipes/tools/posthog.d.ts +16 -0
  414. package/dist/recipes/tools/posthog.js +219 -0
  415. package/dist/recipes/tools/posthog.js.map +1 -0
  416. package/dist/recipes/tools/redis.d.ts +9 -0
  417. package/dist/recipes/tools/redis.js +141 -0
  418. package/dist/recipes/tools/redis.js.map +1 -0
  419. package/dist/recipes/tools/resend.d.ts +8 -0
  420. package/dist/recipes/tools/resend.js +153 -0
  421. package/dist/recipes/tools/resend.js.map +1 -0
  422. package/dist/recipes/tools/salesforce.d.ts +16 -0
  423. package/dist/recipes/tools/salesforce.js +184 -0
  424. package/dist/recipes/tools/salesforce.js.map +1 -0
  425. package/dist/recipes/tools/sendgrid.d.ts +9 -0
  426. package/dist/recipes/tools/sendgrid.js +175 -0
  427. package/dist/recipes/tools/sendgrid.js.map +1 -0
  428. package/dist/recipes/tools/shopify.d.ts +16 -0
  429. package/dist/recipes/tools/shopify.js +266 -0
  430. package/dist/recipes/tools/shopify.js.map +1 -0
  431. package/dist/recipes/tools/snowflake.d.ts +16 -0
  432. package/dist/recipes/tools/snowflake.js +173 -0
  433. package/dist/recipes/tools/snowflake.js.map +1 -0
  434. package/dist/recipes/tools/stripe.js +13 -12
  435. package/dist/recipes/tools/stripe.js.map +1 -1
  436. package/dist/recipes/tools/supabase.d.ts +9 -0
  437. package/dist/recipes/tools/supabase.js +133 -0
  438. package/dist/recipes/tools/supabase.js.map +1 -0
  439. package/dist/recipes/tools/todoist.d.ts +15 -0
  440. package/dist/recipes/tools/todoist.js +228 -0
  441. package/dist/recipes/tools/todoist.js.map +1 -0
  442. package/dist/recipes/tools/twilio.d.ts +11 -0
  443. package/dist/recipes/tools/twilio.js +180 -0
  444. package/dist/recipes/tools/twilio.js.map +1 -0
  445. package/dist/recipes/tools/vercel.d.ts +9 -0
  446. package/dist/recipes/tools/vercel.js +146 -0
  447. package/dist/recipes/tools/vercel.js.map +1 -0
  448. package/dist/recipes/tools/webflow.d.ts +11 -0
  449. package/dist/recipes/tools/webflow.js +244 -0
  450. package/dist/recipes/tools/webflow.js.map +1 -0
  451. package/dist/recipes/tools/woocommerce.d.ts +18 -0
  452. package/dist/recipes/tools/woocommerce.js +260 -0
  453. package/dist/recipes/tools/woocommerce.js.map +1 -0
  454. package/dist/recipes/tools/wrapConnectorExecute.d.ts +25 -0
  455. package/dist/recipes/tools/wrapConnectorExecute.js +37 -0
  456. package/dist/recipes/tools/wrapConnectorExecute.js.map +1 -0
  457. package/dist/recipes/tools/zendesk.js +11 -10
  458. package/dist/recipes/tools/zendesk.js.map +1 -1
  459. package/dist/recipes/triggerVars.d.ts +15 -0
  460. package/dist/recipes/triggerVars.js +57 -0
  461. package/dist/recipes/triggerVars.js.map +1 -0
  462. package/dist/recipes/validation.d.ts +1 -1
  463. package/dist/recipes/validation.js +536 -12
  464. package/dist/recipes/validation.js.map +1 -1
  465. package/dist/recipes/yamlPositions.d.ts +1 -1
  466. package/dist/recipes/yamlPositions.js +7 -2
  467. package/dist/recipes/yamlPositions.js.map +1 -1
  468. package/dist/recipes/yamlRunner.d.ts +216 -4
  469. package/dist/recipes/yamlRunner.js +1030 -96
  470. package/dist/recipes/yamlRunner.js.map +1 -1
  471. package/dist/recipesHttp.d.ts +5 -0
  472. package/dist/recipesHttp.js +68 -6
  473. package/dist/recipesHttp.js.map +1 -1
  474. package/dist/resources.js +14 -1
  475. package/dist/resources.js.map +1 -1
  476. package/dist/riskSignals.d.ts +42 -0
  477. package/dist/riskSignals.js +197 -0
  478. package/dist/riskSignals.js.map +1 -0
  479. package/dist/riskTier.d.ts +9 -0
  480. package/dist/riskTier.js +48 -0
  481. package/dist/riskTier.js.map +1 -1
  482. package/dist/runLog.d.ts +53 -0
  483. package/dist/runLog.js +75 -16
  484. package/dist/runLog.js.map +1 -1
  485. package/dist/schemas/recipe.v1.json +360 -16
  486. package/dist/server.d.ts +60 -0
  487. package/dist/server.js +192 -31
  488. package/dist/server.js.map +1 -1
  489. package/dist/sessionCheckpoint.js +4 -1
  490. package/dist/sessionCheckpoint.js.map +1 -1
  491. package/dist/sessionDetail.d.ts +43 -0
  492. package/dist/sessionDetail.js +59 -0
  493. package/dist/sessionDetail.js.map +1 -0
  494. package/dist/settingsEnv.d.ts +47 -0
  495. package/dist/settingsEnv.js +88 -0
  496. package/dist/settingsEnv.js.map +1 -0
  497. package/dist/streamableHttp.js +44 -10
  498. package/dist/streamableHttp.js.map +1 -1
  499. package/dist/ta/backtest/abnday.d.ts +53 -0
  500. package/dist/ta/backtest/abnday.js +123 -0
  501. package/dist/ta/backtest/abnday.js.map +1 -0
  502. package/dist/ta/backtest/directional.d.ts +48 -0
  503. package/dist/ta/backtest/directional.js +103 -0
  504. package/dist/ta/backtest/directional.js.map +1 -0
  505. package/dist/ta/backtest/fundx.d.ts +67 -0
  506. package/dist/ta/backtest/fundx.js +264 -0
  507. package/dist/ta/backtest/fundx.js.map +1 -0
  508. package/dist/ta/backtest/fvg.d.ts +62 -0
  509. package/dist/ta/backtest/fvg.js +236 -0
  510. package/dist/ta/backtest/fvg.js.map +1 -0
  511. package/dist/ta/backtest/ichimoku.d.ts +41 -0
  512. package/dist/ta/backtest/ichimoku.js +146 -0
  513. package/dist/ta/backtest/ichimoku.js.map +1 -0
  514. package/dist/ta/backtest/ingest.d.ts +20 -0
  515. package/dist/ta/backtest/ingest.js +104 -0
  516. package/dist/ta/backtest/ingest.js.map +1 -0
  517. package/dist/ta/backtest/msb.d.ts +49 -0
  518. package/dist/ta/backtest/msb.js +162 -0
  519. package/dist/ta/backtest/msb.js.map +1 -0
  520. package/dist/ta/backtest/scoring.d.ts +33 -0
  521. package/dist/ta/backtest/scoring.js +82 -0
  522. package/dist/ta/backtest/scoring.js.map +1 -0
  523. package/dist/ta/backtest/summary.d.ts +20 -0
  524. package/dist/ta/backtest/summary.js +42 -0
  525. package/dist/ta/backtest/summary.js.map +1 -0
  526. package/dist/ta/backtest/tsmom.d.ts +72 -0
  527. package/dist/ta/backtest/tsmom.js +202 -0
  528. package/dist/ta/backtest/tsmom.js.map +1 -0
  529. package/dist/ta/backtest/volsq.d.ts +34 -0
  530. package/dist/ta/backtest/volsq.js +154 -0
  531. package/dist/ta/backtest/volsq.js.map +1 -0
  532. package/dist/ta/backtest/walkForward.d.ts +45 -0
  533. package/dist/ta/backtest/walkForward.js +136 -0
  534. package/dist/ta/backtest/walkForward.js.map +1 -0
  535. package/dist/ta/cycles.d.ts +15 -0
  536. package/dist/ta/cycles.js +31 -0
  537. package/dist/ta/cycles.js.map +1 -0
  538. package/dist/ta/desk/accrualEmitter.d.ts +59 -0
  539. package/dist/ta/desk/accrualEmitter.js +136 -0
  540. package/dist/ta/desk/accrualEmitter.js.map +1 -0
  541. package/dist/ta/desk/cellBacktest.d.ts +179 -0
  542. package/dist/ta/desk/cellBacktest.js +514 -0
  543. package/dist/ta/desk/cellBacktest.js.map +1 -0
  544. package/dist/ta/desk/cells/wpMaRejection.d.ts +49 -0
  545. package/dist/ta/desk/cells/wpMaRejection.js +141 -0
  546. package/dist/ta/desk/cells/wpMaRejection.js.map +1 -0
  547. package/dist/ta/desk/cells/wpVolumeClimax.d.ts +57 -0
  548. package/dist/ta/desk/cells/wpVolumeClimax.js +193 -0
  549. package/dist/ta/desk/cells/wpVolumeClimax.js.map +1 -0
  550. package/dist/ta/desk/collectors.d.ts +18 -0
  551. package/dist/ta/desk/collectors.js +450 -0
  552. package/dist/ta/desk/collectors.js.map +1 -0
  553. package/dist/ta/desk/contract.d.ts +51 -0
  554. package/dist/ta/desk/contract.js +261 -0
  555. package/dist/ta/desk/contract.js.map +1 -0
  556. package/dist/ta/desk/deskLedger.d.ts +36 -0
  557. package/dist/ta/desk/deskLedger.js +239 -0
  558. package/dist/ta/desk/deskLedger.js.map +1 -0
  559. package/dist/ta/desk/liqTape.d.ts +37 -0
  560. package/dist/ta/desk/liqTape.js +99 -0
  561. package/dist/ta/desk/liqTape.js.map +1 -0
  562. package/dist/ta/desk/post.d.ts +20 -0
  563. package/dist/ta/desk/post.js +65 -0
  564. package/dist/ta/desk/post.js.map +1 -0
  565. package/dist/ta/desk/render.d.ts +25 -0
  566. package/dist/ta/desk/render.js +398 -0
  567. package/dist/ta/desk/render.js.map +1 -0
  568. package/dist/ta/desk/surfaces.d.ts +216 -0
  569. package/dist/ta/desk/surfaces.js +472 -0
  570. package/dist/ta/desk/surfaces.js.map +1 -0
  571. package/dist/ta/desk/types.d.ts +174 -0
  572. package/dist/ta/desk/types.js +104 -0
  573. package/dist/ta/desk/types.js.map +1 -0
  574. package/dist/ta/index.d.ts +23 -0
  575. package/dist/ta/index.js +54 -0
  576. package/dist/ta/index.js.map +1 -0
  577. package/dist/ta/ledger.d.ts +44 -0
  578. package/dist/ta/ledger.js +95 -0
  579. package/dist/ta/ledger.js.map +1 -0
  580. package/dist/ta/levels.d.ts +21 -0
  581. package/dist/ta/levels.js +41 -0
  582. package/dist/ta/levels.js.map +1 -0
  583. package/dist/ta/swings.d.ts +30 -0
  584. package/dist/ta/swings.js +83 -0
  585. package/dist/ta/swings.js.map +1 -0
  586. package/dist/ta/types.d.ts +81 -0
  587. package/dist/ta/types.js +20 -0
  588. package/dist/ta/types.js.map +1 -0
  589. package/dist/telemetry.js +20 -9
  590. package/dist/telemetry.js.map +1 -1
  591. package/dist/tokenUsageTracker.d.ts +2 -1
  592. package/dist/tokenUsageTracker.js +38 -20
  593. package/dist/tokenUsageTracker.js.map +1 -1
  594. package/dist/tools/contextBundle.js +13 -9
  595. package/dist/tools/contextBundle.js.map +1 -1
  596. package/dist/tools/ctxGetTaskContext.js +8 -3
  597. package/dist/tools/ctxGetTaskContext.js.map +1 -1
  598. package/dist/tools/ctxQueryTraces.d.ts +18 -1
  599. package/dist/tools/ctxQueryTraces.js +176 -27
  600. package/dist/tools/ctxQueryTraces.js.map +1 -1
  601. package/dist/tools/enrichCommit.js +5 -2
  602. package/dist/tools/enrichCommit.js.map +1 -1
  603. package/dist/tools/getDependencyTree.js +2 -2
  604. package/dist/tools/getDependencyTree.js.map +1 -1
  605. package/dist/tools/getDiagnostics.js +3 -0
  606. package/dist/tools/getDiagnostics.js.map +1 -1
  607. package/dist/tools/getDocumentSymbols.js +5 -1
  608. package/dist/tools/getDocumentSymbols.js.map +1 -1
  609. package/dist/tools/getGitHotspots.js +12 -1
  610. package/dist/tools/getGitHotspots.js.map +1 -1
  611. package/dist/tools/getGitLog.js +5 -2
  612. package/dist/tools/getGitLog.js.map +1 -1
  613. package/dist/tools/getGitStatus.js +6 -1
  614. package/dist/tools/getGitStatus.js.map +1 -1
  615. package/dist/tools/getProjectContext.js +3 -1
  616. package/dist/tools/getProjectContext.js.map +1 -1
  617. package/dist/tools/getToolCapabilities.js +39 -4
  618. package/dist/tools/getToolCapabilities.js.map +1 -1
  619. package/dist/tools/gitWrite.js +12 -3
  620. package/dist/tools/gitWrite.js.map +1 -1
  621. package/dist/tools/github/composite.d.ts +1 -1
  622. package/dist/tools/github/composite.js +7 -7
  623. package/dist/tools/github/composite.js.map +1 -1
  624. package/dist/tools/github/pr.d.ts +6 -6
  625. package/dist/tools/github/pr.js +18 -11
  626. package/dist/tools/github/pr.js.map +1 -1
  627. package/dist/tools/httpClient.js +55 -3
  628. package/dist/tools/httpClient.js.map +1 -1
  629. package/dist/tools/index.d.ts +2 -1
  630. package/dist/tools/index.js +19 -12
  631. package/dist/tools/index.js.map +1 -1
  632. package/dist/tools/lsp.d.ts +6 -0
  633. package/dist/tools/lsp.js +81 -12
  634. package/dist/tools/lsp.js.map +1 -1
  635. package/dist/tools/previewEdit.js +16 -1
  636. package/dist/tools/previewEdit.js.map +1 -1
  637. package/dist/tools/resumeClaudeTask.js +17 -0
  638. package/dist/tools/resumeClaudeTask.js.map +1 -1
  639. package/dist/tools/searchAndReplace.js +9 -5
  640. package/dist/tools/searchAndReplace.js.map +1 -1
  641. package/dist/tools/searchTools.d.ts +26 -0
  642. package/dist/tools/searchTools.js +42 -2
  643. package/dist/tools/searchTools.js.map +1 -1
  644. package/dist/tools/terminal.js +23 -51
  645. package/dist/tools/terminal.js.map +1 -1
  646. package/dist/tools/transaction.d.ts +9 -1
  647. package/dist/tools/transaction.js +59 -4
  648. package/dist/tools/transaction.js.map +1 -1
  649. package/dist/tools/utils.d.ts +16 -0
  650. package/dist/tools/utils.js +77 -7
  651. package/dist/tools/utils.js.map +1 -1
  652. package/dist/transport.d.ts +31 -0
  653. package/dist/transport.js +117 -14
  654. package/dist/transport.js.map +1 -1
  655. package/dist/wireHaltPushDispatch.js +12 -0
  656. package/dist/wireHaltPushDispatch.js.map +1 -1
  657. package/dist/workerGateDecisionLog.d.ts +141 -0
  658. package/dist/workerGateDecisionLog.js +361 -0
  659. package/dist/workerGateDecisionLog.js.map +1 -0
  660. package/dist/workers/actionClass.d.ts +38 -0
  661. package/dist/workers/actionClass.js +155 -0
  662. package/dist/workers/actionClass.js.map +1 -0
  663. package/dist/workers/backtest.d.ts +68 -0
  664. package/dist/workers/backtest.js +113 -0
  665. package/dist/workers/backtest.js.map +1 -0
  666. package/dist/workers/contextRisk.d.ts +35 -0
  667. package/dist/workers/contextRisk.js +20 -0
  668. package/dist/workers/contextRisk.js.map +1 -0
  669. package/dist/workers/contextRiskScorer.d.ts +48 -0
  670. package/dist/workers/contextRiskScorer.js +94 -0
  671. package/dist/workers/contextRiskScorer.js.map +1 -0
  672. package/dist/workers/graduation.d.ts +61 -0
  673. package/dist/workers/graduation.js +112 -0
  674. package/dist/workers/graduation.js.map +1 -0
  675. package/dist/workers/outcomeStore.d.ts +98 -0
  676. package/dist/workers/outcomeStore.js +146 -0
  677. package/dist/workers/outcomeStore.js.map +1 -0
  678. package/dist/workers/outcomesCli.d.ts +34 -0
  679. package/dist/workers/outcomesCli.js +91 -0
  680. package/dist/workers/outcomesCli.js.map +1 -0
  681. package/dist/workers/runWorkerShadow.d.ts +90 -0
  682. package/dist/workers/runWorkerShadow.js +323 -0
  683. package/dist/workers/runWorkerShadow.js.map +1 -0
  684. package/dist/workers/shadowGate.d.ts +30 -0
  685. package/dist/workers/shadowGate.js +58 -0
  686. package/dist/workers/shadowGate.js.map +1 -0
  687. package/dist/workers/shadowObserver.d.ts +165 -0
  688. package/dist/workers/shadowObserver.js +203 -0
  689. package/dist/workers/shadowObserver.js.map +1 -0
  690. package/dist/workers/shadowReport.d.ts +16 -0
  691. package/dist/workers/shadowReport.js +61 -0
  692. package/dist/workers/shadowReport.js.map +1 -0
  693. package/dist/workers/shadowRun.d.ts +38 -0
  694. package/dist/workers/shadowRun.js +44 -0
  695. package/dist/workers/shadowRun.js.map +1 -0
  696. package/dist/workers/trustLevel.d.ts +82 -0
  697. package/dist/workers/trustLevel.js +100 -0
  698. package/dist/workers/trustLevel.js.map +1 -0
  699. package/dist/workers/worker.d.ts +49 -0
  700. package/dist/workers/worker.js +88 -0
  701. package/dist/workers/worker.js.map +1 -0
  702. package/dist/workers/workerGate.d.ts +101 -0
  703. package/dist/workers/workerGate.js +210 -0
  704. package/dist/workers/workerGate.js.map +1 -0
  705. package/dist/workers/workerLevelStore.d.ts +33 -0
  706. package/dist/workers/workerLevelStore.js +87 -0
  707. package/dist/workers/workerLevelStore.js.map +1 -0
  708. package/dist/workers/workerLoader.d.ts +7 -0
  709. package/dist/workers/workerLoader.js +31 -0
  710. package/dist/workers/workerLoader.js.map +1 -0
  711. package/dist/writeFileAtomic.js +40 -13
  712. package/dist/writeFileAtomic.js.map +1 -1
  713. package/package.json +14 -6
  714. package/scripts/mcp-stdio-shim.cjs +247 -87
  715. package/templates/CLAUDE.bridge.md +1 -1
  716. package/templates/recipes/dependency-bump.yaml +33 -0
  717. package/templates/recipes/incident-to-pr.yaml +187 -0
  718. package/templates/recipes/outcome-ingester.yaml +53 -0
  719. package/templates/recipes/release-notes.yaml +30 -0
  720. package/templates/recipes/triage-failing-tests-autofile.yaml +152 -0
  721. package/templates/recipes/triage-failing-tests.yaml +38 -0
  722. package/templates/workers/dependency-upkeep.worker.yaml +26 -0
  723. package/templates/workers/release-notes.worker.yaml +17 -0
  724. package/templates/workers/test-guardian.worker.yaml +24 -0
  725. package/deploy/README.md +0 -172
  726. package/deploy/bootstrap-new-vps.sh +0 -364
  727. package/deploy/bootstrap-vps.sh +0 -192
  728. package/deploy/claude-ide-bridge.service.template +0 -67
  729. package/deploy/claude-ide-bridge@.service +0 -31
  730. package/deploy/deploy-dashboard.sh +0 -198
  731. package/deploy/deploy-landing.sh +0 -136
  732. package/deploy/ecosystem.config.js.example +0 -36
  733. package/deploy/install-vps-service.sh +0 -240
  734. package/deploy/macos/README.md +0 -153
  735. package/deploy/macos/com.patchwork.bridge.plist.template +0 -54
  736. package/deploy/macos/com.patchwork.tunnel.plist.template +0 -76
  737. package/deploy/macos/install-mac-bridge.sh +0 -244
  738. package/deploy/macos/uninstall-mac-bridge.sh +0 -22
  739. package/deploy/nginx-claude-bridge.conf.template +0 -129
@@ -1,3 +1,4 @@
1
+ import { truncateToBytes, truncateUtf8Bytes } from "../outputCap.js";
1
2
  const OUTPUT_CAP = 50 * 1024;
2
3
  /**
3
4
  * OpenAI API driver — streams via chat.completions.create.
@@ -43,6 +44,16 @@ export class OpenAIApiDriver {
43
44
  const opts = input.providerOptions ?? {};
44
45
  const maxTokens = typeof opts.maxTokens === "number" ? opts.maxTokens : 4096;
45
46
  const temperature = typeof opts.temperature === "number" ? opts.temperature : undefined;
47
+ // Structured-output passthrough (audit 2026-06-09): when a caller sets
48
+ // providerOptions.responseFormat (e.g. { type: "json_object" } or
49
+ // { type: "json_schema", json_schema: {...} }), forward it to the API so
50
+ // OpenAI-compatible servers that support constrained decoding (vLLM/XGrammar,
51
+ // llama.cpp, LM Studio) enforce valid structured output at the token level.
52
+ // Endpoints that don't support it simply ignore the field. Foundation for
53
+ // hardening the judge→refine verdict JSON on local models.
54
+ const responseFormat = opts.responseFormat && typeof opts.responseFormat === "object"
55
+ ? opts.responseFormat
56
+ : undefined;
46
57
  const contextNote = input.contextFiles && input.contextFiles.length > 0
47
58
  ? `\n\n--- BEGIN CONTEXT FILE LIST ---\n${input.contextFiles
48
59
  .map((f) => f.slice(0, 500).replace(/[\x00-\x1f\x7f]/g, ""))
@@ -65,7 +76,10 @@ export class OpenAIApiDriver {
65
76
  const model = input.model ?? this.opts.defaultModel ?? "gpt-4o";
66
77
  this.log(`[${this.name}] streaming: model=${model} workspace=${input.workspace}`);
67
78
  let text = "";
79
+ let outputBytesSent = 0;
68
80
  let firstChunkAt;
81
+ let promptTokens;
82
+ let completionTokens;
69
83
  try {
70
84
  // biome-ignore lint/suspicious/noExplicitAny: dynamic import shape
71
85
  const stream = await client.chat.completions.create({
@@ -74,21 +88,60 @@ export class OpenAIApiDriver {
74
88
  ...(temperature !== undefined ? { temperature } : {}),
75
89
  messages,
76
90
  stream: true,
91
+ ...(responseFormat ? { response_format: responseFormat } : {}),
92
+ // Ask the API to emit a final usage chunk (prompt/completion token
93
+ // counts) so RunBudget can enforce a real token/USD budget instead
94
+ // of failing open. Compliant OpenAI-style endpoints return it; an
95
+ // endpoint that ignores the option simply omits usage and we fail
96
+ // open (providerMeta carries no tokens).
97
+ stream_options: { include_usage: true },
77
98
  }, { signal: input.signal });
78
99
  // biome-ignore lint/suspicious/noExplicitAny: stream shape from dynamic import
79
100
  for await (const chunk of stream) {
101
+ // The include_usage final chunk carries an empty `choices` array and a
102
+ // `usage` object; capture it whenever present (so it works even if an
103
+ // endpoint attaches usage to a content-bearing chunk).
104
+ if (chunk.usage) {
105
+ if (typeof chunk.usage.prompt_tokens === "number")
106
+ promptTokens = chunk.usage.prompt_tokens;
107
+ if (typeof chunk.usage.completion_tokens === "number")
108
+ completionTokens = chunk.usage.completion_tokens;
109
+ }
80
110
  const delta = chunk.choices?.[0]?.delta?.content ?? "";
81
111
  if (delta) {
82
112
  if (firstChunkAt === undefined)
83
113
  firstChunkAt = Date.now();
84
- if (text.length < OUTPUT_CAP) {
114
+ if (outputBytesSent < OUTPUT_CAP) {
85
115
  // Cap accumulation in-loop to bound memory under runaway streams
86
116
  // (e.g. local LLMs that loop). Once we hit the cap, stop appending
87
117
  // and abort the upstream stream so sockets and decoder buffers
88
- // are released. onChunk is gated by the same cap so partial
89
- // listeners stop receiving deltas.
90
- text += delta;
91
- input.onChunk?.(delta);
118
+ // are released.
119
+ //
120
+ // LOW #19 audit 2026-06-03: use truncateToBytes so the chunk sent
121
+ // to onChunk is always the SAME bytes as what lands in result.text.
122
+ // The previous pattern appended the full `delta` to `text` then
123
+ // called `onChunk(delta)`, but `truncateUtf8Bytes(text, OUTPUT_CAP)`
124
+ // at the end could cut the last delta — so onChunk sum > result.text.
125
+ const remaining = OUTPUT_CAP - outputBytesSent;
126
+ const { send, bytes } = truncateToBytes(delta, remaining);
127
+ text += send;
128
+ outputBytesSent += bytes;
129
+ if (send)
130
+ input.onChunk?.(send);
131
+ if (bytes < Buffer.byteLength(delta, "utf8")) {
132
+ // We've hit the cap mid-delta — abort the stream.
133
+ // biome-ignore lint/suspicious/noExplicitAny: stream shape
134
+ const ctrl = stream.controller;
135
+ if (ctrl && typeof ctrl.abort === "function") {
136
+ try {
137
+ ctrl.abort();
138
+ }
139
+ catch {
140
+ /* best effort */
141
+ }
142
+ }
143
+ break;
144
+ }
92
145
  }
93
146
  else {
94
147
  // biome-ignore lint/suspicious/noExplicitAny: stream shape
@@ -111,23 +164,30 @@ export class OpenAIApiDriver {
111
164
  input.signal.aborted;
112
165
  if (isAbort) {
113
166
  return {
114
- text: text.slice(0, OUTPUT_CAP),
167
+ text: truncateUtf8Bytes(text, OUTPUT_CAP),
115
168
  durationMs: Date.now() - start,
116
169
  wasAborted: true,
117
170
  startupMs: firstChunkAt !== undefined ? firstChunkAt - start : undefined,
118
171
  };
119
172
  }
120
173
  return {
121
- text: text.slice(0, OUTPUT_CAP),
174
+ text: truncateUtf8Bytes(text, OUTPUT_CAP),
122
175
  durationMs: Date.now() - start,
123
176
  errorMessage: err instanceof Error ? err.message : String(err),
124
177
  };
125
178
  }
126
179
  return {
127
- text: text.slice(0, OUTPUT_CAP),
180
+ text: truncateUtf8Bytes(text, OUTPUT_CAP),
128
181
  durationMs: Date.now() - start,
129
182
  startupMs: firstChunkAt !== undefined ? firstChunkAt - start : undefined,
130
- providerMeta: { model },
183
+ providerMeta: {
184
+ model,
185
+ // Present only when the endpoint returned a usage chunk. Both fields
186
+ // together or neither — a half-populated count would mislead budgets.
187
+ ...(promptTokens !== undefined && completionTokens !== undefined
188
+ ? { inputTokens: promptTokens, outputTokens: completionTokens }
189
+ : {}),
190
+ },
131
191
  };
132
192
  }
133
193
  /** Override in subclasses to use a different env var. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/drivers/openai/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/drivers/openai/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared UTF-8-aware output truncation for driver results.
3
+ *
4
+ * Audit 2026-06-03 (MEDIUM #30): the API drivers (claude/api, openai, gemini)
5
+ * capped output with `text.slice(0, OUTPUT_CAP)`, which counts UTF-16 code
6
+ * units — so a 50K "byte" cap could emit ~200KB of UTF-8 wire bytes for CJK /
7
+ * emoji content and could leave a lone surrogate at the boundary. These
8
+ * helpers (previously private to claude/subprocess.ts) measure and cut by
9
+ * UTF-8 bytes. Shared so every driver path uses one implementation.
10
+ */
11
+ /**
12
+ * Truncate `text` to at most `cap` UTF-8 bytes without splitting a multi-byte
13
+ * codepoint. `String.slice(0, cap)` counts UTF-16 code units, so a 50K cap can
14
+ * emit ~200KB of UTF-8 wire bytes for CJK / emoji content; this cuts by bytes
15
+ * at a codepoint boundary.
16
+ */
17
+ export declare function truncateUtf8Bytes(text: string, cap: number): string;
18
+ /**
19
+ * Truncate `text` so it adds at most `remaining` UTF-8 bytes to a stream and
20
+ * never splits a multi-byte codepoint. Returns the safely-truncated slice plus
21
+ * its actual byte length so a running byte total can be advanced correctly
22
+ * (used by the streaming accumulators).
23
+ */
24
+ export declare function truncateToBytes(text: string, remaining: number): {
25
+ send: string;
26
+ bytes: number;
27
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Shared UTF-8-aware output truncation for driver results.
3
+ *
4
+ * Audit 2026-06-03 (MEDIUM #30): the API drivers (claude/api, openai, gemini)
5
+ * capped output with `text.slice(0, OUTPUT_CAP)`, which counts UTF-16 code
6
+ * units — so a 50K "byte" cap could emit ~200KB of UTF-8 wire bytes for CJK /
7
+ * emoji content and could leave a lone surrogate at the boundary. These
8
+ * helpers (previously private to claude/subprocess.ts) measure and cut by
9
+ * UTF-8 bytes. Shared so every driver path uses one implementation.
10
+ */
11
+ /**
12
+ * Largest byte length ≤ `cap` that ends on a UTF-8 codepoint boundary. UTF-8
13
+ * continuation bytes match `0b10xxxxxx`; if the byte at the cut is one, we're
14
+ * mid-sequence, so back off to the preceding lead byte. This drops a partial
15
+ * trailing codepoint cleanly (no U+FFFD, no lone surrogate, never over `cap`).
16
+ */
17
+ function codepointBoundary(buf, cap) {
18
+ let end = cap;
19
+ while (end > 0 && (buf[end] & 0xc0) === 0x80)
20
+ end--;
21
+ return end;
22
+ }
23
+ /**
24
+ * Truncate `text` to at most `cap` UTF-8 bytes without splitting a multi-byte
25
+ * codepoint. `String.slice(0, cap)` counts UTF-16 code units, so a 50K cap can
26
+ * emit ~200KB of UTF-8 wire bytes for CJK / emoji content; this cuts by bytes
27
+ * at a codepoint boundary.
28
+ */
29
+ export function truncateUtf8Bytes(text, cap) {
30
+ const buf = Buffer.from(text, "utf8");
31
+ if (buf.length <= cap)
32
+ return text;
33
+ return buf.subarray(0, codepointBoundary(buf, cap)).toString("utf8");
34
+ }
35
+ /**
36
+ * Truncate `text` so it adds at most `remaining` UTF-8 bytes to a stream and
37
+ * never splits a multi-byte codepoint. Returns the safely-truncated slice plus
38
+ * its actual byte length so a running byte total can be advanced correctly
39
+ * (used by the streaming accumulators).
40
+ */
41
+ export function truncateToBytes(text, remaining) {
42
+ const buf = Buffer.from(text, "utf8");
43
+ if (buf.length <= remaining)
44
+ return { send: text, bytes: buf.length };
45
+ const send = buf
46
+ .subarray(0, codepointBoundary(buf, remaining))
47
+ .toString("utf8");
48
+ return { send, bytes: Buffer.byteLength(send, "utf8") };
49
+ }
50
+ //# sourceMappingURL=outputCap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outputCap.js","sourceRoot":"","sources":["../../src/drivers/outputCap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,GAAW,EAAE,GAAW;IACjD,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,OAAO,GAAG,GAAG,CAAC,IAAI,CAAE,GAAG,CAAC,GAAG,CAAY,GAAG,IAAI,CAAC,KAAK,IAAI;QAAE,GAAG,EAAE,CAAC;IAChE,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,GAAW;IACzD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACvE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,SAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,IAAI,GAAG,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IACtE,MAAM,IAAI,GAAG,GAAG;SACb,QAAQ,CAAC,CAAC,EAAE,iBAAiB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;SAC9C,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Pure, zero-dependency vector math. No imports — keep it that way so it can
3
+ * be reused anywhere (ranking, dedup, semantic search) without pulling in
4
+ * network / config / driver dependencies.
5
+ */
6
+ /**
7
+ * Cosine similarity = dot(a, b) / (|a| * |b|).
8
+ *
9
+ * Returns `0` (never `NaN`, never throws) on:
10
+ * - length mismatch between `a` and `b`
11
+ * - either vector being zero-magnitude
12
+ * - empty vectors
13
+ *
14
+ * Identical vectors → 1, orthogonal → 0, opposite → -1.
15
+ */
16
+ export declare function cosineSimilarity(a: number[], b: number[]): number;
17
+ /**
18
+ * Return the `k` highest-scoring items, sorted score-descending. Stable for
19
+ * ties — items with equal score keep their original relative order.
20
+ *
21
+ * `k` larger than the list returns all items (sorted). `k <= 0` returns `[]`.
22
+ */
23
+ export declare function topK<T>(items: T[], scoreOf: (item: T) => number, k: number): T[];
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Pure, zero-dependency vector math. No imports — keep it that way so it can
3
+ * be reused anywhere (ranking, dedup, semantic search) without pulling in
4
+ * network / config / driver dependencies.
5
+ */
6
+ /**
7
+ * Cosine similarity = dot(a, b) / (|a| * |b|).
8
+ *
9
+ * Returns `0` (never `NaN`, never throws) on:
10
+ * - length mismatch between `a` and `b`
11
+ * - either vector being zero-magnitude
12
+ * - empty vectors
13
+ *
14
+ * Identical vectors → 1, orthogonal → 0, opposite → -1.
15
+ */
16
+ export function cosineSimilarity(a, b) {
17
+ if (a.length !== b.length || a.length === 0)
18
+ return 0;
19
+ let dot = 0;
20
+ let magA = 0;
21
+ let magB = 0;
22
+ for (let i = 0; i < a.length; i++) {
23
+ // Bounds-safe: i < a.length === b.length (checked above).
24
+ const av = a[i];
25
+ const bv = b[i];
26
+ dot += av * bv;
27
+ magA += av * av;
28
+ magB += bv * bv;
29
+ }
30
+ if (magA === 0 || magB === 0)
31
+ return 0;
32
+ const denom = Math.sqrt(magA) * Math.sqrt(magB);
33
+ if (denom === 0)
34
+ return 0;
35
+ return dot / denom;
36
+ }
37
+ /**
38
+ * Return the `k` highest-scoring items, sorted score-descending. Stable for
39
+ * ties — items with equal score keep their original relative order.
40
+ *
41
+ * `k` larger than the list returns all items (sorted). `k <= 0` returns `[]`.
42
+ */
43
+ export function topK(items, scoreOf, k) {
44
+ if (k <= 0)
45
+ return [];
46
+ // Decorate with original index to make the sort stable across ties.
47
+ const decorated = items.map((item, index) => ({
48
+ item,
49
+ index,
50
+ score: scoreOf(item),
51
+ }));
52
+ decorated.sort((x, y) => {
53
+ if (y.score !== x.score)
54
+ return y.score - x.score;
55
+ return x.index - y.index;
56
+ });
57
+ return decorated.slice(0, k).map((d) => d.item);
58
+ }
59
+ //# sourceMappingURL=cosine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cosine.js","sourceRoot":"","sources":["../../src/embeddings/cosine.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAW,EAAE,CAAW;IACvD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACtD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,0DAA0D;QAC1D,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAW,CAAC;QAC1B,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAW,CAAC;QAC1B,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC;QACf,IAAI,IAAI,EAAE,GAAG,EAAE,CAAC;QAChB,IAAI,IAAI,EAAE,GAAG,EAAE,CAAC;IAClB,CAAC;IACD,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1B,OAAO,GAAG,GAAG,KAAK,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,IAAI,CAClB,KAAU,EACV,OAA4B,EAC5B,CAAS;IAET,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACtB,oEAAoE;IACpE,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5C,IAAI;QACJ,KAAK;QACL,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC;KACrB,CAAC,CAAC,CAAC;IACJ,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;YAAE,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;QAClD,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;IAC3B,CAAC,CAAC,CAAC;IACH,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { EmbeddingsProvider, EmbeddingsProviderOpts } from "./types.js";
2
+ export { cosineSimilarity, topK } from "./cosine.js";
3
+ export { LocalEmbeddingsProvider } from "./localEmbeddings.js";
4
+ export type { EmbeddingsProvider, EmbeddingsProviderOpts } from "./types.js";
5
+ /**
6
+ * Build an embeddings provider, or `null` when no endpoint is configured
7
+ * (neither `opts.endpoint` nor `LOCAL_EMBEDDINGS_ENDPOINT` nor
8
+ * `LOCAL_ENDPOINT`). Returning `null` lets callers fail soft.
9
+ */
10
+ export declare function createEmbeddingsProvider(opts?: EmbeddingsProviderOpts): EmbeddingsProvider | null;
11
+ /**
12
+ * Resolve a bound embed function for the configured local provider, or
13
+ * `undefined` when embeddings are unconfigured. Wired into tool registration
14
+ * sites — `createCtxQueryTracesTool({ ..., embedFn: getLocalEmbedFn() })` — so
15
+ * the opt-in semantic ranking path activates only when a local endpoint is set
16
+ * and stays byte-identical to substring search otherwise. Binds `embed` so
17
+ * callers can pass the function value directly without losing `this`.
18
+ */
19
+ export declare function getLocalEmbedFn(opts?: EmbeddingsProviderOpts): ((texts: string[]) => Promise<number[][] | null>) | undefined;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Embeddings module — internal, NOT an MCP tool (no outputSchema, not
3
+ * registered in src/tools/index.ts).
4
+ *
5
+ * The factory is the fail-soft hinge: {@link createEmbeddingsProvider} returns
6
+ * `null` when no endpoint is configured at all, so every consumer can detect
7
+ * "embeddings unavailable" and fall back to existing behavior.
8
+ */
9
+ import { LocalEmbeddingsProvider } from "./localEmbeddings.js";
10
+ import { resolveEmbeddingsEndpoint } from "./localEndpoints.js";
11
+ export { cosineSimilarity, topK } from "./cosine.js";
12
+ export { LocalEmbeddingsProvider } from "./localEmbeddings.js";
13
+ /**
14
+ * Build an embeddings provider, or `null` when no endpoint is configured
15
+ * (neither `opts.endpoint` nor `LOCAL_EMBEDDINGS_ENDPOINT` nor
16
+ * `LOCAL_ENDPOINT`). Returning `null` lets callers fail soft.
17
+ */
18
+ export function createEmbeddingsProvider(opts = {}) {
19
+ if (!resolveEmbeddingsEndpoint(opts))
20
+ return null;
21
+ return new LocalEmbeddingsProvider(opts);
22
+ }
23
+ /**
24
+ * Resolve a bound embed function for the configured local provider, or
25
+ * `undefined` when embeddings are unconfigured. Wired into tool registration
26
+ * sites — `createCtxQueryTracesTool({ ..., embedFn: getLocalEmbedFn() })` — so
27
+ * the opt-in semantic ranking path activates only when a local endpoint is set
28
+ * and stays byte-identical to substring search otherwise. Binds `embed` so
29
+ * callers can pass the function value directly without losing `this`.
30
+ */
31
+ export function getLocalEmbedFn(opts = {}) {
32
+ const provider = createEmbeddingsProvider(opts);
33
+ return provider ? (texts) => provider.embed(texts) : undefined;
34
+ }
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAG/D;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAA+B,EAAE;IAEjC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,OAA+B,EAAE;IAEjC,MAAM,QAAQ,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAe,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { EmbeddingsProvider, EmbeddingsProviderOpts } from "./types.js";
2
+ /**
3
+ * OpenAI-compatible embeddings client (Ollama / MLX / mlx-embeddings / vLLM).
4
+ *
5
+ * POSTs `{ model, input }` to `${endpoint}/embeddings` and parses the OpenAI
6
+ * shape `{ data: [{ embedding: number[] }, ...] }`.
7
+ *
8
+ * Fail-soft contract — `embed()` returns `null` (never throws) on:
9
+ * - a public/non-loopback endpoint when LOCAL_ENDPOINT_ALLOW_REMOTE !== "1"
10
+ * (anti-exfiltration — the texts we send are prompts/context)
11
+ * - non-ok HTTP status
12
+ * - JSON parse / unexpected shape
13
+ * - any network throw inside fetch
14
+ *
15
+ * Configuration precedence (see {@link resolveEmbeddingsEndpoint} /
16
+ * {@link resolveEmbeddingsModel}):
17
+ * endpoint: opts.endpoint ?? LOCAL_EMBEDDINGS_ENDPOINT ?? LOCAL_ENDPOINT
18
+ * model: opts.model ?? LOCAL_EMBEDDINGS_MODEL ?? LOCAL_MODEL ?? nomic-embed-text
19
+ * apiKey: opts.apiKey ?? LOCAL_API_KEY ?? "ollama"
20
+ */
21
+ export declare class LocalEmbeddingsProvider implements EmbeddingsProvider {
22
+ private readonly endpoint;
23
+ private readonly model;
24
+ private readonly apiKey;
25
+ private readonly fetchImpl;
26
+ private readonly log;
27
+ constructor(opts?: EmbeddingsProviderOpts);
28
+ embed(texts: string[]): Promise<number[][] | null>;
29
+ }
@@ -0,0 +1,103 @@
1
+ import { isLoopbackOrPrivateEndpoint } from "../localEndpointGuard.js";
2
+ import { resolveEmbeddingsEndpoint, resolveEmbeddingsModel, } from "./localEndpoints.js";
3
+ /**
4
+ * OpenAI-compatible embeddings client (Ollama / MLX / mlx-embeddings / vLLM).
5
+ *
6
+ * POSTs `{ model, input }` to `${endpoint}/embeddings` and parses the OpenAI
7
+ * shape `{ data: [{ embedding: number[] }, ...] }`.
8
+ *
9
+ * Fail-soft contract — `embed()` returns `null` (never throws) on:
10
+ * - a public/non-loopback endpoint when LOCAL_ENDPOINT_ALLOW_REMOTE !== "1"
11
+ * (anti-exfiltration — the texts we send are prompts/context)
12
+ * - non-ok HTTP status
13
+ * - JSON parse / unexpected shape
14
+ * - any network throw inside fetch
15
+ *
16
+ * Configuration precedence (see {@link resolveEmbeddingsEndpoint} /
17
+ * {@link resolveEmbeddingsModel}):
18
+ * endpoint: opts.endpoint ?? LOCAL_EMBEDDINGS_ENDPOINT ?? LOCAL_ENDPOINT
19
+ * model: opts.model ?? LOCAL_EMBEDDINGS_MODEL ?? LOCAL_MODEL ?? nomic-embed-text
20
+ * apiKey: opts.apiKey ?? LOCAL_API_KEY ?? "ollama"
21
+ */
22
+ export class LocalEmbeddingsProvider {
23
+ endpoint;
24
+ model;
25
+ apiKey;
26
+ fetchImpl;
27
+ log;
28
+ constructor(opts = {}) {
29
+ this.endpoint = resolveEmbeddingsEndpoint(opts);
30
+ this.model = resolveEmbeddingsModel(opts);
31
+ this.apiKey = opts.apiKey ?? process.env.LOCAL_API_KEY ?? "ollama";
32
+ this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;
33
+ this.log = opts.log ?? (() => { });
34
+ }
35
+ async embed(texts) {
36
+ const endpoint = this.endpoint;
37
+ if (!endpoint) {
38
+ this.log("embeddings: no endpoint configured — returning null");
39
+ return null;
40
+ }
41
+ // Anti-exfiltration guard. The texts we POST are prompts/context — a
42
+ // public host would leak them. Mirror src/drivers/local/index.ts, but
43
+ // fail SOFT (return null) instead of throwing.
44
+ if (process.env.LOCAL_ENDPOINT_ALLOW_REMOTE !== "1" &&
45
+ !isLoopbackOrPrivateEndpoint(endpoint)) {
46
+ this.log(`embeddings: endpoint="${endpoint}" is not loopback/private — refusing ` +
47
+ `to send prompt/context (set LOCAL_ENDPOINT_ALLOW_REMOTE=1 to override)`);
48
+ return null;
49
+ }
50
+ if (texts.length === 0)
51
+ return [];
52
+ const url = `${endpoint.replace(/\/$/, "")}/embeddings`;
53
+ try {
54
+ const res = await this.fetchImpl(url, {
55
+ method: "POST",
56
+ headers: {
57
+ "Content-Type": "application/json",
58
+ Authorization: `Bearer ${this.apiKey}`,
59
+ },
60
+ body: JSON.stringify({ model: this.model, input: texts }),
61
+ });
62
+ if (!res.ok) {
63
+ this.log(`embeddings: non-ok status ${res.status} from ${url}`);
64
+ return null;
65
+ }
66
+ const body = (await res.json());
67
+ const vectors = parseEmbeddings(body);
68
+ if (!vectors) {
69
+ this.log(`embeddings: unexpected response shape from ${url}`);
70
+ return null;
71
+ }
72
+ return vectors;
73
+ }
74
+ catch (err) {
75
+ this.log(`embeddings: request failed — ${err instanceof Error ? err.message : String(err)}`);
76
+ return null;
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Parse the OpenAI embeddings response shape `{ data: [{ embedding: [...] }] }`.
82
+ * Returns `null` on any structural mismatch (so the caller can fail soft).
83
+ */
84
+ function parseEmbeddings(body) {
85
+ if (typeof body !== "object" || body === null)
86
+ return null;
87
+ const data = body.data;
88
+ if (!Array.isArray(data))
89
+ return null;
90
+ const out = [];
91
+ for (const row of data) {
92
+ if (typeof row !== "object" || row === null)
93
+ return null;
94
+ const embedding = row.embedding;
95
+ if (!Array.isArray(embedding))
96
+ return null;
97
+ if (!embedding.every((n) => typeof n === "number"))
98
+ return null;
99
+ out.push(embedding);
100
+ }
101
+ return out;
102
+ }
103
+ //# sourceMappingURL=localEmbeddings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localEmbeddings.js","sourceRoot":"","sources":["../../src/embeddings/localEmbeddings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared endpoint/model precedence resolution for the embeddings module.
3
+ * Kept tiny + import-free (besides env reads) so both the factory
4
+ * (`index.ts`) and the provider (`localEmbeddings.ts`) agree on exactly the
5
+ * same precedence chain.
6
+ */
7
+ import type { EmbeddingsProviderOpts } from "./types.js";
8
+ /**
9
+ * Resolve the embeddings endpoint, honoring (in order):
10
+ * 1. `opts.endpoint`
11
+ * 2. `LOCAL_EMBEDDINGS_ENDPOINT` env
12
+ * 3. `LOCAL_ENDPOINT` env (reuse the chat endpoint when a dedicated
13
+ * embeddings endpoint isn't configured)
14
+ * Returns `undefined` when none is set — the fail-soft hinge.
15
+ */
16
+ export declare function resolveEmbeddingsEndpoint(opts?: EmbeddingsProviderOpts): string | undefined;
17
+ /**
18
+ * Resolve the embeddings model, honoring (in order):
19
+ * 1. `opts.model`
20
+ * 2. `LOCAL_EMBEDDINGS_MODEL` env
21
+ * 3. `LOCAL_MODEL` env
22
+ * 4. `nomic-embed-text` (sensible Ollama default)
23
+ */
24
+ export declare function resolveEmbeddingsModel(opts?: EmbeddingsProviderOpts): string;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Treat empty / whitespace-only values as "unset" so the precedence chain is
3
+ * not shadowed by a stale `export LOCAL_EMBEDDINGS_ENDPOINT=` or a blank
4
+ * dashboard field — `??` alone would let `""` win over a real fallback. Also
5
+ * trims, avoiding `http://host /embeddings`-style trailing-space URL bugs.
6
+ */
7
+ function clean(v) {
8
+ const t = v?.trim();
9
+ return t ? t : undefined;
10
+ }
11
+ /**
12
+ * Resolve the embeddings endpoint, honoring (in order):
13
+ * 1. `opts.endpoint`
14
+ * 2. `LOCAL_EMBEDDINGS_ENDPOINT` env
15
+ * 3. `LOCAL_ENDPOINT` env (reuse the chat endpoint when a dedicated
16
+ * embeddings endpoint isn't configured)
17
+ * Returns `undefined` when none is set — the fail-soft hinge.
18
+ */
19
+ export function resolveEmbeddingsEndpoint(opts) {
20
+ return (clean(opts?.endpoint) ??
21
+ clean(process.env.LOCAL_EMBEDDINGS_ENDPOINT) ??
22
+ clean(process.env.LOCAL_ENDPOINT));
23
+ }
24
+ /**
25
+ * Resolve the embeddings model, honoring (in order):
26
+ * 1. `opts.model`
27
+ * 2. `LOCAL_EMBEDDINGS_MODEL` env
28
+ * 3. `LOCAL_MODEL` env
29
+ * 4. `nomic-embed-text` (sensible Ollama default)
30
+ */
31
+ export function resolveEmbeddingsModel(opts) {
32
+ return (clean(opts?.model) ??
33
+ clean(process.env.LOCAL_EMBEDDINGS_MODEL) ??
34
+ clean(process.env.LOCAL_MODEL) ??
35
+ "nomic-embed-text");
36
+ }
37
+ //# sourceMappingURL=localEndpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localEndpoints.js","sourceRoot":"","sources":["../../src/embeddings/localEndpoints.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,SAAS,KAAK,CAAC,CAAqB;IAClC,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;IACpB,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CACvC,IAA6B;IAE7B,OAAO,CACL,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC;QACrB,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;QAC5C,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAClC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA6B;IAClE,OAAO,CACL,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;QAClB,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;QACzC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QAC9B,kBAAkB,CACnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Internal embeddings module — NOT an MCP tool.
3
+ *
4
+ * Turns text into vectors via an OpenAI-compatible /v1/embeddings endpoint
5
+ * (Ollama, MLX, mlx-embeddings, vLLM, llama.cpp, LM Studio, etc.). Every
6
+ * consumer is expected to fail soft: the factory returns `null` when no
7
+ * endpoint is configured, and `embed()` returns `null` on any failure
8
+ * (network / parse / non-ok / SSRF-rejected) rather than throwing — mirroring
9
+ * the driver `run()`-never-rejects convention.
10
+ */
11
+ /** A provider that turns text into embedding vectors. */
12
+ export interface EmbeddingsProvider {
13
+ /**
14
+ * Embed a batch of texts. Returns one vector per input (in order), or `null`
15
+ * on ANY failure (network error, non-ok HTTP status, parse error,
16
+ * SSRF-rejected endpoint). Never throws.
17
+ */
18
+ embed(texts: string[]): Promise<number[][] | null>;
19
+ }
20
+ /** Construction options for a {@link EmbeddingsProvider}. */
21
+ export interface EmbeddingsProviderOpts {
22
+ /** OpenAI-compatible base URL, e.g. `http://localhost:11434/v1`. */
23
+ endpoint?: string;
24
+ /** Model id, e.g. `nomic-embed-text`. */
25
+ model?: string;
26
+ /** Bearer token for `Authorization`. Defaults to the `ollama` placeholder. */
27
+ apiKey?: string;
28
+ /** Injected fetch (for tests). Defaults to `globalThis.fetch`. */
29
+ fetchImpl?: typeof fetch;
30
+ /** Optional logger for fail-soft diagnostics. */
31
+ log?: (msg: string) => void;
32
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Internal embeddings module — NOT an MCP tool.
3
+ *
4
+ * Turns text into vectors via an OpenAI-compatible /v1/embeddings endpoint
5
+ * (Ollama, MLX, mlx-embeddings, vLLM, llama.cpp, LM Studio, etc.). Every
6
+ * consumer is expected to fail soft: the factory returns `null` when no
7
+ * endpoint is configured, and `embed()` returns `null` on any failure
8
+ * (network / parse / non-ok / SSRF-rejected) rather than throwing — mirroring
9
+ * the driver `run()`-never-rejects convention.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=types.js.map