flujo-ai 3.22.0 → 3.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +133 -133
  3. package/.next/app-path-routes-manifest.json +6 -6
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/prerender-manifest.json +12 -12
  6. package/.next/react-loadable-manifest.json +6 -6
  7. package/.next/server/app/_not-found/page.js +1 -1
  8. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/_not-found.html +1 -1
  10. package/.next/server/app/_not-found.rsc +1 -1
  11. package/.next/server/app/api/approvals/[id]/route.js +1 -1
  12. package/.next/server/app/api/approvals/[id]/route.js.nft.json +1 -1
  13. package/.next/server/app/api/approvals/[id]/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/approvals/route.js.nft.json +1 -1
  15. package/.next/server/app/api/approvals/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/backup/route.js +1 -1
  17. package/.next/server/app/api/backup/route.js.nft.json +1 -1
  18. package/.next/server/app/api/backup/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/browse/route.js +1 -1
  20. package/.next/server/app/api/browse/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/bugs/enhance/route.js.nft.json +1 -1
  22. package/.next/server/app/api/bugs/enhance/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/cwd/route.js +1 -1
  24. package/.next/server/app/api/cwd/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/encryption/secure/route.js.nft.json +1 -1
  26. package/.next/server/app/api/encryption/secure/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/env/route_client-reference-manifest.js +1 -1
  28. package/.next/server/app/api/flow/[id]/route.js.nft.json +1 -1
  29. package/.next/server/app/api/flow/[id]/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/flow/compile/route.js.nft.json +1 -1
  31. package/.next/server/app/api/flow/compile/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/flow/generate/route.js.nft.json +1 -1
  33. package/.next/server/app/api/flow/generate/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/flow/improve/route.js.nft.json +1 -1
  35. package/.next/server/app/api/flow/improve/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/flow/prompt-renderer/route.js.nft.json +1 -1
  37. package/.next/server/app/api/flow/prompt-renderer/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/flow/quick-chat/route.js.nft.json +1 -1
  39. package/.next/server/app/api/flow/quick-chat/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/flow/repair/route.js.nft.json +1 -1
  41. package/.next/server/app/api/flow/repair/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/flow/route.js.nft.json +1 -1
  43. package/.next/server/app/api/flow/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/git/route.js +5 -5
  45. package/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/init/route.js.nft.json +1 -1
  47. package/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/local-models/capability/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/local-models/pull/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/local-models/suggest/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/mcp/cancel/route.js.nft.json +1 -1
  52. package/.next/server/app/api/mcp/cancel/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/mcp/servers/[name]/prompts/get/route.js.nft.json +1 -1
  54. package/.next/server/app/api/mcp/servers/[name]/prompts/get/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/mcp/servers/[name]/prompts/route.js.nft.json +1 -1
  56. package/.next/server/app/api/mcp/servers/[name]/prompts/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/mcp/servers/[name]/resources/read/route.js.nft.json +1 -1
  58. package/.next/server/app/api/mcp/servers/[name]/resources/read/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/mcp/servers/[name]/resources/route.js.nft.json +1 -1
  60. package/.next/server/app/api/mcp/servers/[name]/resources/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/mcp/servers/[name]/route.js.nft.json +1 -1
  62. package/.next/server/app/api/mcp/servers/[name]/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/mcp/servers/[name]/status/route.js.nft.json +1 -1
  64. package/.next/server/app/api/mcp/servers/[name]/status/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/mcp/servers/[name]/tools/[toolName]/route.js.nft.json +1 -1
  66. package/.next/server/app/api/mcp/servers/[name]/tools/[toolName]/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/mcp/servers/[name]/tools/route.js.nft.json +1 -1
  68. package/.next/server/app/api/mcp/servers/[name]/tools/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/mcp/servers/route.js.nft.json +1 -1
  70. package/.next/server/app/api/mcp/servers/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/mcp/test-connection/route.js +1 -1
  72. package/.next/server/app/api/mcp/test-connection/route.js.nft.json +1 -1
  73. package/.next/server/app/api/mcp/test-connection/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/mcp/test-connection/stream/route.js +1 -1
  75. package/.next/server/app/api/mcp/test-connection/stream/route.js.nft.json +1 -1
  76. package/.next/server/app/api/mcp/test-connection/stream/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/mcp-registry/route.js.nft.json +1 -1
  78. package/.next/server/app/api/mcp-registry/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/mcp-registry/spotlight/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/model/[id]/route.js.nft.json +1 -1
  81. package/.next/server/app/api/model/[id]/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/model/provider/route.js.nft.json +1 -1
  83. package/.next/server/app/api/model/provider/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/model/route.js.nft.json +1 -1
  85. package/.next/server/app/api/model/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/model/test/route.js.nft.json +1 -1
  87. package/.next/server/app/api/model/test/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/oauth/callback/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/oauth/initiate/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/oauth/reset/route.js.nft.json +1 -1
  91. package/.next/server/app/api/oauth/reset/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/api/planned-executions/[id]/route.js.nft.json +1 -1
  93. package/.next/server/app/api/planned-executions/[id]/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/planned-executions/[id]/run/route.js.nft.json +1 -1
  95. package/.next/server/app/api/planned-executions/[id]/run/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/planned-executions/[id]/runs/route.js.nft.json +1 -1
  97. package/.next/server/app/api/planned-executions/[id]/runs/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/planned-executions/preview-schedule/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/planned-executions/reconcile/route.js.nft.json +1 -1
  100. package/.next/server/app/api/planned-executions/reconcile/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/planned-executions/route.js.nft.json +1 -1
  102. package/.next/server/app/api/planned-executions/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/restore/route.js +1 -1
  104. package/.next/server/app/api/restore/route.js.nft.json +1 -1
  105. package/.next/server/app/api/restore/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/runs/active/route.js.nft.json +1 -1
  107. package/.next/server/app/api/runs/active/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/storage/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/update/route.js +1 -1
  110. package/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/waves/route.js.nft.json +1 -1
  112. package/.next/server/app/api/waves/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/webhooks/[id]/route.js +1 -1
  114. package/.next/server/app/api/webhooks/[id]/route.js.nft.json +1 -1
  115. package/.next/server/app/api/webhooks/[id]/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/chat/page.js +8 -8
  117. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  118. package/.next/server/app/chat.html +1 -1
  119. package/.next/server/app/chat.rsc +2 -2
  120. package/.next/server/app/docs/page.js +8 -2
  121. package/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  122. package/.next/server/app/docs.html +1 -1
  123. package/.next/server/app/docs.rsc +2 -2
  124. package/.next/server/app/executions/page_client-reference-manifest.js +1 -1
  125. package/.next/server/app/executions.html +1 -1
  126. package/.next/server/app/executions.rsc +2 -2
  127. package/.next/server/app/flows/page.js +3 -3
  128. package/.next/server/app/flows/page_client-reference-manifest.js +1 -1
  129. package/.next/server/app/flows.html +1 -1
  130. package/.next/server/app/flows.rsc +2 -2
  131. package/.next/server/app/index.html +1 -1
  132. package/.next/server/app/index.rsc +2 -2
  133. package/.next/server/app/mcp/page.js +5 -5
  134. package/.next/server/app/mcp/page_client-reference-manifest.js +1 -1
  135. package/.next/server/app/mcp-flows/route.js +1 -1
  136. package/.next/server/app/mcp-flows/route.js.nft.json +1 -1
  137. package/.next/server/app/mcp-flows/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/mcp-proxy/[server]/route.js +1 -1
  139. package/.next/server/app/mcp-proxy/[server]/route.js.nft.json +1 -1
  140. package/.next/server/app/mcp-proxy/[server]/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/mcp.html +1 -1
  142. package/.next/server/app/mcp.rsc +2 -2
  143. package/.next/server/app/models/page.js +1 -1
  144. package/.next/server/app/models/page.js.nft.json +1 -1
  145. package/.next/server/app/models/page_client-reference-manifest.js +1 -1
  146. package/.next/server/app/page.js +1 -1
  147. package/.next/server/app/page_client-reference-manifest.js +1 -1
  148. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  149. package/.next/server/app/settings.html +1 -1
  150. package/.next/server/app/settings.rsc +2 -2
  151. package/.next/server/app/v1/chat/completions/route.js +1 -1
  152. package/.next/server/app/v1/chat/completions/route.js.nft.json +1 -1
  153. package/.next/server/app/v1/chat/completions/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/v1/chat/conversations/[conversationId]/breakpoints/route.js.nft.json +1 -1
  155. package/.next/server/app/v1/chat/conversations/[conversationId]/breakpoints/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/v1/chat/conversations/[conversationId]/cancel/route.js.nft.json +1 -1
  157. package/.next/server/app/v1/chat/conversations/[conversationId]/cancel/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/continue/route.js +1 -1
  159. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/continue/route.js.nft.json +1 -1
  160. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/continue/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/step/route.js +1 -1
  162. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/step/route.js.nft.json +1 -1
  163. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/step/route_client-reference-manifest.js +1 -1
  164. package/.next/server/app/v1/chat/conversations/[conversationId]/edit-state/route.js.nft.json +1 -1
  165. package/.next/server/app/v1/chat/conversations/[conversationId]/edit-state/route_client-reference-manifest.js +1 -1
  166. package/.next/server/app/v1/chat/conversations/[conversationId]/events/route.js +2 -2
  167. package/.next/server/app/v1/chat/conversations/[conversationId]/events/route.js.nft.json +1 -1
  168. package/.next/server/app/v1/chat/conversations/[conversationId]/events/route_client-reference-manifest.js +1 -1
  169. package/.next/server/app/v1/chat/conversations/[conversationId]/resources/route_client-reference-manifest.js +1 -1
  170. package/.next/server/app/v1/chat/conversations/[conversationId]/respond/route.js +1 -1
  171. package/.next/server/app/v1/chat/conversations/[conversationId]/respond/route.js.nft.json +1 -1
  172. package/.next/server/app/v1/chat/conversations/[conversationId]/respond/route_client-reference-manifest.js +1 -1
  173. package/.next/server/app/v1/chat/conversations/[conversationId]/route.js +1 -1
  174. package/.next/server/app/v1/chat/conversations/[conversationId]/route.js.nft.json +1 -1
  175. package/.next/server/app/v1/chat/conversations/[conversationId]/route_client-reference-manifest.js +1 -1
  176. package/.next/server/app/v1/chat/conversations/route.js.nft.json +1 -1
  177. package/.next/server/app/v1/chat/conversations/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/v1/models/route.js.nft.json +1 -1
  179. package/.next/server/app/v1/models/route_client-reference-manifest.js +1 -1
  180. package/.next/server/app/waves/page.js +1 -1
  181. package/.next/server/app/waves/page_client-reference-manifest.js +1 -1
  182. package/.next/server/app/waves.html +1 -1
  183. package/.next/server/app/waves.rsc +1 -1
  184. package/.next/server/app-paths-manifest.json +6 -6
  185. package/.next/server/chunks/1083.js +1 -1
  186. package/.next/server/chunks/1197.js +1 -1
  187. package/.next/server/chunks/1226.js +1 -1
  188. package/.next/server/chunks/1373.js +1 -1
  189. package/.next/server/chunks/264.js +2 -2
  190. package/.next/server/chunks/2823.js +6 -0
  191. package/.next/server/chunks/2859.js +1 -1
  192. package/.next/server/chunks/289.js +1 -1
  193. package/.next/server/chunks/3774.js +1 -1
  194. package/.next/server/chunks/3823.js +4 -4
  195. package/.next/server/chunks/3984.js +4 -4
  196. package/.next/server/chunks/4239.js +1 -1
  197. package/.next/server/chunks/4561.js +4 -4
  198. package/.next/server/chunks/4637.js +8 -2
  199. package/.next/server/chunks/4671.js +1 -1
  200. package/.next/server/chunks/472.js +1 -1
  201. package/.next/server/chunks/4771.js +2 -2
  202. package/.next/server/chunks/5708.js +7 -1
  203. package/.next/server/chunks/5734.js +4 -4
  204. package/.next/server/chunks/6996.js +3 -3
  205. package/.next/server/chunks/701.js +1 -1
  206. package/.next/server/chunks/7362.js +8 -8
  207. package/.next/server/chunks/7506.js +1 -1
  208. package/.next/server/chunks/7769.js +6 -0
  209. package/.next/server/chunks/8739.js +1 -1
  210. package/.next/server/chunks/9509.js +2 -2
  211. package/.next/server/functions-config-manifest.json +2 -2
  212. package/.next/server/instrumentation.js.nft.json +1 -1
  213. package/.next/server/middleware-build-manifest.js +1 -1
  214. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  215. package/.next/server/pages/404.html +1 -1
  216. package/.next/server/pages/500.html +1 -1
  217. package/.next/server/server-reference-manifest.js +1 -1
  218. package/.next/server/server-reference-manifest.json +1 -1
  219. package/.next/static/chunks/6016.8201fa15ab6aa90e.js +1 -0
  220. package/.next/static/chunks/{7583.7cb88044a5212b29.js → 7583.7af90b94e3c263f2.js} +1 -1
  221. package/.next/static/chunks/812-624e93f4c0f671c7.js +1 -0
  222. package/.next/static/chunks/{9164-3d34bf7eab100ac7.js → 9164-d9f1abba780231e0.js} +1 -1
  223. package/.next/static/chunks/app/chat/page-ea6752144d35c892.js +1 -0
  224. package/.next/static/chunks/app/docs/page-935b0386917b91bb.js +1 -0
  225. package/.next/static/chunks/app/executions/page-d72027b0179e8fa2.js +1 -0
  226. package/.next/static/chunks/app/flows/page-1844e86dd43fa285.js +1 -0
  227. package/.next/static/chunks/app/mcp/{page-770a870b3d40766b.js → page-b6866f49f76a6801.js} +1 -1
  228. package/.next/static/chunks/{webpack-dded92b3d7b33173.js → webpack-f4a471ab560eb19e.js} +1 -1
  229. package/.next/trace +8 -9
  230. package/README.md +1 -1
  231. package/package.json +1 -1
  232. package/.next/server/chunks/7214.js +0 -6
  233. package/.next/server/chunks/7463.js +0 -6
  234. package/.next/static/chunks/6016.a157e5bde2faa652.js +0 -1
  235. package/.next/static/chunks/812-181496bc32cd0cd3.js +0 -1
  236. package/.next/static/chunks/app/chat/page-3a3efba25b59a747.js +0 -1
  237. package/.next/static/chunks/app/docs/page-84a92c3d5d8d5e13.js +0 -1
  238. package/.next/static/chunks/app/executions/page-445afbf37819e3ce.js +0 -1
  239. package/.next/static/chunks/app/flows/page-74ebca19957cc6db.js +0 -1
  240. /package/.next/static/{MMmWHUQ3UAJBdRB7G1zGj → WRUzIBdypUJvv0xAct8qc}/_buildManifest.js +0 -0
  241. /package/.next/static/{MMmWHUQ3UAJBdRB7G1zGj → WRUzIBdypUJvv0xAct8qc}/_ssgManifest.js +0 -0
@@ -1,4 +1,4 @@
1
- "use strict";exports.id=4637,exports.ids=[4637],exports.modules={74637:(a,b,c)=>{let d;c.d(b,{internalCallTool:()=>bm,internalToolDefinitions:()=>aS});var e=c(33873),f=c.n(e),g=c(29021),h=c(79646),i=c(18230);let j=(0,i.h)("utils/process/killProcessTree");var k=c(11659),l=c(12763),m=c(50010),n=c(35432),o=c(67463),p=c(89850),q=c(8336),r=c(78606);function s(a,b,c,d){return"number"!=typeof a||Number.isNaN(a)?d:Math.max(b,Math.min(c,Math.floor(a)))}let t={type:"arrowclosed",width:20,height:20,color:"#1976d2"},u={stroke:"#1976d2",strokeWidth:2},v={type:"arrowclosed",width:20,height:20,color:"#009688"},w={stroke:"#009688",strokeWidth:2},x=new Set(["full-history","latest-message","isolated"]),y=new Set(["steps","final-only"]),z=new Set(["full-conversation","latest-message"]);function A(a){let b=(0,q.A$)(a);if(0===b.length)return{text:a,stripped:!1};let c="",d=0;for(let e of b)c+=a.slice(d,e.index)+e.name,d=e.index+e.fullMatch.length;return{text:c+=a.slice(d),stripped:!0}}function B(a,b){if(b.some(b=>b.id===a))return a;let c=a.toLowerCase(),d=b.find(a=>a.name.toLowerCase()===c);return d?d.id:null}var C=c(54898),D=c(82464);let E=(0,i.h)("backend/services/flow/generationContext");function F(a,b){let c=String(a).replace(/\s+/g," ").trim();return c.length<=b?c:`${c.slice(0,b).trimEnd()}…`}async function G(){let a=await l.modelService.loadModels(),b=a.map(a=>({id:a.id,name:a.name,...a.displayName?{displayName:a.displayName}:{},...a.description?{description:F(a.description,100)}:{}})),c=[],d={};try{let a=await D.mcpService.loadServerConfigs();if(Array.isArray(a))for(let b of a){if(b.disabled)continue;let a=!1;try{let c=await D.mcpService.getServerStatus(b.name);a=c?.status==="connected"}catch(a){E.debug(`getServerStatus failed for ${b.name}; listing name only`,a)}if(!a){c.push({name:b.name,connected:!1});continue}let{tools:e}=await D.mcpService.listServerTools(b.name),f=(e??[]).filter(a=>a.name).map(a=>({name:a.name,...a.description?{description:F(a.description,100)}:{}}));d[b.name]=f.map(a=>a.name),c.push({name:b.name,connected:!0,tools:f})}}catch(a){E.warn("Could not load MCP servers for the authoring context",a)}let e=[];try{e=await k.flowService.loadFlows()}catch(a){E.warn("Could not load flows for the authoring context",a)}let f={models:b,servers:c,flows:e.map(a=>({id:a.id,name:a.name,...a.description?{description:F(a.description,300)}:{},nodeCount:a.nodes?.length??0}))};return{blocks:f,compile:{models:a.map(a=>({id:a.id,name:a.name,displayName:a.displayName})),servers:c.map(a=>({name:a.name})),serverTools:d,flows:e.map(a=>({id:a.id,name:a.name}))},catalog:function(a){let b=[];for(let c of(b.push("AVAILABLE MODELS (reference by id or name):"),0===a.models.length&&b.push(" (none configured)"),a.models)){let a=c.displayName&&c.displayName!==c.name?`"${c.displayName}" (${c.name})`:c.name,d=c.description?` — ${c.description}`:"";b.push(` - id: ${c.id} — ${a}${d}`)}for(let c of(b.push("",'AVAILABLE MCP SERVERS (attach to process nodes via "servers"):'),0===a.servers.length&&b.push(" (none configured)"),a.servers)){if(!c.connected){b.push(` - ${c.name} (offline — tools unknown; may still be used)`);continue}b.push(` - ${c.name}:`);let a=(c.tools??[]).slice(0,20);for(let c of a){let a=c.description?` — ${c.description}`:"";b.push(` - ${c.name}${a}`)}let d=(c.tools?.length??0)-a.length;d>0&&b.push(` - …and ${d} more`)}for(let c of(b.push("",'EXISTING FLOWS (usable as subflow targets via "flow"):'),0===a.flows.length&&b.push(" (none)"),a.flows.slice(0,15))){let a=c.description?` — ${c.description}`:` — ${c.nodeCount} nodes`;b.push(` - "${c.name}"${a}`)}return a.flows.length>15&&b.push(` - …and ${a.flows.length-15} more`),b.join("\n")}(f),validatorServers:c.map(a=>({name:a.name,status:a.connected?"connected":"disconnected"}))}}let H=(0,i.h)("backend/services/flow/compileFlow");async function I(a,b={}){if(!a||"object"!=typeof a||Array.isArray(a))return{success:!1,error:"The spec must be a JSON object (a FlowSpec)",statusCode:400};let c=await G(),d=c.compile;if(b.updateFlowId){let a=d.flows??[];if(!a.some(a=>a.id===b.updateFlowId))return{success:!1,error:`No flow with id "${b.updateFlowId}" to update`,statusCode:404};d={...d,flows:a.filter(a=>a.id!==b.updateFlowId)}}let e=function(a,b={},c={}){let d=[],e=(a,b,c)=>d.push({severity:"error",code:a,message:b,nodeKey:c}),f=(a,b,c)=>d.push({severity:"warning",code:a,message:b,nodeKey:c}),g=b.models??[],h=new Set((b.servers??[]).map(a=>a.name)),i=b.serverTools??{},j=s(c.maxDepth,0,3,3),k=s(c.maxFlows,1,8,8),l=[],m=[...(b.flows??[]).map(a=>a.name)],n=[],o=0;var q=function a(d,q,C){let D=Array.isArray(d?.nodes)?d.nodes:[],E=Array.isArray(d?.edges)?d.edges:[],G=function(a,b){let c=(a??"").trim().replace(/[^\w-]+/g,"_").replace(/^_+|_+$/g,"");c||(c="generated_flow");let d=new Set(b.map(a=>a.toLowerCase()));if(!d.has(c.toLowerCase()))return c;for(let a=2;;a++){let b=`${c}_${a}`;if(!d.has(b.toLowerCase()))return b}}(d?.name,m);m.push(G),o++;let H=(0,p.A)(),I=[...C,G],J=new Map,K=[],L=[];for(let c of D){let d=c?.key;if(!d||"string"!=typeof d){e("node-missing-key",'A node is missing its "key" — every node needs a unique key.');continue}if(J.has(d)){e("node-duplicate-key",`Duplicate node key "${d}" — keys must be unique; the later node was dropped.`,d);continue}let l=c.type;if("mcp"===l){e("mcp-node-not-allowed",`Node "${d}": do not emit "mcp" nodes — attach servers to a process node via its "servers" list instead.`,d);continue}if("start"!==l&&"process"!==l&&"finish"!==l&&"subflow"!==l&&"resource"!==l&&"signal"!==l){e("unknown-node-type",`Node "${d}" has unknown type "${String(l)}".`,d);continue}let m={},r="string"==typeof c.prompt?c.prompt:void 0;if("start"===l){let{text:a,stripped:b}=A(r??"");b&&f("pill-stripped",`Node "${d}": binding pills are not supported in generated prompts and were replaced with plain names.`,d),m.promptTemplate=a}else if("process"===l){let{text:a,stripped:b}=A(r??"");if(b&&f("pill-stripped",`Node "${d}": binding pills are not supported in generated prompts and were replaced with plain names.`,d),m.promptTemplate=a,c.model){let a=function(a,b){if(b.some(b=>b.id===a))return a;let c=a.toLowerCase(),d=b.find(a=>a.displayName?.toLowerCase()===c);if(d)return d.id;let e=b.find(a=>a.name?.toLowerCase()===c);return e?e.id:null}(c.model,g);a?m.boundModel=a:(m.boundModel=c.model,f("model-unresolved",`Node "${d}": model "${c.model}" does not match any configured model (by id, display name, or name).`,d))}void 0!==c.inputMode&&(x.has(c.inputMode)?(m.inputMode=c.inputMode,"isolated"===c.inputMode&&"string"==typeof c.isolatedPrompt&&(m.isolatedPrompt=c.isolatedPrompt)):f("invalid-input-mode",`Node "${d}": inputMode "${String(c.inputMode)}" is not valid (full-history | latest-message | isolated); omitted.`,d)),void 0!==c.outputMode&&(z.has(c.outputMode)?m.outputMode=c.outputMode:f("invalid-output-mode",`Node "${d}": outputMode "${String(c.outputMode)}" is not valid on a process node (full-conversation | latest-message); omitted.`,d)),void 0!==c.maxTurns&&("number"!=typeof c.maxTurns||Number.isNaN(c.maxTurns)?f("invalid-max-turns",`Node "${d}": maxTurns "${String(c.maxTurns)}" is not a number; omitted.`,d):m.maxTurns=s(c.maxTurns,1,1e3,1)),"boolean"==typeof c.excludeModelPrompt&&(m.excludeModelPrompt=c.excludeModelPrompt),"boolean"==typeof c.excludeStartNodePrompt&&(m.excludeStartNodePrompt=c.excludeStartNodePrompt),"boolean"==typeof c.excludeSystemPrompt&&(m.excludeSystemPrompt=c.excludeSystemPrompt),Array.isArray(c.allowedTools)&&(m.allowedTools=c.allowedTools.filter(a=>"string"==typeof a&&a)),"string"==typeof c.captureVariable&&c.captureVariable.trim()&&(m.captureVariable=c.captureVariable.trim()),"string"==typeof c.captureResource&&c.captureResource.trim()&&(m.captureResource=c.captureResource.trim()),"string"==typeof c.captureKv&&c.captureKv.trim()&&(m.captureKv=c.captureKv.trim())}else if("subflow"===l){if(function(c,d,g,h,i){let l=Array.isArray(c.parallelFlows)&&c.parallelFlows.length>0,m=Array.isArray(c.parallelSubflowSpecs)&&c.parallelSubflowSpecs.length>0,p="string"==typeof c.parallelFlowsVariable?c.parallelFlowsVariable.trim():"";if([void 0!==c.flow&&null!==c.flow?"flow":null,void 0!==c.subflowSpec&&null!==c.subflowSpec?"subflowSpec":null,l?"parallelFlows":null,m?"parallelSubflowSpecs":null,void 0!==c.generateSubflow&&null!==c.generateSubflow?"generateSubflow":null].filter(Boolean).length>1&&f("subflow-multiple-sources",`Node "${d}": a subflow node should have only one target source ("flow", "subflowSpec", "parallelFlows", "parallelSubflowSpecs", or "generateSubflow"); applying precedence flow > subflowSpec > parallelFlows > parallelSubflowSpecs > generateSubflow.`,d),!0===c.mapOverList){let a=void 0!==c.flow&&null!==c.flow||void 0!==c.subflowSpec&&null!==c.subflowSpec;(l||m)&&e("subflow-map-and-parallel",`Node "${d}": "mapOverList" runs a SINGLE child once per item and cannot be combined with "parallelFlows"/"parallelSubflowSpecs".`,d),a?(i.mapOverList=!0,void 0!==c.itemSplit&&("json-array"===c.itemSplit||"lines"===c.itemSplit?i.itemSplit=c.itemSplit:f("invalid-item-split",`Node "${d}": itemSplit "${String(c.itemSplit)}" is not valid (json-array | lines); using json-array.`,d)),!0===c.sequential&&(i.sequential=!0),F(c,i)):e("subflow-map-no-child",`Node "${d}": "mapOverList" needs a single child ("flow" or "subflowSpec") to run once per item.`,d)}if(p&&(!0===c.mapOverList?e("subflow-map-and-parallel-var",`Node "${d}": "parallelFlowsVariable" (dynamic fan-out) cannot be combined with "mapOverList".`,d):i.parallelSubflowIdsVar=p),c.flow){if(h.some(a=>a.toLowerCase()===c.flow.toLowerCase()))return e("subflow-cycle",`Node "${d}": flow "${c.flow}" refers to an ancestor flow, which would create a cycle.`,d);let a=B(c.flow,[...b.flows??[],...n]);return a?i.subflowId=a:e("subflow-unresolved",`Node "${d}": flow "${c.flow}" does not match any existing flow (by id or name).`,d)}if(c.subflowSpec){if(g+1>j)return e("subflow-too-deep",`Node "${d}": nested subflows may not go deeper than ${j} level(s); this inline subflow was not compiled.`,d);if(o>=k)return e("subflow-too-many",`Node "${d}": compiling this inline subflow would exceed the maximum of ${k} flows in one bundle; it was not compiled.`,d);let b=a(c.subflowSpec,g+1,h);return b?i.subflowId=b.id:e("subflow-child-empty",`Node "${d}": the inline "subflowSpec" produced no usable flow.`,d)}if(l){let a=[];for(let f of c.parallelFlows){if("string"!=typeof f||!f){e("parallel-flow-invalid",`Node "${d}": a "parallelFlows" entry is missing or not a string.`,d);continue}if(h.some(a=>a.toLowerCase()===f.toLowerCase())){e("subflow-cycle",`Node "${d}": parallel flow "${f}" refers to an ancestor flow, which would create a cycle.`,d);continue}let c=B(f,[...b.flows??[],...n]);c?a.includes(c)||a.push(c):e("parallel-flow-unresolved",`Node "${d}": parallel flow "${f}" does not match any existing flow (by id or name).`,d)}a.length>0?(i.parallelSubflowIds=a,F(c,i)):e("parallel-empty",`Node "${d}": "parallelFlows" resolved to no runnable lanes.`,d);return}if(m){let b=[];for(let f of c.parallelSubflowSpecs){if(!f||"object"!=typeof f){e("parallel-spec-invalid",`Node "${d}": a "parallelSubflowSpecs" entry is not a FlowSpec object.`,d);continue}if(g+1>j){e("subflow-too-deep",`Node "${d}": nested subflows may not go deeper than ${j} level(s); a parallel inline subflow was not compiled.`,d);continue}if(o>=k){e("subflow-too-many",`Node "${d}": compiling a parallel inline subflow would exceed the maximum of ${k} flows in one bundle; it was not compiled.`,d);continue}let c=a(f,g+1,h);c?b.push(c.id):e("subflow-child-empty",`Node "${d}": a "parallelSubflowSpecs" entry produced no usable flow.`,d)}b.length>0?(i.parallelSubflowIds=b,F(c,i)):e("parallel-empty",`Node "${d}": "parallelSubflowSpecs" produced no runnable lanes.`,d);return}p&&i.parallelSubflowIdsVar?F(c,i):void 0!==c.generateSubflow&&null!==c.generateSubflow?e("subflow-generate-unsupported",`Node "${d}": "generateSubflow" is only available through the AI flow generator. Use "subflowSpec" for deterministic nested authoring, or "flow" to reference an existing flow.`,d):e("subflow-missing-flow",`Node "${d}": subflow nodes need a "flow" (existing flow), a "subflowSpec" (inline child), "parallelFlows"/"parallelSubflowSpecs" (concurrent children), or "generateSubflow".`,d)}(c,d,q,I,m),void 0!==c.inputMode&&(x.has(c.inputMode)?m.inputMode=c.inputMode:f("invalid-input-mode",`Node "${d}": inputMode "${String(c.inputMode)}" is not valid (full-history | latest-message | isolated); omitted.`,d)),void 0!==c.outputMode&&(y.has(c.outputMode)?m.outputMode=c.outputMode:f("invalid-output-mode",`Node "${d}": outputMode "${String(c.outputMode)}" is not valid (steps | final-only); omitted.`,d)),void 0!==r){let{text:a,stripped:b}=A(r);b&&f("pill-stripped",`Node "${d}": binding pills are not supported in generated prompts and were replaced with plain names.`,d),m.promptTemplate=a}"boolean"==typeof c.allowCallerPrompt&&(m.allowCallerPrompt=c.allowCallerPrompt),"string"==typeof c.captureVariable&&c.captureVariable.trim()&&(m.captureVariable=c.captureVariable.trim()),"string"==typeof c.captureResource&&c.captureResource.trim()&&(m.captureResource=c.captureResource.trim()),"string"==typeof c.captureKv&&c.captureKv.trim()&&(m.captureKv=c.captureKv.trim())}else if("resource"===l){let a="string"==typeof c.runName?c.runName.trim():"",b="string"==typeof c.server?c.server.trim():"",e="string"==typeof c.uri?c.uri.trim():"";a?(m.scope="run",m.runName=a,(b||e)&&f("resource-both-bindings",`Node "${d}": a resource node is EITHER a run artifact ("runName") or a static MCP resource ("server"+"uri"); kept the run artifact.`,d)):(m.scope="mcp",b&&(m.boundServer=b),e&&(m.uri=e),b&&e?h.has(b)||f("server-unknown",`Node "${d}": MCP server "${b}" is not configured in FLUJO.`,d):f("resource-missing-binding",`Node "${d}": a static resource node needs both "server" and "uri" (or a "runName" for a run artifact).`,d))}else if("signal"===l){let a="string"==typeof c.topic?c.topic.trim():"";a?m.topic=a:f("signal-missing-topic",`Node "${d}": a signal node needs a "topic" to emit.`,d),"string"==typeof c.payloadTemplate&&c.payloadTemplate?m.payloadTemplate=c.payloadTemplate:void 0!==r&&(m.payloadTemplate=r)}let t={id:(0,p.A)(),type:l,position:{x:0,y:0},data:{label:c.label||`${l.charAt(0).toUpperCase()}${l.slice(1)} Node`,type:l,...c.description?{description:c.description}:{},properties:m}};if(J.set(d,t),K.push(t),"process"===l&&Array.isArray(c.servers)){let a=new Set;for(let b of c.servers){let c,e=b?.name;if(!e||"string"!=typeof e){f("server-missing-name",`Node "${d}": a server reference is missing its "name"; skipped.`,d);continue}if(a.has(e))continue;a.add(e),h.has(e)||f("server-unknown",`Node "${d}": MCP server "${e}" is not configured in FLUJO.`,d);let g=i[e];if(Array.isArray(b.tools)){if(c=b.tools.filter(a=>"string"==typeof a&&a),g){let a=c.filter(a=>!g.includes(a));a.length>0&&f("tool-unknown",`Node "${d}": server "${e}" does not report tool(s): ${a.join(", ")}.`,d)}}else c=g?[...g]:[];let j={id:(0,p.A)(),type:"mcp",position:{x:0,y:0},data:{label:e,type:"mcp",properties:{boundServer:e,enabledTools:c}}};K.push(j),L.push({processKey:d,mcpNode:j})}}else"process"!==l&&Array.isArray(c.servers)&&c.servers.length>0&&f("servers-on-non-process",`Node "${d}": only process nodes can have "servers"; ignored.`,d)}if(0===J.size)return e("no-usable-nodes","No usable nodes — the spec must contain at least a start node and one step."),null;let M=[],N=new Set;for(let a of E){let b,c=a?.from,d=a?.to,g=c?J.get(c):void 0,h=d?J.get(d):void 0;if(!g||!h){e("edge-unknown-node",`Edge "${String(c)}" -> "${String(d)}" references a node key that does not exist (or was dropped).`);continue}if(g===h){e("edge-self-loop",`Edge "${c}" -> "${d}": a node cannot connect to itself.`);continue}if("resource"===g.type||"resource"===h.type){if(("resource"!==g.type||"process"!==h.type)&&("process"!==g.type||"resource"!==h.type)){e("resource-edge-invalid",`Edge "${c}" -> "${d}": resource nodes connect only to process nodes.`);continue}a.condition&&f("resource-edge-condition",`Edge "${c}" -> "${d}": conditions do not apply to resource wiring; dropped.`),!0===a.bidirectional&&f("resource-edge-bidirectional",`Edge "${c}" -> "${d}": resource wiring is directional (read vs write); "bidirectional" was ignored — add the opposite edge explicitly if the step both reads and writes.`);let b=`${c}->${d}`;if(N.has(b)){f("edge-duplicate",`Edge "${c}" -> "${d}" appears more than once; kept the first.`);continue}N.add(b),M.push(function(a,b){let c="resource"===a.type?"resource-out":"process-right-resource",d="resource"===b.type?"resource-in":"process-left-resource";return{id:`${a.id}:${c}->${b.id}:${d}`,source:a.id,sourceHandle:c,target:b.id,targetHandle:d,type:"resourceEdge",data:{edgeType:"resource"},animated:!1,markerEnd:{...v},style:{...w}}}(g,h));continue}if("start"===h.type){e("edge-into-start",`Edge "${c}" -> "${d}": nothing may connect INTO a start node.`);continue}if("finish"===g.type){e("edge-out-of-finish",`Edge "${c}" -> "${d}": nothing may connect OUT OF a finish node.`);continue}let i=`${c}->${d}`;if(N.has(i)){f("edge-duplicate",`Edge "${c}" -> "${d}" appears more than once; kept the first.`);continue}N.add(i);let j=!0===a.bidirectional;j&&("start"===g.type||"finish"===h.type)&&(f("bidirectional-illegal",`Edge "${c}" -> "${d}" cannot be bidirectional; downgraded to one-way.`),j=!1);let k=a.condition;k&&"object"==typeof k&&("process"!==g.type?f("edge-condition-non-process",`Edge "${c}" -> "${d}" has a condition but leaves a ${g.type} node; conditions only route from process nodes and were dropped.`):(0,r.C)(k.kind)?"always"===k.kind?b={kind:"always",..."last-message"===k.target?{target:"last-message"}:{},...!0===k.negate?{negate:!0}:{}}:"string"!=typeof k.value||0===k.value.length?f("edge-condition-value",`Edge "${c}" -> "${d}" has a condition with no "value"; the condition was dropped.`):("regex"!==k.kind||(0,r.wM)(k.value)||f("edge-condition-regex",`Edge "${c}" -> "${d}" has a regex condition that does not compile; it is kept but will never match at runtime.`),b={kind:k.kind,value:k.value,..."last-message"===k.target?{target:"last-message"}:{},...!0===k.ignoreCase?{ignoreCase:!0}:{},...!0===k.negate?{negate:!0}:{}}):f("edge-condition-kind",`Edge "${c}" -> "${d}" has an unknown condition kind "${String(k.kind)}"; the condition was dropped.`)),M.push(function(a,b,c,d){let e=`${a.type}-bottom`,f=`${b.type}-top`;return{id:`${a.id}:${e}->${b.id}:${f}`,source:a.id,sourceHandle:e,target:b.id,targetHandle:f,type:"custom",data:{edgeType:"standard",...c?{bidirectional:!0}:{},...d?{condition:d}:{}},animated:!0}}(g,h,j,b))}for(let{processKey:a,mcpNode:b}of L){let c=J.get(a);M.push(function(a,b){let c="process-right-mcp",d="mcp-left";return{id:`${a.id}:${c}->${b.id}:${d}`,source:a.id,sourceHandle:c,target:b.id,targetHandle:d,type:"mcpEdge",data:{edgeType:"mcp"},animated:!1,markerEnd:{...t},markerStart:{...t},style:{...u}}}(c,b))}!function(a,b,c,d,e){let f=new Map;for(let a of c){let b=a.data?.edgeType;"mcp"!==b&&"resource"!==b&&(f.has(a.source)||f.set(a.source,[]),f.get(a.source).push(a.target))}let g=[...b.values()],h=g.filter(a=>"resource"===a.type),i=g.filter(a=>"resource"!==a.type),j=i.filter(a=>"start"===a.type),k=j.length>0?j:i.slice(0,1),l=new Map,m=k.map(a=>({id:a.id,depth:0}));for(let a of m)l.set(a.id,0);for(;m.length;){let{id:a,depth:b}=m.shift();for(let c of f.get(a)??[])l.has(c)||(l.set(c,b+1),m.push({id:c,depth:b+1}))}let n=0;for(let a of l.values())n=Math.max(n,a);for(let a of i)l.has(a.id)||l.set(a.id,n+1);let o=new Map;for(let[a,c]of b)o.set(c.id,a);let p=new Map;for(let a of i){let b=o.get(a.id),c=e&&b?e[b]:void 0;if(c&&"number"==typeof c.x&&"number"==typeof c.y){a.position={x:c.x,y:c.y};continue}let d=l.get(a.id),f=p.get(d)??0;p.set(d,f+1),a.position={x:250+280*f,y:150+170*d}}let q=new Map;for(let{processKey:a,mcpNode:c}of d){let d=b.get(a),e=q.get(a)??0;q.set(a,e+1),c.position={x:d.position.x+320,y:d.position.y+150*e}}let r=new Map(g.map(a=>[a.id,a])),s=new Map,t=0;for(let a of h){let b=o.get(a.id),d=e&&b?e[b]:void 0;if(d&&"number"==typeof d.x&&"number"==typeof d.y){a.position={x:d.x,y:d.y};continue}let f=c.find(b=>b.data?.edgeType==="resource"&&(b.source===a.id||b.target===a.id)),g=f?f.source===a.id?f.target:f.source:void 0,h=g?r.get(g):void 0;if(h){let b=s.get(h.id)??0;s.set(h.id,b+1),a.position={x:h.position.x-320,y:h.position.y+150*b}}else{let b=0;for(let a of i)b=Math.max(b,a.position.y);a.position={x:250+280*t,y:b+170},t++}}}(0,J,M,L,0===q?c.positions:void 0);let O={id:H,name:G,...d?.description?{description:d.description}:{},nodes:K,edges:M};return l.push(O),n.push({id:O.id,name:O.name}),O}(a,0,[]),C=l,D=d;let E=D.filter(a=>"error"===a.severity).length;return{flow:q,flows:C,issues:D,errorCount:E,warningCount:D.length-E};function F(a,b){void 0!==a.concurrencyLimit&&("number"!=typeof a.concurrencyLimit||Number.isNaN(a.concurrencyLimit)?f("invalid-concurrency-limit",`Node "${a.key}": concurrencyLimit "${String(a.concurrencyLimit)}" is not a number; using the runtime default.`,a.key):b.concurrencyLimit=Math.max(1,Math.floor(a.concurrencyLimit))),"string"==typeof a.joinSeparator&&(b.joinSeparator=a.joinSeparator),void 0!==a.errorStrategy&&("fail-fast"===a.errorStrategy||"collect-all"===a.errorStrategy?b.errorStrategy=a.errorStrategy:f("invalid-error-strategy",`Node "${a.key}": errorStrategy "${String(a.errorStrategy)}" is not valid (fail-fast | collect-all); using collect-all.`,a.key))}}(a,d);if(!e.flow)return{success:!1,error:"The spec could not be compiled into a flow",statusCode:422,issues:e.issues.map(a=>({severity:a.severity,code:a.code,message:a.message}))};b.updateFlowId&&(e.flow.id=b.updateFlowId);let f=e.flows.flatMap(a=>(0,C.nC)(a,{models:c.compile.models,servers:c.validatorServers,serverTools:c.compile.serverTools}).issues),g=function(a,b){let c=[...a.map(a=>({severity:a.severity,code:a.code,message:a.message})),...b.issues],d=c.filter(a=>"error"===a.severity).length;return{issues:c,errorCount:d,warningCount:c.length-d,isRunnable:0===d}}(e.issues,{issues:f,errorCount:0,warningCount:0,isRunnable:!0}),h=!1;if(b.save&&0===g.errorCount){for(let a of e.flows)await k.flowService.saveFlow(a);h=!0,H.info(`Compiled and saved ${e.flows.length} flow(s); root "${e.flow.name}" (${e.flow.id})`)}else b.save&&H.info(`Compiled flow "${e.flow.name}" has ${g.errorCount} error(s); NOT saved`);return{success:!0,flow:e.flow,flows:e.flows,validation:g,saved:h}}var J=c(44561),K=c(79111),L=c(34115);let M=(0,i.h)("backend/execution/flow/loadConversationState");async function N(a){try{(0,K.ln)(a)}catch{M.warn("Rejected unsafe conversationId on load",{conversationId:a});return}if(J.FlowExecutor.conversationStates.has(a))return M.debug("Loaded state from memory",{conversationId:a}),J.FlowExecutor.conversationStates.get(a);let b=`conversations/${a}`;try{let c=await (0,K.YM)(b,void 0);if(c)return M.debug("Loaded state from storage",{conversationId:a}),await (0,L.jM)(c),J.FlowExecutor.conversationStates.set(a,c),c}catch(b){M.warn("Error loading conversation state from storage",{conversationId:a,error:b})}}var O=c(96249),P=c(63129),Q=c(51172);let R=`A FlowSpec is a JSON object describing a FLUJO flow semantically — node keys and edges, no coordinates, no ids, no layout:
1
+ "use strict";exports.id=4637,exports.ids=[4637],exports.modules={74637:(a,b,c)=>{let d;c.d(b,{internalCallTool:()=>bm,internalToolDefinitions:()=>aS});var e=c(33873),f=c.n(e),g=c(29021),h=c(79646),i=c(18230);let j=(0,i.h)("utils/process/killProcessTree");var k=c(11659),l=c(12763),m=c(50010),n=c(35432),o=c(52823),p=c(89850),q=c(8336),r=c(78606);function s(a,b,c,d){return"number"!=typeof a||Number.isNaN(a)?d:Math.max(b,Math.min(c,Math.floor(a)))}let t={type:"arrowclosed",width:20,height:20,color:"#1976d2"},u={stroke:"#1976d2",strokeWidth:2},v={type:"arrowclosed",width:20,height:20,color:"#009688"},w={stroke:"#009688",strokeWidth:2},x=new Set(["full-history","latest-message","isolated"]),y=new Set(["steps","final-only"]),z=new Set(["full-conversation","latest-message"]);function A(a){let b=(0,q.A$)(a);if(0===b.length)return{text:a,stripped:!1};let c="",d=0;for(let e of b)c+=a.slice(d,e.index)+e.name,d=e.index+e.fullMatch.length;return{text:c+=a.slice(d),stripped:!0}}function B(a,b){if(b.some(b=>b.id===a))return a;let c=a.toLowerCase(),d=b.find(a=>a.name.toLowerCase()===c);return d?d.id:null}var C=c(54898),D=c(82464);let E=(0,i.h)("backend/services/flow/generationContext");function F(a,b){let c=String(a).replace(/\s+/g," ").trim();return c.length<=b?c:`${c.slice(0,b).trimEnd()}…`}async function G(){let a=await l.modelService.loadModels(),b=a.map(a=>({id:a.id,name:a.name,...a.displayName?{displayName:a.displayName}:{},...a.description?{description:F(a.description,100)}:{}})),c=[],d={};try{let a=await D.mcpService.loadServerConfigs();if(Array.isArray(a))for(let b of a){if(b.disabled)continue;let a=!1;try{let c=await D.mcpService.getServerStatus(b.name);a=c?.status==="connected"}catch(a){E.debug(`getServerStatus failed for ${b.name}; listing name only`,a)}if(!a){c.push({name:b.name,connected:!1});continue}let{tools:e}=await D.mcpService.listServerTools(b.name),f=(e??[]).filter(a=>a.name).map(a=>({name:a.name,...a.description?{description:F(a.description,100)}:{}}));d[b.name]=f.map(a=>a.name),c.push({name:b.name,connected:!0,tools:f})}}catch(a){E.warn("Could not load MCP servers for the authoring context",a)}let e=[];try{e=await k.flowService.loadFlows()}catch(a){E.warn("Could not load flows for the authoring context",a)}let f={models:b,servers:c,flows:e.map(a=>({id:a.id,name:a.name,...a.description?{description:F(a.description,300)}:{},nodeCount:a.nodes?.length??0}))};return{blocks:f,compile:{models:a.map(a=>({id:a.id,name:a.name,displayName:a.displayName})),servers:c.map(a=>({name:a.name})),serverTools:d,flows:e.map(a=>({id:a.id,name:a.name}))},catalog:function(a){let b=[];for(let c of(b.push("AVAILABLE MODELS (reference by id or name):"),0===a.models.length&&b.push(" (none configured)"),a.models)){let a=c.displayName&&c.displayName!==c.name?`"${c.displayName}" (${c.name})`:c.name,d=c.description?` — ${c.description}`:"";b.push(` - id: ${c.id} — ${a}${d}`)}for(let c of(b.push("",'AVAILABLE MCP SERVERS (attach to process nodes via "servers"):'),0===a.servers.length&&b.push(" (none configured)"),a.servers)){if(!c.connected){b.push(` - ${c.name} (offline — tools unknown; may still be used)`);continue}b.push(` - ${c.name}:`);let a=(c.tools??[]).slice(0,20);for(let c of a){let a=c.description?` — ${c.description}`:"";b.push(` - ${c.name}${a}`)}let d=(c.tools?.length??0)-a.length;d>0&&b.push(` - …and ${d} more`)}for(let c of(b.push("",'EXISTING FLOWS (usable as subflow targets via "flow"):'),0===a.flows.length&&b.push(" (none)"),a.flows.slice(0,15))){let a=c.description?` — ${c.description}`:` — ${c.nodeCount} nodes`;b.push(` - "${c.name}"${a}`)}return a.flows.length>15&&b.push(` - …and ${a.flows.length-15} more`),b.join("\n")}(f),validatorServers:c.map(a=>({name:a.name,status:a.connected?"connected":"disconnected"}))}}let H=(0,i.h)("backend/services/flow/compileFlow");async function I(a,b={}){if(!a||"object"!=typeof a||Array.isArray(a))return{success:!1,error:"The spec must be a JSON object (a FlowSpec)",statusCode:400};let c=await G(),d=c.compile;if(b.updateFlowId){let a=d.flows??[];if(!a.some(a=>a.id===b.updateFlowId))return{success:!1,error:`No flow with id "${b.updateFlowId}" to update`,statusCode:404};d={...d,flows:a.filter(a=>a.id!==b.updateFlowId)}}let e=function(a,b={},c={}){let d=[],e=(a,b,c)=>d.push({severity:"error",code:a,message:b,nodeKey:c}),f=(a,b,c)=>d.push({severity:"warning",code:a,message:b,nodeKey:c}),g=b.models??[],h=new Set((b.servers??[]).map(a=>a.name)),i=b.serverTools??{},j=s(c.maxDepth,0,3,3),k=s(c.maxFlows,1,8,8),l=[],m=[...(b.flows??[]).map(a=>a.name)],n=[],o=0;var q=function a(d,q,C){let D=Array.isArray(d?.nodes)?d.nodes:[],E=Array.isArray(d?.edges)?d.edges:[],G=function(a,b){let c=(a??"").trim().replace(/[^\w-]+/g,"_").replace(/^_+|_+$/g,"");c||(c="generated_flow");let d=new Set(b.map(a=>a.toLowerCase()));if(!d.has(c.toLowerCase()))return c;for(let a=2;;a++){let b=`${c}_${a}`;if(!d.has(b.toLowerCase()))return b}}(d?.name,m);m.push(G),o++;let H=(0,p.A)(),I=[...C,G],J=new Map,K=[],L=[];for(let c of D){let d=c?.key;if(!d||"string"!=typeof d){e("node-missing-key",'A node is missing its "key" — every node needs a unique key.');continue}if(J.has(d)){e("node-duplicate-key",`Duplicate node key "${d}" — keys must be unique; the later node was dropped.`,d);continue}let l=c.type;if("mcp"===l){e("mcp-node-not-allowed",`Node "${d}": do not emit "mcp" nodes — attach servers to a process node via its "servers" list instead.`,d);continue}if("start"!==l&&"process"!==l&&"finish"!==l&&"subflow"!==l&&"resource"!==l&&"signal"!==l){e("unknown-node-type",`Node "${d}" has unknown type "${String(l)}".`,d);continue}let m={},r="string"==typeof c.prompt?c.prompt:void 0;if("start"===l){let{text:a,stripped:b}=A(r??"");b&&f("pill-stripped",`Node "${d}": binding pills are not supported in generated prompts and were replaced with plain names.`,d),m.promptTemplate=a}else if("process"===l){let{text:a,stripped:b}=A(r??"");if(b&&f("pill-stripped",`Node "${d}": binding pills are not supported in generated prompts and were replaced with plain names.`,d),m.promptTemplate=a,c.model){let a=function(a,b){if(b.some(b=>b.id===a))return a;let c=a.toLowerCase(),d=b.find(a=>a.displayName?.toLowerCase()===c);if(d)return d.id;let e=b.find(a=>a.name?.toLowerCase()===c);return e?e.id:null}(c.model,g);a?m.boundModel=a:(m.boundModel=c.model,f("model-unresolved",`Node "${d}": model "${c.model}" does not match any configured model (by id, display name, or name).`,d))}void 0!==c.inputMode&&(x.has(c.inputMode)?(m.inputMode=c.inputMode,"isolated"===c.inputMode&&"string"==typeof c.isolatedPrompt&&(m.isolatedPrompt=c.isolatedPrompt)):f("invalid-input-mode",`Node "${d}": inputMode "${String(c.inputMode)}" is not valid (full-history | latest-message | isolated); omitted.`,d)),void 0!==c.outputMode&&(z.has(c.outputMode)?m.outputMode=c.outputMode:f("invalid-output-mode",`Node "${d}": outputMode "${String(c.outputMode)}" is not valid on a process node (full-conversation | latest-message); omitted.`,d)),void 0!==c.maxTurns&&("number"!=typeof c.maxTurns||Number.isNaN(c.maxTurns)?f("invalid-max-turns",`Node "${d}": maxTurns "${String(c.maxTurns)}" is not a number; omitted.`,d):m.maxTurns=s(c.maxTurns,1,1e3,1)),"boolean"==typeof c.excludeModelPrompt&&(m.excludeModelPrompt=c.excludeModelPrompt),"boolean"==typeof c.excludeStartNodePrompt&&(m.excludeStartNodePrompt=c.excludeStartNodePrompt),"boolean"==typeof c.excludeSystemPrompt&&(m.excludeSystemPrompt=c.excludeSystemPrompt),Array.isArray(c.allowedTools)&&(m.allowedTools=c.allowedTools.filter(a=>"string"==typeof a&&a)),"string"==typeof c.captureVariable&&c.captureVariable.trim()&&(m.captureVariable=c.captureVariable.trim()),"string"==typeof c.captureResource&&c.captureResource.trim()&&(m.captureResource=c.captureResource.trim()),"string"==typeof c.captureKv&&c.captureKv.trim()&&(m.captureKv=c.captureKv.trim())}else if("subflow"===l){if(function(c,d,g,h,i){let l=Array.isArray(c.parallelFlows)&&c.parallelFlows.length>0,m=Array.isArray(c.parallelSubflowSpecs)&&c.parallelSubflowSpecs.length>0,p="string"==typeof c.parallelFlowsVariable?c.parallelFlowsVariable.trim():"";if([void 0!==c.flow&&null!==c.flow?"flow":null,void 0!==c.subflowSpec&&null!==c.subflowSpec?"subflowSpec":null,l?"parallelFlows":null,m?"parallelSubflowSpecs":null,void 0!==c.generateSubflow&&null!==c.generateSubflow?"generateSubflow":null].filter(Boolean).length>1&&f("subflow-multiple-sources",`Node "${d}": a subflow node should have only one target source ("flow", "subflowSpec", "parallelFlows", "parallelSubflowSpecs", or "generateSubflow"); applying precedence flow > subflowSpec > parallelFlows > parallelSubflowSpecs > generateSubflow.`,d),!0===c.mapOverList){let a=void 0!==c.flow&&null!==c.flow||void 0!==c.subflowSpec&&null!==c.subflowSpec;(l||m)&&e("subflow-map-and-parallel",`Node "${d}": "mapOverList" runs a SINGLE child once per item and cannot be combined with "parallelFlows"/"parallelSubflowSpecs".`,d),a?(i.mapOverList=!0,void 0!==c.itemSplit&&("json-array"===c.itemSplit||"lines"===c.itemSplit?i.itemSplit=c.itemSplit:f("invalid-item-split",`Node "${d}": itemSplit "${String(c.itemSplit)}" is not valid (json-array | lines); using json-array.`,d)),!0===c.sequential&&(i.sequential=!0),F(c,i)):e("subflow-map-no-child",`Node "${d}": "mapOverList" needs a single child ("flow" or "subflowSpec") to run once per item.`,d)}if(p&&(!0===c.mapOverList?e("subflow-map-and-parallel-var",`Node "${d}": "parallelFlowsVariable" (dynamic fan-out) cannot be combined with "mapOverList".`,d):i.parallelSubflowIdsVar=p),c.flow){if(h.some(a=>a.toLowerCase()===c.flow.toLowerCase()))return e("subflow-cycle",`Node "${d}": flow "${c.flow}" refers to an ancestor flow, which would create a cycle.`,d);let a=B(c.flow,[...b.flows??[],...n]);return a?i.subflowId=a:e("subflow-unresolved",`Node "${d}": flow "${c.flow}" does not match any existing flow (by id or name).`,d)}if(c.subflowSpec){if(g+1>j)return e("subflow-too-deep",`Node "${d}": nested subflows may not go deeper than ${j} level(s); this inline subflow was not compiled.`,d);if(o>=k)return e("subflow-too-many",`Node "${d}": compiling this inline subflow would exceed the maximum of ${k} flows in one bundle; it was not compiled.`,d);let b=a(c.subflowSpec,g+1,h);return b?i.subflowId=b.id:e("subflow-child-empty",`Node "${d}": the inline "subflowSpec" produced no usable flow.`,d)}if(l){let a=[];for(let f of c.parallelFlows){if("string"!=typeof f||!f){e("parallel-flow-invalid",`Node "${d}": a "parallelFlows" entry is missing or not a string.`,d);continue}if(h.some(a=>a.toLowerCase()===f.toLowerCase())){e("subflow-cycle",`Node "${d}": parallel flow "${f}" refers to an ancestor flow, which would create a cycle.`,d);continue}let c=B(f,[...b.flows??[],...n]);c?a.includes(c)||a.push(c):e("parallel-flow-unresolved",`Node "${d}": parallel flow "${f}" does not match any existing flow (by id or name).`,d)}a.length>0?(i.parallelSubflowIds=a,F(c,i)):e("parallel-empty",`Node "${d}": "parallelFlows" resolved to no runnable lanes.`,d);return}if(m){let b=[];for(let f of c.parallelSubflowSpecs){if(!f||"object"!=typeof f){e("parallel-spec-invalid",`Node "${d}": a "parallelSubflowSpecs" entry is not a FlowSpec object.`,d);continue}if(g+1>j){e("subflow-too-deep",`Node "${d}": nested subflows may not go deeper than ${j} level(s); a parallel inline subflow was not compiled.`,d);continue}if(o>=k){e("subflow-too-many",`Node "${d}": compiling a parallel inline subflow would exceed the maximum of ${k} flows in one bundle; it was not compiled.`,d);continue}let c=a(f,g+1,h);c?b.push(c.id):e("subflow-child-empty",`Node "${d}": a "parallelSubflowSpecs" entry produced no usable flow.`,d)}b.length>0?(i.parallelSubflowIds=b,F(c,i)):e("parallel-empty",`Node "${d}": "parallelSubflowSpecs" produced no runnable lanes.`,d);return}p&&i.parallelSubflowIdsVar?F(c,i):void 0!==c.generateSubflow&&null!==c.generateSubflow?e("subflow-generate-unsupported",`Node "${d}": "generateSubflow" is only available through the AI flow generator. Use "subflowSpec" for deterministic nested authoring, or "flow" to reference an existing flow.`,d):e("subflow-missing-flow",`Node "${d}": subflow nodes need a "flow" (existing flow), a "subflowSpec" (inline child), "parallelFlows"/"parallelSubflowSpecs" (concurrent children), or "generateSubflow".`,d)}(c,d,q,I,m),void 0!==c.inputMode&&(x.has(c.inputMode)?m.inputMode=c.inputMode:f("invalid-input-mode",`Node "${d}": inputMode "${String(c.inputMode)}" is not valid (full-history | latest-message | isolated); omitted.`,d)),void 0!==c.outputMode&&(y.has(c.outputMode)?m.outputMode=c.outputMode:f("invalid-output-mode",`Node "${d}": outputMode "${String(c.outputMode)}" is not valid (steps | final-only); omitted.`,d)),void 0!==r){let{text:a,stripped:b}=A(r);b&&f("pill-stripped",`Node "${d}": binding pills are not supported in generated prompts and were replaced with plain names.`,d),m.promptTemplate=a}"boolean"==typeof c.allowCallerPrompt&&(m.allowCallerPrompt=c.allowCallerPrompt),"boolean"==typeof c.allowCallerFanout&&(m.allowCallerFanout=c.allowCallerFanout),"string"==typeof c.captureVariable&&c.captureVariable.trim()&&(m.captureVariable=c.captureVariable.trim()),"string"==typeof c.captureResource&&c.captureResource.trim()&&(m.captureResource=c.captureResource.trim()),"string"==typeof c.captureKv&&c.captureKv.trim()&&(m.captureKv=c.captureKv.trim())}else if("resource"===l){let a="string"==typeof c.runName?c.runName.trim():"",b="string"==typeof c.server?c.server.trim():"",e="string"==typeof c.uri?c.uri.trim():"";a?(m.scope="run",m.runName=a,(b||e)&&f("resource-both-bindings",`Node "${d}": a resource node is EITHER a run artifact ("runName") or a static MCP resource ("server"+"uri"); kept the run artifact.`,d)):(m.scope="mcp",b&&(m.boundServer=b),e&&(m.uri=e),b&&e?h.has(b)||f("server-unknown",`Node "${d}": MCP server "${b}" is not configured in FLUJO.`,d):f("resource-missing-binding",`Node "${d}": a static resource node needs both "server" and "uri" (or a "runName" for a run artifact).`,d))}else if("signal"===l){let a="string"==typeof c.topic?c.topic.trim():"";a?m.topic=a:f("signal-missing-topic",`Node "${d}": a signal node needs a "topic" to emit.`,d),"string"==typeof c.payloadTemplate&&c.payloadTemplate?m.payloadTemplate=c.payloadTemplate:void 0!==r&&(m.payloadTemplate=r)}let t={id:(0,p.A)(),type:l,position:{x:0,y:0},data:{label:c.label||`${l.charAt(0).toUpperCase()}${l.slice(1)} Node`,type:l,...c.description?{description:c.description}:{},properties:m}};if(J.set(d,t),K.push(t),"process"===l&&Array.isArray(c.servers)){let a=new Set;for(let b of c.servers){let c,e=b?.name;if(!e||"string"!=typeof e){f("server-missing-name",`Node "${d}": a server reference is missing its "name"; skipped.`,d);continue}if(a.has(e))continue;a.add(e),h.has(e)||f("server-unknown",`Node "${d}": MCP server "${e}" is not configured in FLUJO.`,d);let g=i[e];if(Array.isArray(b.tools)){if(c=b.tools.filter(a=>"string"==typeof a&&a),g){let a=c.filter(a=>!g.includes(a));a.length>0&&f("tool-unknown",`Node "${d}": server "${e}" does not report tool(s): ${a.join(", ")}.`,d)}}else c=g?[...g]:[];let j={id:(0,p.A)(),type:"mcp",position:{x:0,y:0},data:{label:e,type:"mcp",properties:{boundServer:e,enabledTools:c}}};K.push(j),L.push({processKey:d,mcpNode:j})}}else"process"!==l&&Array.isArray(c.servers)&&c.servers.length>0&&f("servers-on-non-process",`Node "${d}": only process nodes can have "servers"; ignored.`,d)}if(0===J.size)return e("no-usable-nodes","No usable nodes — the spec must contain at least a start node and one step."),null;let M=[],N=new Set;for(let a of E){let b,c=a?.from,d=a?.to,g=c?J.get(c):void 0,h=d?J.get(d):void 0;if(!g||!h){e("edge-unknown-node",`Edge "${String(c)}" -> "${String(d)}" references a node key that does not exist (or was dropped).`);continue}if(g===h){e("edge-self-loop",`Edge "${c}" -> "${d}": a node cannot connect to itself.`);continue}if("resource"===g.type||"resource"===h.type){if(("resource"!==g.type||"process"!==h.type)&&("process"!==g.type||"resource"!==h.type)){e("resource-edge-invalid",`Edge "${c}" -> "${d}": resource nodes connect only to process nodes.`);continue}a.condition&&f("resource-edge-condition",`Edge "${c}" -> "${d}": conditions do not apply to resource wiring; dropped.`),!0===a.bidirectional&&f("resource-edge-bidirectional",`Edge "${c}" -> "${d}": resource wiring is directional (read vs write); "bidirectional" was ignored — add the opposite edge explicitly if the step both reads and writes.`);let b=`${c}->${d}`;if(N.has(b)){f("edge-duplicate",`Edge "${c}" -> "${d}" appears more than once; kept the first.`);continue}N.add(b),M.push(function(a,b){let c="resource"===a.type?"resource-out":"process-right-resource",d="resource"===b.type?"resource-in":"process-left-resource";return{id:`${a.id}:${c}->${b.id}:${d}`,source:a.id,sourceHandle:c,target:b.id,targetHandle:d,type:"resourceEdge",data:{edgeType:"resource"},animated:!1,markerEnd:{...v},style:{...w}}}(g,h));continue}if("start"===h.type){e("edge-into-start",`Edge "${c}" -> "${d}": nothing may connect INTO a start node.`);continue}if("finish"===g.type){e("edge-out-of-finish",`Edge "${c}" -> "${d}": nothing may connect OUT OF a finish node.`);continue}let i=`${c}->${d}`;if(N.has(i)){f("edge-duplicate",`Edge "${c}" -> "${d}" appears more than once; kept the first.`);continue}N.add(i);let j=!0===a.bidirectional;j&&("start"===g.type||"finish"===h.type)&&(f("bidirectional-illegal",`Edge "${c}" -> "${d}" cannot be bidirectional; downgraded to one-way.`),j=!1);let k=a.condition;k&&"object"==typeof k&&("process"!==g.type?f("edge-condition-non-process",`Edge "${c}" -> "${d}" has a condition but leaves a ${g.type} node; conditions only route from process nodes and were dropped.`):(0,r.C)(k.kind)?"always"===k.kind?b={kind:"always",..."last-message"===k.target?{target:"last-message"}:{},...!0===k.negate?{negate:!0}:{}}:"string"!=typeof k.value||0===k.value.length?f("edge-condition-value",`Edge "${c}" -> "${d}" has a condition with no "value"; the condition was dropped.`):("regex"!==k.kind||(0,r.wM)(k.value)||f("edge-condition-regex",`Edge "${c}" -> "${d}" has a regex condition that does not compile; it is kept but will never match at runtime.`),b={kind:k.kind,value:k.value,..."last-message"===k.target?{target:"last-message"}:{},...!0===k.ignoreCase?{ignoreCase:!0}:{},...!0===k.negate?{negate:!0}:{}}):f("edge-condition-kind",`Edge "${c}" -> "${d}" has an unknown condition kind "${String(k.kind)}"; the condition was dropped.`)),M.push(function(a,b,c,d){let e=`${a.type}-bottom`,f=`${b.type}-top`;return{id:`${a.id}:${e}->${b.id}:${f}`,source:a.id,sourceHandle:e,target:b.id,targetHandle:f,type:"custom",data:{edgeType:"standard",...c?{bidirectional:!0}:{},...d?{condition:d}:{}},animated:!0}}(g,h,j,b))}for(let{processKey:a,mcpNode:b}of L){let c=J.get(a);M.push(function(a,b){let c="process-right-mcp",d="mcp-left";return{id:`${a.id}:${c}->${b.id}:${d}`,source:a.id,sourceHandle:c,target:b.id,targetHandle:d,type:"mcpEdge",data:{edgeType:"mcp"},animated:!1,markerEnd:{...t},markerStart:{...t},style:{...u}}}(c,b))}!function(a,b,c,d,e){let f=new Map;for(let a of c){let b=a.data?.edgeType;"mcp"!==b&&"resource"!==b&&(f.has(a.source)||f.set(a.source,[]),f.get(a.source).push(a.target))}let g=[...b.values()],h=g.filter(a=>"resource"===a.type),i=g.filter(a=>"resource"!==a.type),j=i.filter(a=>"start"===a.type),k=j.length>0?j:i.slice(0,1),l=new Map,m=k.map(a=>({id:a.id,depth:0}));for(let a of m)l.set(a.id,0);for(;m.length;){let{id:a,depth:b}=m.shift();for(let c of f.get(a)??[])l.has(c)||(l.set(c,b+1),m.push({id:c,depth:b+1}))}let n=0;for(let a of l.values())n=Math.max(n,a);for(let a of i)l.has(a.id)||l.set(a.id,n+1);let o=new Map;for(let[a,c]of b)o.set(c.id,a);let p=new Map;for(let a of i){let b=o.get(a.id),c=e&&b?e[b]:void 0;if(c&&"number"==typeof c.x&&"number"==typeof c.y){a.position={x:c.x,y:c.y};continue}let d=l.get(a.id),f=p.get(d)??0;p.set(d,f+1),a.position={x:250+280*f,y:150+170*d}}let q=new Map;for(let{processKey:a,mcpNode:c}of d){let d=b.get(a),e=q.get(a)??0;q.set(a,e+1),c.position={x:d.position.x+320,y:d.position.y+150*e}}let r=new Map(g.map(a=>[a.id,a])),s=new Map,t=0;for(let a of h){let b=o.get(a.id),d=e&&b?e[b]:void 0;if(d&&"number"==typeof d.x&&"number"==typeof d.y){a.position={x:d.x,y:d.y};continue}let f=c.find(b=>b.data?.edgeType==="resource"&&(b.source===a.id||b.target===a.id)),g=f?f.source===a.id?f.target:f.source:void 0,h=g?r.get(g):void 0;if(h){let b=s.get(h.id)??0;s.set(h.id,b+1),a.position={x:h.position.x-320,y:h.position.y+150*b}}else{let b=0;for(let a of i)b=Math.max(b,a.position.y);a.position={x:250+280*t,y:b+170},t++}}}(0,J,M,L,0===q?c.positions:void 0);let O={id:H,name:G,...d?.description?{description:d.description}:{},nodes:K,edges:M};return l.push(O),n.push({id:O.id,name:O.name}),O}(a,0,[]),C=l,D=d;let E=D.filter(a=>"error"===a.severity).length;return{flow:q,flows:C,issues:D,errorCount:E,warningCount:D.length-E};function F(a,b){void 0!==a.concurrencyLimit&&("number"!=typeof a.concurrencyLimit||Number.isNaN(a.concurrencyLimit)?f("invalid-concurrency-limit",`Node "${a.key}": concurrencyLimit "${String(a.concurrencyLimit)}" is not a number; using the runtime default.`,a.key):b.concurrencyLimit=Math.max(1,Math.floor(a.concurrencyLimit))),"string"==typeof a.joinSeparator&&(b.joinSeparator=a.joinSeparator),void 0!==a.errorStrategy&&("fail-fast"===a.errorStrategy||"collect-all"===a.errorStrategy?b.errorStrategy=a.errorStrategy:f("invalid-error-strategy",`Node "${a.key}": errorStrategy "${String(a.errorStrategy)}" is not valid (fail-fast | collect-all); using collect-all.`,a.key))}}(a,d);if(!e.flow)return{success:!1,error:"The spec could not be compiled into a flow",statusCode:422,issues:e.issues.map(a=>({severity:a.severity,code:a.code,message:a.message}))};b.updateFlowId&&(e.flow.id=b.updateFlowId);let f=e.flows.flatMap(a=>(0,C.nC)(a,{models:c.compile.models,servers:c.validatorServers,serverTools:c.compile.serverTools}).issues),g=function(a,b){let c=[...a.map(a=>({severity:a.severity,code:a.code,message:a.message})),...b.issues],d=c.filter(a=>"error"===a.severity).length;return{issues:c,errorCount:d,warningCount:c.length-d,isRunnable:0===d}}(e.issues,{issues:f,errorCount:0,warningCount:0,isRunnable:!0}),h=!1;if(b.save&&0===g.errorCount){for(let a of e.flows)await k.flowService.saveFlow(a);h=!0,H.info(`Compiled and saved ${e.flows.length} flow(s); root "${e.flow.name}" (${e.flow.id})`)}else b.save&&H.info(`Compiled flow "${e.flow.name}" has ${g.errorCount} error(s); NOT saved`);return{success:!0,flow:e.flow,flows:e.flows,validation:g,saved:h}}var J=c(44561),K=c(79111),L=c(34115);let M=(0,i.h)("backend/execution/flow/loadConversationState");async function N(a){try{(0,K.ln)(a)}catch{M.warn("Rejected unsafe conversationId on load",{conversationId:a});return}if(J.FlowExecutor.conversationStates.has(a))return M.debug("Loaded state from memory",{conversationId:a}),J.FlowExecutor.conversationStates.get(a);let b=`conversations/${a}`;try{let c=await (0,K.YM)(b,void 0);if(c)return M.debug("Loaded state from storage",{conversationId:a}),await (0,L.jM)(c),J.FlowExecutor.conversationStates.set(a,c),c}catch(b){M.warn("Error loading conversation state from storage",{conversationId:a,error:b})}}var O=c(96249),P=c(63129),Q=c(51172);let R=`A FlowSpec is a JSON object describing a FLUJO flow semantically — node keys and edges, no coordinates, no ids, no layout:
2
2
  {
3
3
  "name": "short_flow_name", // letters/digits/_/- only
4
4
  "description": "what the flow does",
@@ -37,11 +37,15 @@ NODE TYPES:
37
37
  "errorStrategy": "collect-all" | "fail-fast", // optional (parallel / mapOverList), default collect-all
38
38
  "inputMode": "full-history" | "latest-message" | "isolated",
39
39
  "allowCallerPrompt": true | false, // optional, only with inputMode "isolated"; when true a routing step may pass a "prompt" via its handoff tool that overrides the subflow's "prompt" (default)
40
+ "allowCallerFanout": true | false, // optional (subflow); when true a routing step may pass a "parallelFlows" array (+ optional "concurrencyLimit") via its handoff tool to CHOOSE the parallel fan-out set at runtime (rule 7c)
40
41
  "captureVariable": "NAME", // optional; save the subflow's output into a run variable other steps inject with \${var:NAME}
41
42
  "captureResource": "NAME", // optional; ALSO save the subflow's output as a tracked run resource (\${res:NAME}, rule 9b)
42
43
  "captureKv": "NAME", // optional; ALSO save the subflow's output to a PERSISTENT cross-run key (\${kv:NAME}, rule 9d)
43
44
  "outputMode": "steps" | "final-only" }
44
45
  - { "key": "...", "type": "finish", "label": "..." }
46
+ - { "key": "...", "type": "signal", "label": "...", // fire-and-forget event (rule 11): emits {topic, payload} then passes through unchanged
47
+ "topic": "<event topic other flows' triggers listen for>", // REQUIRED — a signal with no topic emits nothing
48
+ "payloadTemplate": "event body, may use \${var:NAME}" } // optional; defaults to the node prompt
45
49
  - { "key": "...", "type": "resource", "label": "...", // OPTIONAL/advanced (rule 9c): a data artifact shown in the graph
46
50
  "server": "<server name>", "uri": "<resource uri>", // EITHER a static MCP resource…
47
51
  "runName": "NAME" } // …OR a run artifact steps produce/consume
@@ -61,12 +65,14 @@ RULES:
61
65
  6. A subflow node may have only ONE outgoing edge. Give it EITHER a "flow" (naming an existing flow) OR a "subflowSpec" (an inline nested FlowSpec that becomes its own child flow) — not both. A "subflowSpec" may itself contain subflow nodes with their own "subflowSpec" (bounded nesting), so you can author whole multi-level flows in one object.
62
66
  7. A subflow node may instead fan out to SEVERAL child flows CONCURRENTLY via "parallelFlows" (existing flows) or "parallelSubflowSpecs" (inline). These are STATIC (fixed at author time): the same input is sent to every lane and their outputs are joined. This is about multiple CHILDREN, not successors — the single-outgoing-edge rule (rule 6) still holds. Prefer "parallelFlows" for large fan-outs.
63
67
  7b. For DYNAMIC fan-out — when the set of target flows should be decided AT RUNTIME by an earlier step — use "parallelFlowsVariable": "NAME". An upstream process/subflow node captures a JSON array of flow ids/names (or a newline list) into run variable NAME via "captureVariable": "NAME"; this subflow node then fans out over exactly those flows. Unknown ids and a self-reference are dropped, the count is capped, and a non-empty resolution overrides any static "parallelFlows" (empty falls back to it). Mutually exclusive with "mapOverList". Still one outgoing edge (rule 6).
68
+ 7c. For AGENTIC fan-out — when the ROUTING MODEL itself should decide the parallel set at the very moment it hands off — set "allowCallerFanout": true on the target subflow node. Its handoff tool then exposes an optional "parallelFlows" (array of flow ids/names) argument plus an optional "concurrencyLimit"; the model passes them at call time to fan out over exactly those flows. A non-empty caller set overrides "parallelFlows"/"parallelFlowsVariable"; unknown ids and a self-reference are dropped and the count is capped. Mutually exclusive with "mapOverList". Still one outgoing edge (rule 6). Prefer 7b's "parallelFlowsVariable" when an EARLIER step computes the set; use 7c when the DECIDING (routing) step is the one that chooses it.
64
69
  8. A subflow node may instead run its SINGLE child flow ONCE PER ITEM via "mapOverList": true. The input is split into items by "itemSplit" ("json-array" default, or "lines"), each item is sent to its own child run, and the per-item outputs are joined (same pool/tuning as parallel). Use "sequential": true to run items in order. Mutually exclusive with "parallelFlows"/"parallelSubflowSpecs".
65
70
  9. Named variables (scratchpad): a step can save its output with "captureVariable": "NAME" (process or subflow) and any LATER step injects it with \${var:NAME} inside its "prompt" / "isolatedPrompt" / subflow prompt. This survives "latest-message"/"isolated" scoping, unlike conversation history — use it to carry a todo list, a file path, a diff, or a captured result across steps that don't share history. \${var:NAME} is run-scoped and plaintext; it is DISTINCT from \${global:VAR} (configured secrets/config, resolved only for tool args). Capture a subflow's output on the PARENT subflow node — a variable set inside a child flow is not visible to the parent.
66
71
  9b. Run resources (tracked data): "captureResource": "NAME" (process or subflow) ALSO saves the step's output as a run-scoped RESOURCE — an addressable artifact (flujo://run/... URI) with lineage (which step produced it, which steps read it), readable via the built-in "flujo" MCP server. A later step injects it with \${res:NAME} exactly like \${var:NAME}. Prefer "captureVariable" for short strings; use "captureResource" when the output is a large document/report/dataset worth tracking, or when other tools/agents should be able to read it back. Large or binary MCP tool results (screenshots, files) are auto-captured as run resources without any spec field.
67
72
  9c. Resource nodes are OPTIONAL/advanced — most flows should not use them; prefer rule 9b's captureResource/\${res:NAME}. Emit one only when the data artifact itself should be VISIBLE in the graph: an edge resource→process means the step READS the artifact (its contents are injected into the step's context); process→resource means the step's output is SAVED to it (run artifacts only, equivalent to captureResource with the node's runName). Resource edges are data wiring, not flow control — they never carry conditions and don't count as a step's successor.
68
73
  9d. Persistent state (cross-run): "captureKv": "NAME" (process or subflow) saves the step's output to a PERSISTENT key-value store that SURVIVES ACROSS RUNS, injected by any step with \${kv:NAME}. Unlike \${var:} / \${res:} (discarded when the run ends), \${kv:} is what a long-lived SCHEDULED flow uses to carry a loop counter, a pagination cursor, a last-seen id, or a flag to its NEXT run. By default a key lives on the flow's FOLDER board (flows in the same folder share it); prefix the name to change scope: "flow/NAME" (this flow only) or "global/NAME" (whole instance). Plaintext, never secrets (distinct from \${global:VAR}). A model can also read/update a key mid-run via the built-in "flujo" server's kv_get / kv_set tools.
69
- 10. Keep flows minimal — only the steps the task needs. Write clear, specific prompts and labels; fill "description" on process nodes.`;var S=c(12700),T=c(79861);let U=(0,i.h)("backend/services/mcp/quality/cache"),V="mcp-quality-cache";class W{constructor(a=Date.now()){this.entries={},this.dirty=!1,this.now=a}async load(){try{let a=await (0,K.YM)(V,{entries:{}});this.entries=a?.entries&&"object"==typeof a.entries?a.entries:{}}catch(a){U.warn("Failed to load quality cache; starting empty",a),this.entries={}}}get(a,b){let c=this.entries[`${a}::${b}`];if(c&&!(this.now-c.at>864e5))return c.signal}set(a,b,c){this.entries[`${a}::${b}`]={at:this.now,signal:c},this.dirty=!0}async flush(){let a=!1;for(let[b,c]of Object.entries(this.entries))this.now-c.at>864e5&&(delete this.entries[b],a=!0);if(this.dirty||a)try{await (0,K.VH)(V,{entries:this.entries})}catch(a){U.warn("Failed to persist quality cache",a)}}}function X(a){return!Number.isFinite(a)||a<0?0:a>1?1:a}function Y(a,b){return!Number.isFinite(a)||a<=0?0:!Number.isFinite(b)||b<=1?+(a>0):X(Math.log10(a+1)/Math.log10(b+1))}let Z=(0,i.h)("backend/services/mcp/quality/providers/githubStars"),$="github",_=new Map;function aa(){return d??(process.env.GITHUB_TOKEN||void 0)}function ab(a){let b,c=a.server.repository;if(c?.source&&"github"!==c.source.toLowerCase())return null;var d=c?.url;if(!d)return null;try{b=new URL(d.replace(/^git\+/,""))}catch{return null}if(!/(^|\.)github\.com$/i.test(b.hostname))return null;let e=b.pathname.replace(/\.git$/i,"").split("/").filter(Boolean);return e.length<2?null:`${e[0]}/${e[1]}`.toLowerCase()}async function ac(a,b){let c=new AbortController,d=()=>c.abort();b.addEventListener("abort",d,{once:!0});let e=setTimeout(()=>c.abort(),6e3);try{return await fetch(`https://api.github.com${a}`,{headers:function(){let a={Accept:"application/vnd.github+json","User-Agent":"FLUJO-mcp-quality","X-GitHub-Api-Version":"2022-11-28"},b=aa();return b&&(a.Authorization=`Bearer ${b}`),a}(),signal:c.signal,cache:"no-store"})}catch(b){return Z.warn(`GitHub request failed (${a})`,b instanceof Error?b.message:b),null}finally{clearTimeout(e),b.removeEventListener("abort",d)}}function ad(a){return(403===a.status||429===a.status)&&"0"===a.headers.get("x-ratelimit-remaining")}let ae=(0,i.h)("backend/services/mcp/quality/providers/npmDownloads"),af="npm-downloads",ag=new Map;function ah(a){for(let b of a.server.packages??[])if("npm"===b.registryType&&b.identifier)return b.identifier;return null}function ai(a){return a.startsWith("@")}async function aj(a,b){let c=new AbortController,d=()=>c.abort();b.addEventListener("abort",d,{once:!0});let e=setTimeout(()=>c.abort(),6e3);try{return await fetch(`https://api.npmjs.org${a}`,{signal:c.signal,cache:"no-store"})}catch(b){return ae.warn(`npm request failed (${a})`,b instanceof Error?b.message:b),null}finally{clearTimeout(e),b.removeEventListener("abort",d)}}let ak="registry-status",al=[{id:$,label:"GitHub Stars & Activity",defaultWeight:.5,isApplicable:a=>null!==ab(a),cacheKey:a=>ab(a),async prefetch(a,b,c){let d=`${a??""} mcp`.trim(),e=`/search/repositories?q=${encodeURIComponent(d)}&sort=stars&order=desc&per_page=50`,f=await ac(e,c);if(f&&f.ok)try{for(let a of(await f.json()).items??[])a.full_name&&_.set(a.full_name.toLowerCase(),{stars:"number"==typeof a.stargazers_count?a.stargazers_count:0,pushedAtMs:a.pushed_at?Date.parse(a.pushed_at):null})}catch(a){Z.warn("Failed to parse GitHub search response",a)}else if(f&&ad(f))return void Z.warn("GitHub search rate-limited; ranking will degrade to other providers");if(!aa())return;let g=3;for(let a of b){if(g<=0)break;let b=ab(a);if(!b||_.has(b))continue;g-=1;let d=await ac(`/repos/${b}`,c);if(d){if(ad(d))break;if(d.ok)try{let a=await d.json();_.set(b,{stars:"number"==typeof a.stargazers_count?a.stargazers_count:0,pushedAtMs:a.pushed_at?Date.parse(a.pushed_at):null})}catch{}}}},async fetch(a){let b=ab(a);if(!b)return null;let c=_.get(b);if(!c)return null;let d=Y(c.stars,5e4),e=function(a,b,c=30,d=730){if(!a||!Number.isFinite(a))return 0;let e=(b-a)/864e5;return e<=c?1:e>=d?0:X(1-(e-c)/(d-c))}(c.pushedAtMs,Date.now());return{providerId:$,score:X(.7*d+.3*e),evidence:{repo:b,stars:c.stars,...c.pushedAtMs?{pushedAt:new Date(c.pushedAtMs).toISOString()}:{},starsScore:d,recencyScore:e}}}},{id:af,label:"npm Weekly Downloads",defaultWeight:.35,isApplicable:a=>null!==ah(a),cacheKey:a=>ah(a),async prefetch(a,b,c){let d=b.map(ah).filter(a=>!!a&&!ag.has(a)),e=d.filter(a=>!ai(a)),f=d.filter(ai);for(let a=0;a<e.length;a+=100){let b=e.slice(a,a+100),d=await aj(`/downloads/point/last-week/${b.join(",")}`,c);if(d&&d.ok)try{let a=await d.json();if(1===b.length)"number"==typeof a.downloads&&ag.set(b[0],a.downloads);else for(let c of b){let b=a[c];ag.set(c,b&&"number"==typeof b.downloads?b.downloads:0)}}catch(a){ae.warn("Failed to parse npm bulk downloads response",a)}}for(let a of f){let b=await aj(`/downloads/point/last-week/${a}`,c);if(b&&b.ok)try{let c=await b.json();"number"==typeof c.downloads&&ag.set(a,c.downloads)}catch{}}},async fetch(a){let b=ah(a);if(!b)return null;let c=ag.get(b);return void 0===c?null:{providerId:af,score:Y(c,1e6),evidence:{package:b,weeklyDownloads:c}}}},{id:ak,label:"Registry Status",defaultWeight:.15,isApplicable:a=>!0,cacheKey:()=>null,fetch:async a=>({providerId:ak,score:+("active"===a.verificationStatus),evidence:{status:a.verificationStatus}})}];var am=c(89392);let an=(0,i.h)("backend/services/mcp/quality/settings");function ao(){return{providers:al.map(a=>({id:a.id,enabled:!0,weight:a.defaultWeight})),minScore:0,worksGate:!0}}async function ap(){try{let a=await (0,K.YM)(am.Z.MCP_QUALITY_SETTINGS,ao());return function(a){var b,c;let d=ao(),e=a??{},f=new Map;if(Array.isArray(e.providers))for(let a of e.providers)a&&"string"==typeof a.id&&f.set(a.id,a);return{providers:d.providers.map(a=>{let b=f.get(a.id);return{id:a.id,enabled:"boolean"==typeof b?.enabled?b.enabled:a.enabled,weight:"number"==typeof b?.weight&&Number.isFinite(b.weight)&&b.weight>=0?b.weight:a.weight}}),minScore:(b=e.minScore,c=d.minScore,"number"==typeof b&&Number.isFinite(b)?Math.min(1,Math.max(0,b)):c),worksGate:"boolean"==typeof e.worksGate?e.worksGate:d.worksGate,..."string"==typeof e.githubToken&&e.githubToken.length>0?{githubToken:e.githubToken}:{}}}(a)}catch(a){return an.warn("Failed to load mcpQuality settings; using defaults",a),ao()}}let aq=(0,i.h)("backend/services/mcp/quality/orchestrator");async function ar(a,b){let c=new AbortController,d=setTimeout(()=>c.abort(),b);try{return await a(c.signal)}finally{clearTimeout(d)}}async function as(a,b,c={}){let e=c.topN??10;try{var f;let g=c.settings??await ap(),h=al.filter(a=>(function(a,b){let c=a.providers.find(a=>a.id===b);return!!c?.enabled})(g,a.id));if(0===h.length)return b.map(a=>({candidate:a,score:0,signals:[]}));d=(f=g.githubToken)&&f.length>0?f:void 0;let i=b.slice(0,e),j=b.slice(e),k=new W(c.now??Date.now());await k.load();let l=i.map(()=>[]);for(let b of h){let c=i.map((a,b)=>({c:a,i:b})).filter(({c:a})=>b.isApplicable(a)),d=[],e=[];for(let a of c){let c=b.cacheKey(a.c);if(c){let d=k.get(b.id,c);if(d){e.push({i:a.i,signal:d});continue}}d.push(a)}if(b.prefetch&&d.length>0)try{await ar(c=>b.prefetch(a,d.map(a=>a.c),c),12e3)}catch(a){aq.warn(`Provider "${b.id}" prefetch failed; degrading`,a)}for(let{c:a,i:c}of d)try{let d=await ar(c=>b.fetch(a,c),2e3);if(d){l[c].push(d);let e=b.cacheKey(a);e&&k.set(b.id,e,d)}}catch(c){aq.warn(`Provider "${b.id}" fetch failed for "${a.registryName}"`,c)}for(let{i:a,signal:b}of e)l[a].push(b)}await k.flush();let m=i.map((a,b)=>{let c=l[b],d=new Map(c.map(a=>[a.providerId,a])),e=h.filter(b=>b.isApplicable(a)).map(a=>({providerId:a.id,weight:function(a,b){let c=a.providers.find(a=>a.id===b);return c&&c.enabled?c.weight:0}(g,a.id),score:d.get(a.id)?.score??0})),{score:f}=function(a){let b=0,c=0;for(let d of a){let a=Number.isFinite(d.weight)&&d.weight>0?d.weight:0,e=X(d.score);b+=a,c+=a*e}return{score:b>0?c/b:0,breakdown:a}}(e);return{candidate:a,score:f,signals:c}}).map((a,b)=>({s:a,idx:b})).sort((a,b)=>b.s.score-a.s.score||a.idx-b.idx).map(a=>a.s),n=j.map(a=>({candidate:a,score:0,signals:[]}));return[...m,...n]}catch(a){return aq.warn("enrichAndRank failed; returning unranked candidates",a),b.map(a=>({candidate:a,score:0,signals:[]}))}}let at=(0,i.h)("backend/services/mcp/registryInstall"),au="/v0.1/servers";function av(a){return{registryName:a.server.name,server:a.server,verificationStatus:(0,T.vf)(a)}}async function aw(a,b=8){let c=await ax(a,b);return(await as(a,c.map(av))).map(a=>(function(a,b){let c=(0,T.AH)(a),d=c[0];return{name:a.name,...a.title?{title:a.title}:{},...a.description?{description:a.description}:{},installable:c.length>0,requiredEnv:d?(0,T.jg)(d):[],...b?{quality:function(a){let b=b=>a.signals.find(a=>a.providerId===b)?.evidence,c=b($),d=b(af),e=b(ak);return{score:a.score,..."number"==typeof c?.stars?{stars:c.stars}:{},..."number"==typeof d?.weeklyDownloads?{weeklyDownloads:d.weeklyDownloads}:{},..."string"==typeof e?.status?{status:e.status}:{}}}(b)}:{}}})(a.candidate.server,a))}async function ax(a,b){let c=new URL(S.jD+au);c.searchParams.set("version","latest"),c.searchParams.set("limit",String(Math.min(Math.max(b,1),30))),a&&c.searchParams.set("search",a);let d=await (0,S.Ws)(c,15e3);return Array.isArray(d?.servers)?d.servers:[]}async function ay(a){let b=new URL(S.jD+au);b.searchParams.set("version","latest"),b.searchParams.set("limit","10"),b.searchParams.set("search",a);let c=await (0,S.Ws)(b,15e3),d=Array.isArray(c?.servers)?c.servers:[];return d.find(b=>b.server?.name===a)??d[0]??null}async function az(a,b,c){let d;if(!a||"string"!=typeof a)return{installed:!1,error:"A registry server name is required"};try{d=await ay(a)}catch(a){return{installed:!1,error:`Registry lookup failed: ${a instanceof Error?a.message:String(a)}`}}let e=d?.server??null;if(!e)return{installed:!1,error:`No registry entry found for "${a}"`};let f=(0,T.AH)(e)[0];if(!f)return{installed:!1,error:`"${e.name}" has no install method FLUJO supports (stdio package or HTTP remote)`};let g=(0,T.vf)(d),h=(0,T.O$)(a,e,f,g);if(c?.resolveOnly)return{installed:!1,serverName:h.serverName,plan:h};let i=(0,T.jg)(f,b);if(i.length>0)return{installed:!1,needsEnv:i,plan:h,error:`"${e.name}" requires values for: ${i.join(", ")}`};let j=(0,T.cy)((0,T.Yr)(e,f),b),k=j.name,l=await D.mcpService.loadServerConfigs();if(Array.isArray(l)&&l.some(a=>a.name===k)){at.info(`installRegistryServer: "${k}" already configured; reusing`);let{tools:a,error:b}=await D.mcpService.listServerTools(k);return{installed:!0,alreadyExisted:!0,serverName:k,plan:h,tools:(a??[]).map(a=>({name:a.name,...a.description?{description:a.description}:{}})),...b?{error:b}:{}}}at.info(`installRegistryServer: installing "${e.name}" as "${k}" (${f.kind})`);let m=await D.mcpService.updateServerConfig(k,j);if(!Array.isArray(m)&&m&&"success"in m&&!1===m.success)return{installed:!1,error:`Saving the server failed: ${m.error??"unknown error"}`};let{tools:n,error:o}=await D.mcpService.listServerTools(k),p=(n??[]).map(a=>({name:a.name,...a.description?{description:a.description}:{}}));if((c?.worksGate??(await ap()).worksGate)&&0===p.length){at.warn(`installRegistryServer: "${k}" exposed no tools${o?` (${o})`:""}; rolling back (works-gate)`);try{await D.mcpService.deleteServerConfig(k)}catch(a){at.error(`installRegistryServer: rollback of "${k}" failed`,a)}return{installed:!1,worksGateRejected:!0,serverName:k,plan:h,error:o?`"${e.name}" failed to start: ${o}`:`"${e.name}" connected but exposed no tools — rejected by the works-gate. Try a different server.`}}return{installed:!0,serverName:k,plan:h,tools:p,...o?{error:o}:{}}}async function aA(a,b,c){let d;if(!a||"string"!=typeof a)return{installed:!1,error:"A capability search query is required"};try{d=await ax(a,10)}catch(a){return{installed:!1,error:`Registry lookup failed: ${a instanceof Error?a.message:String(a)}`}}let e=await as(a,d.map(av)),f=await ap(),g=c?.minScore??f.minScore,h=c?.maxAttempts??3,i=[],j=0;for(let a of e){let d=a.candidate.registryName;if(a.score<g){i.push({name:d,score:a.score,reason:`below minScore ${g}`});break}if(0===(0,T.AH)(a.candidate.server).length){i.push({name:d,score:a.score,reason:"no supported install method"});continue}if(j>=h)break;j+=1;let e=await az(d,b,{worksGate:!0});if(c?.onAttempt)try{await c.onAttempt(e.plan,e)}catch(a){at.error("installBestForCapability: onAttempt hook failed",a)}if(e.installed)return{...e,attempts:i};let f=e.needsEnv?.length?`needs env: ${e.needsEnv.join(", ")}`:e.worksGateRejected?"works-gate rejected (no tools / failed to start)":e.error??"install failed";i.push({name:d,score:a.score,reason:f})}return{installed:!1,attempts:i,error:`No working server found for "${a}" among the top ${j} installable candidate(s).`}}let aB={requireConsent:!0,namespaceAllowlist:[],trustBrainStem:!0};function aC(a){let{caller:b,settings:c,registryName:d,allowInstall:e}=a;if("generator"===b)return!0===e?{allowed:!0,reason:"generator-allowinstall",message:"Approved by the per-generation allowInstall opt-in."}:{allowed:!1,reason:"generator-not-allowed",message:"Installing is not allowed in this generation (allowInstall is off)."};if("authoring-tool"===b&&c.trustBrainStem)return{allowed:!0,reason:"brainstem-trusted",message:"Approved: the /mcp-flows authoring tool is trusted (mcpAutoInstall.trustBrainStem)."};if(!c.requireConsent)return{allowed:!0,reason:"consent-disabled",message:"Approved: consent is not required (mcpAutoInstall.requireConsent is off)."};let f=function(a){if("string"!=typeof a)return"";let b=a.indexOf("/");return b>0?a.slice(0,b):""}(d);return""!==f&&c.namespaceAllowlist.includes(f)?{allowed:!0,reason:"namespace-allowlisted",message:`Approved: namespace "${f}" is allowlisted.`}:{allowed:!1,reason:"consent-required",message:"Consent required: review the exact command and arguments, then approve this install (SEP-1024)."}}function aD(a,b,c,d,e){return{at:new Date().toISOString(),caller:b,registryName:a.registryName,resolvedName:a.resolvedName,serverName:a.serverName,transport:a.transport,...a.command?{command:a.command}:{},...a.args?{args:a.args}:{},...a.serverUrl?{serverUrl:a.serverUrl}:{},requiredEnvNames:a.requiredEnvNames,verificationStatus:a.verificationStatus,consent:{allowed:c.allowed,reason:c.reason},installed:d,...e?{error:e}:{}}}let aE=(0,i.h)("backend/services/mcp/autoInstall");async function aF(){try{let a=await (0,K.YM)(am.Z.MCP_AUTO_INSTALL_SETTINGS,aB)??{};return{requireConsent:"boolean"==typeof a.requireConsent?a.requireConsent:aB.requireConsent,namespaceAllowlist:Array.isArray(a.namespaceAllowlist)?a.namespaceAllowlist.filter(a=>"string"==typeof a&&a.length>0):[],trustBrainStem:"boolean"==typeof a.trustBrainStem?a.trustBrainStem:aB.trustBrainStem}}catch(a){return aE.warn("Failed to load mcpAutoInstall settings; using safe defaults",a),{...aB}}}let aG="mcp-install-audit",aH=Promise.resolve();async function aI(){try{return await (0,K.YM)(aG,[])}catch(a){return aE.error("Failed to load MCP install audit log",a),[]}}async function aJ(a){let b=aH.catch(()=>{}).then(async()=>{let b=await aI();b.push(a);let c=b.slice(-300);await (0,K.VH)(aG,c)}).catch(a=>{aE.error("Failed to append MCP install audit entry",a)});aH=b,await b}let aK=(0,i.h)("backend/services/mcp/flowAuthoringTools"),aL=["list_flow_building_blocks","validate_flow_spec","create_flow","search_mcp_marketplace","install_mcp_server","install_best_mcp_server"];function aM(){return{type:"object",properties:{spec:{type:"object",description:"The FlowSpec object (see the tool description for the format)."}},required:["spec"]}}function aN(a,b=!1){return{content:[{type:"text",text:JSON.stringify(a,null,2)}],...b?{isError:!0}:{}}}async function aO(a,b){try{if("list_flow_building_blocks"===a){let a=await G();return aN(a.blocks)}if("search_mcp_marketplace"===a){let a="string"==typeof b?.query?b.query:"",c=await aw(a);return aN(c)}if("install_mcp_server"===a){let a="string"==typeof b?.name?b.name:"",c=b?.env&&"object"==typeof b.env&&!Array.isArray(b.env)?b.env:void 0,d=await az(a,c,{resolveOnly:!0});if(!d.plan)return aN(d,!0);let e=await aF(),f=aC({caller:"authoring-tool",settings:e,registryName:a}),g=(0,T.nO)(d.plan.verificationStatus)?void 0:`Unverified / self-asserted registry entry (status: ${d.plan.verificationStatus}). Registry entries are publisher-asserted — review the command before approving.`;if(await aJ(aD(d.plan,"authoring-tool",f,!1)),!f.allowed)return aN({installed:!1,consentRequired:!0,message:f.message,plan:d.plan,...g?{verificationWarning:g}:{}});let h=await az(a,c);return await aJ(aD(h.plan??d.plan,"authoring-tool",f,h.installed,h.error)),aN({...h,...g?{verificationWarning:g}:{}},!h.installed)}if("install_best_mcp_server"===a){let a="string"==typeof b?.capability?b.capability:"",c=b?.env&&"object"==typeof b.env&&!Array.isArray(b.env)?b.env:void 0;if(!a)return aN({error:'Provide a "capability" search term.'},!0);let d=await aF(),e=aC({caller:"authoring-tool",settings:d,registryName:a});if(!e.allowed){let b=(await aw(a)).find(a=>a.installable),d=b?await az(b.name,c,{resolveOnly:!0}):null;return d?.plan&&await aJ(aD(d.plan,"authoring-tool",e,!1)),aN({installed:!1,consentRequired:!0,message:e.message,...d?.plan?{plan:d.plan}:{}})}let f=await aA(a,c,{onAttempt:async(a,b)=>{a&&await aJ(aD(a,"authoring-tool",e,b.installed,b.error))}}),g=f.installed&&!(0,T.nO)(f.plan?.verificationStatus)?`Installed an unverified / self-asserted registry entry (status: ${f.plan?.verificationStatus}).`:void 0;return aN({...f,...g?{verificationWarning:g}:{}},!f.installed)}if("validate_flow_spec"===a||"create_flow"===a){let c=function(a){let b=a?.spec;if("string"==typeof b)try{return JSON.parse(b)}catch{return null}return b??null}(b);if(!c)return aN({error:'Provide a "spec" argument: a FlowSpec object (or a JSON string of one).'},!0);let d=await I(c,{save:"create_flow"===a});if(!d.success)return aN({error:d.error,issues:d.issues??[]},!0);let e=d.flows.length,f=e>1?` (plus ${e-1} nested subflow flow(s))`:"",g={flowId:d.flow.id,flowName:d.flow.name,nodeCount:d.flow.nodes.length,edgeCount:d.flow.edges.length,...e>1?{flows:d.flows.map(a=>({id:a.id,name:a.name}))}:{},validation:d.validation,..."create_flow"===a?{saved:d.saved,...d.saved?{note:`Flow "${d.flow.name}"${f} was created. It is callable as a tool on this MCP server (after a fresh tools/list) and as model "flow-${d.flow.name}" on the OpenAI-compatible endpoint.`}:{note:"NOT saved: validation found errors. Fix the issues and call create_flow again."}}:{}};return aN(g,"create_flow"===a&&!d.saved)}return aN({error:`Unknown authoring tool: ${a}`},!0)}catch(b){return aK.error("authoringCallTool failed",{toolName:a,err:b}),aN({error:`Authoring tool failed: ${b instanceof Error?b.message:String(b)}`},!0)}}var aP=c(92018);let aQ=(0,i.h)("backend/services/mcp/internalTools");function aR(a,b=!1){return{content:[{type:"text",text:"string"==typeof a?a:JSON.stringify(a,null,2)}],...b?{isError:!0}:{}}}function aS(){return[{name:"list_flow_building_blocks",description:'List everything a FlowSpec may reference in this FLUJO instance: configured models (bind to process steps), MCP servers and their tools (attach via "servers"), and existing flows (usable as subflow targets). Call this before authoring a spec.',inputSchema:{type:"object",properties:{}}},{name:"validate_flow_spec",description:`Compile and validate a FlowSpec WITHOUT saving. Returns the compiled flow's validation result (errors block saving; warnings are advisory) so you can iterate until clean, then call create_flow.
74
+ 10. Keep flows minimal — only the steps the task needs. Write clear, specific prompts and labels; fill "description" on process nodes.
75
+ 11. Signals (fire-and-forget events): a "signal" node is a deterministic pass-through that, when the path reaches it, emits an event {topic, payload} onto the flow-run event bus and then continues to its successor unchanged (it never calls a model or touches the conversation). Use it when the flow should notify or kick off ANOTHER flow mid-run — a flow-event trigger configured elsewhere listens for the same "topic" (e.g. "when the review finds blockers, emit a review-blocked signal"). Always give it a "topic" (a signal with no topic emits nothing); "payloadTemplate" is the optional event body and may inject \${var:NAME}. Make emission conditional by putting a conditioned edge (rule 5b) INTO the signal node.`;var S=c(12700),T=c(79861);let U=(0,i.h)("backend/services/mcp/quality/cache"),V="mcp-quality-cache";class W{constructor(a=Date.now()){this.entries={},this.dirty=!1,this.now=a}async load(){try{let a=await (0,K.YM)(V,{entries:{}});this.entries=a?.entries&&"object"==typeof a.entries?a.entries:{}}catch(a){U.warn("Failed to load quality cache; starting empty",a),this.entries={}}}get(a,b){let c=this.entries[`${a}::${b}`];if(c&&!(this.now-c.at>864e5))return c.signal}set(a,b,c){this.entries[`${a}::${b}`]={at:this.now,signal:c},this.dirty=!0}async flush(){let a=!1;for(let[b,c]of Object.entries(this.entries))this.now-c.at>864e5&&(delete this.entries[b],a=!0);if(this.dirty||a)try{await (0,K.VH)(V,{entries:this.entries})}catch(a){U.warn("Failed to persist quality cache",a)}}}function X(a){return!Number.isFinite(a)||a<0?0:a>1?1:a}function Y(a,b){return!Number.isFinite(a)||a<=0?0:!Number.isFinite(b)||b<=1?+(a>0):X(Math.log10(a+1)/Math.log10(b+1))}let Z=(0,i.h)("backend/services/mcp/quality/providers/githubStars"),$="github",_=new Map;function aa(){return d??(process.env.GITHUB_TOKEN||void 0)}function ab(a){let b,c=a.server.repository;if(c?.source&&"github"!==c.source.toLowerCase())return null;var d=c?.url;if(!d)return null;try{b=new URL(d.replace(/^git\+/,""))}catch{return null}if(!/(^|\.)github\.com$/i.test(b.hostname))return null;let e=b.pathname.replace(/\.git$/i,"").split("/").filter(Boolean);return e.length<2?null:`${e[0]}/${e[1]}`.toLowerCase()}async function ac(a,b){let c=new AbortController,d=()=>c.abort();b.addEventListener("abort",d,{once:!0});let e=setTimeout(()=>c.abort(),6e3);try{return await fetch(`https://api.github.com${a}`,{headers:function(){let a={Accept:"application/vnd.github+json","User-Agent":"FLUJO-mcp-quality","X-GitHub-Api-Version":"2022-11-28"},b=aa();return b&&(a.Authorization=`Bearer ${b}`),a}(),signal:c.signal,cache:"no-store"})}catch(b){return Z.warn(`GitHub request failed (${a})`,b instanceof Error?b.message:b),null}finally{clearTimeout(e),b.removeEventListener("abort",d)}}function ad(a){return(403===a.status||429===a.status)&&"0"===a.headers.get("x-ratelimit-remaining")}let ae=(0,i.h)("backend/services/mcp/quality/providers/npmDownloads"),af="npm-downloads",ag=new Map;function ah(a){for(let b of a.server.packages??[])if("npm"===b.registryType&&b.identifier)return b.identifier;return null}function ai(a){return a.startsWith("@")}async function aj(a,b){let c=new AbortController,d=()=>c.abort();b.addEventListener("abort",d,{once:!0});let e=setTimeout(()=>c.abort(),6e3);try{return await fetch(`https://api.npmjs.org${a}`,{signal:c.signal,cache:"no-store"})}catch(b){return ae.warn(`npm request failed (${a})`,b instanceof Error?b.message:b),null}finally{clearTimeout(e),b.removeEventListener("abort",d)}}let ak="registry-status",al=[{id:$,label:"GitHub Stars & Activity",defaultWeight:.5,isApplicable:a=>null!==ab(a),cacheKey:a=>ab(a),async prefetch(a,b,c){let d=`${a??""} mcp`.trim(),e=`/search/repositories?q=${encodeURIComponent(d)}&sort=stars&order=desc&per_page=50`,f=await ac(e,c);if(f&&f.ok)try{for(let a of(await f.json()).items??[])a.full_name&&_.set(a.full_name.toLowerCase(),{stars:"number"==typeof a.stargazers_count?a.stargazers_count:0,pushedAtMs:a.pushed_at?Date.parse(a.pushed_at):null})}catch(a){Z.warn("Failed to parse GitHub search response",a)}else if(f&&ad(f))return void Z.warn("GitHub search rate-limited; ranking will degrade to other providers");if(!aa())return;let g=3;for(let a of b){if(g<=0)break;let b=ab(a);if(!b||_.has(b))continue;g-=1;let d=await ac(`/repos/${b}`,c);if(d){if(ad(d))break;if(d.ok)try{let a=await d.json();_.set(b,{stars:"number"==typeof a.stargazers_count?a.stargazers_count:0,pushedAtMs:a.pushed_at?Date.parse(a.pushed_at):null})}catch{}}}},async fetch(a){let b=ab(a);if(!b)return null;let c=_.get(b);if(!c)return null;let d=Y(c.stars,5e4),e=function(a,b,c=30,d=730){if(!a||!Number.isFinite(a))return 0;let e=(b-a)/864e5;return e<=c?1:e>=d?0:X(1-(e-c)/(d-c))}(c.pushedAtMs,Date.now());return{providerId:$,score:X(.7*d+.3*e),evidence:{repo:b,stars:c.stars,...c.pushedAtMs?{pushedAt:new Date(c.pushedAtMs).toISOString()}:{},starsScore:d,recencyScore:e}}}},{id:af,label:"npm Weekly Downloads",defaultWeight:.35,isApplicable:a=>null!==ah(a),cacheKey:a=>ah(a),async prefetch(a,b,c){let d=b.map(ah).filter(a=>!!a&&!ag.has(a)),e=d.filter(a=>!ai(a)),f=d.filter(ai);for(let a=0;a<e.length;a+=100){let b=e.slice(a,a+100),d=await aj(`/downloads/point/last-week/${b.join(",")}`,c);if(d&&d.ok)try{let a=await d.json();if(1===b.length)"number"==typeof a.downloads&&ag.set(b[0],a.downloads);else for(let c of b){let b=a[c];ag.set(c,b&&"number"==typeof b.downloads?b.downloads:0)}}catch(a){ae.warn("Failed to parse npm bulk downloads response",a)}}for(let a of f){let b=await aj(`/downloads/point/last-week/${a}`,c);if(b&&b.ok)try{let c=await b.json();"number"==typeof c.downloads&&ag.set(a,c.downloads)}catch{}}},async fetch(a){let b=ah(a);if(!b)return null;let c=ag.get(b);return void 0===c?null:{providerId:af,score:Y(c,1e6),evidence:{package:b,weeklyDownloads:c}}}},{id:ak,label:"Registry Status",defaultWeight:.15,isApplicable:a=>!0,cacheKey:()=>null,fetch:async a=>({providerId:ak,score:+("active"===a.verificationStatus),evidence:{status:a.verificationStatus}})}];var am=c(89392);let an=(0,i.h)("backend/services/mcp/quality/settings");function ao(){return{providers:al.map(a=>({id:a.id,enabled:!0,weight:a.defaultWeight})),minScore:0,worksGate:!0}}async function ap(){try{let a=await (0,K.YM)(am.Z.MCP_QUALITY_SETTINGS,ao());return function(a){var b,c;let d=ao(),e=a??{},f=new Map;if(Array.isArray(e.providers))for(let a of e.providers)a&&"string"==typeof a.id&&f.set(a.id,a);return{providers:d.providers.map(a=>{let b=f.get(a.id);return{id:a.id,enabled:"boolean"==typeof b?.enabled?b.enabled:a.enabled,weight:"number"==typeof b?.weight&&Number.isFinite(b.weight)&&b.weight>=0?b.weight:a.weight}}),minScore:(b=e.minScore,c=d.minScore,"number"==typeof b&&Number.isFinite(b)?Math.min(1,Math.max(0,b)):c),worksGate:"boolean"==typeof e.worksGate?e.worksGate:d.worksGate,..."string"==typeof e.githubToken&&e.githubToken.length>0?{githubToken:e.githubToken}:{}}}(a)}catch(a){return an.warn("Failed to load mcpQuality settings; using defaults",a),ao()}}let aq=(0,i.h)("backend/services/mcp/quality/orchestrator");async function ar(a,b){let c=new AbortController,d=setTimeout(()=>c.abort(),b);try{return await a(c.signal)}finally{clearTimeout(d)}}async function as(a,b,c={}){let e=c.topN??10;try{var f;let g=c.settings??await ap(),h=al.filter(a=>(function(a,b){let c=a.providers.find(a=>a.id===b);return!!c?.enabled})(g,a.id));if(0===h.length)return b.map(a=>({candidate:a,score:0,signals:[]}));d=(f=g.githubToken)&&f.length>0?f:void 0;let i=b.slice(0,e),j=b.slice(e),k=new W(c.now??Date.now());await k.load();let l=i.map(()=>[]);for(let b of h){let c=i.map((a,b)=>({c:a,i:b})).filter(({c:a})=>b.isApplicable(a)),d=[],e=[];for(let a of c){let c=b.cacheKey(a.c);if(c){let d=k.get(b.id,c);if(d){e.push({i:a.i,signal:d});continue}}d.push(a)}if(b.prefetch&&d.length>0)try{await ar(c=>b.prefetch(a,d.map(a=>a.c),c),12e3)}catch(a){aq.warn(`Provider "${b.id}" prefetch failed; degrading`,a)}for(let{c:a,i:c}of d)try{let d=await ar(c=>b.fetch(a,c),2e3);if(d){l[c].push(d);let e=b.cacheKey(a);e&&k.set(b.id,e,d)}}catch(c){aq.warn(`Provider "${b.id}" fetch failed for "${a.registryName}"`,c)}for(let{i:a,signal:b}of e)l[a].push(b)}await k.flush();let m=i.map((a,b)=>{let c=l[b],d=new Map(c.map(a=>[a.providerId,a])),e=h.filter(b=>b.isApplicable(a)).map(a=>({providerId:a.id,weight:function(a,b){let c=a.providers.find(a=>a.id===b);return c&&c.enabled?c.weight:0}(g,a.id),score:d.get(a.id)?.score??0})),{score:f}=function(a){let b=0,c=0;for(let d of a){let a=Number.isFinite(d.weight)&&d.weight>0?d.weight:0,e=X(d.score);b+=a,c+=a*e}return{score:b>0?c/b:0,breakdown:a}}(e);return{candidate:a,score:f,signals:c}}).map((a,b)=>({s:a,idx:b})).sort((a,b)=>b.s.score-a.s.score||a.idx-b.idx).map(a=>a.s),n=j.map(a=>({candidate:a,score:0,signals:[]}));return[...m,...n]}catch(a){return aq.warn("enrichAndRank failed; returning unranked candidates",a),b.map(a=>({candidate:a,score:0,signals:[]}))}}let at=(0,i.h)("backend/services/mcp/registryInstall"),au="/v0.1/servers";function av(a){return{registryName:a.server.name,server:a.server,verificationStatus:(0,T.vf)(a)}}async function aw(a,b=8){let c=await ax(a,b);return(await as(a,c.map(av))).map(a=>(function(a,b){let c=(0,T.AH)(a),d=c[0];return{name:a.name,...a.title?{title:a.title}:{},...a.description?{description:a.description}:{},installable:c.length>0,requiredEnv:d?(0,T.jg)(d):[],...b?{quality:function(a){let b=b=>a.signals.find(a=>a.providerId===b)?.evidence,c=b($),d=b(af),e=b(ak);return{score:a.score,..."number"==typeof c?.stars?{stars:c.stars}:{},..."number"==typeof d?.weeklyDownloads?{weeklyDownloads:d.weeklyDownloads}:{},..."string"==typeof e?.status?{status:e.status}:{}}}(b)}:{}}})(a.candidate.server,a))}async function ax(a,b){let c=new URL(S.jD+au);c.searchParams.set("version","latest"),c.searchParams.set("limit",String(Math.min(Math.max(b,1),30))),a&&c.searchParams.set("search",a);let d=await (0,S.Ws)(c,15e3);return Array.isArray(d?.servers)?d.servers:[]}async function ay(a){let b=new URL(S.jD+au);b.searchParams.set("version","latest"),b.searchParams.set("limit","10"),b.searchParams.set("search",a);let c=await (0,S.Ws)(b,15e3),d=Array.isArray(c?.servers)?c.servers:[];return d.find(b=>b.server?.name===a)??d[0]??null}async function az(a,b,c){let d;if(!a||"string"!=typeof a)return{installed:!1,error:"A registry server name is required"};try{d=await ay(a)}catch(a){return{installed:!1,error:`Registry lookup failed: ${a instanceof Error?a.message:String(a)}`}}let e=d?.server??null;if(!e)return{installed:!1,error:`No registry entry found for "${a}"`};let f=(0,T.AH)(e)[0];if(!f)return{installed:!1,error:`"${e.name}" has no install method FLUJO supports (stdio package or HTTP remote)`};let g=(0,T.vf)(d),h=(0,T.O$)(a,e,f,g);if(c?.resolveOnly)return{installed:!1,serverName:h.serverName,plan:h};let i=(0,T.jg)(f,b);if(i.length>0)return{installed:!1,needsEnv:i,plan:h,error:`"${e.name}" requires values for: ${i.join(", ")}`};let j=(0,T.cy)((0,T.Yr)(e,f),b),k=j.name,l=await D.mcpService.loadServerConfigs();if(Array.isArray(l)&&l.some(a=>a.name===k)){at.info(`installRegistryServer: "${k}" already configured; reusing`);let{tools:a,error:b}=await D.mcpService.listServerTools(k);return{installed:!0,alreadyExisted:!0,serverName:k,plan:h,tools:(a??[]).map(a=>({name:a.name,...a.description?{description:a.description}:{}})),...b?{error:b}:{}}}at.info(`installRegistryServer: installing "${e.name}" as "${k}" (${f.kind})`);let m=await D.mcpService.updateServerConfig(k,j);if(!Array.isArray(m)&&m&&"success"in m&&!1===m.success)return{installed:!1,error:`Saving the server failed: ${m.error??"unknown error"}`};let{tools:n,error:o}=await D.mcpService.listServerTools(k),p=(n??[]).map(a=>({name:a.name,...a.description?{description:a.description}:{}}));if((c?.worksGate??(await ap()).worksGate)&&0===p.length){at.warn(`installRegistryServer: "${k}" exposed no tools${o?` (${o})`:""}; rolling back (works-gate)`);try{await D.mcpService.deleteServerConfig(k)}catch(a){at.error(`installRegistryServer: rollback of "${k}" failed`,a)}return{installed:!1,worksGateRejected:!0,serverName:k,plan:h,error:o?`"${e.name}" failed to start: ${o}`:`"${e.name}" connected but exposed no tools — rejected by the works-gate. Try a different server.`}}return{installed:!0,serverName:k,plan:h,tools:p,...o?{error:o}:{}}}async function aA(a,b,c){let d;if(!a||"string"!=typeof a)return{installed:!1,error:"A capability search query is required"};try{d=await ax(a,10)}catch(a){return{installed:!1,error:`Registry lookup failed: ${a instanceof Error?a.message:String(a)}`}}let e=await as(a,d.map(av)),f=await ap(),g=c?.minScore??f.minScore,h=c?.maxAttempts??3,i=[],j=0;for(let a of e){let d=a.candidate.registryName;if(a.score<g){i.push({name:d,score:a.score,reason:`below minScore ${g}`});break}if(0===(0,T.AH)(a.candidate.server).length){i.push({name:d,score:a.score,reason:"no supported install method"});continue}if(j>=h)break;j+=1;let e=await az(d,b,{worksGate:!0});if(c?.onAttempt)try{await c.onAttempt(e.plan,e)}catch(a){at.error("installBestForCapability: onAttempt hook failed",a)}if(e.installed)return{...e,attempts:i};let f=e.needsEnv?.length?`needs env: ${e.needsEnv.join(", ")}`:e.worksGateRejected?"works-gate rejected (no tools / failed to start)":e.error??"install failed";i.push({name:d,score:a.score,reason:f})}return{installed:!1,attempts:i,error:`No working server found for "${a}" among the top ${j} installable candidate(s).`}}let aB={requireConsent:!0,namespaceAllowlist:[],trustBrainStem:!0};function aC(a){let{caller:b,settings:c,registryName:d,allowInstall:e}=a;if("generator"===b)return!0===e?{allowed:!0,reason:"generator-allowinstall",message:"Approved by the per-generation allowInstall opt-in."}:{allowed:!1,reason:"generator-not-allowed",message:"Installing is not allowed in this generation (allowInstall is off)."};if("authoring-tool"===b&&c.trustBrainStem)return{allowed:!0,reason:"brainstem-trusted",message:"Approved: the /mcp-flows authoring tool is trusted (mcpAutoInstall.trustBrainStem)."};if(!c.requireConsent)return{allowed:!0,reason:"consent-disabled",message:"Approved: consent is not required (mcpAutoInstall.requireConsent is off)."};let f=function(a){if("string"!=typeof a)return"";let b=a.indexOf("/");return b>0?a.slice(0,b):""}(d);return""!==f&&c.namespaceAllowlist.includes(f)?{allowed:!0,reason:"namespace-allowlisted",message:`Approved: namespace "${f}" is allowlisted.`}:{allowed:!1,reason:"consent-required",message:"Consent required: review the exact command and arguments, then approve this install (SEP-1024)."}}function aD(a,b,c,d,e){return{at:new Date().toISOString(),caller:b,registryName:a.registryName,resolvedName:a.resolvedName,serverName:a.serverName,transport:a.transport,...a.command?{command:a.command}:{},...a.args?{args:a.args}:{},...a.serverUrl?{serverUrl:a.serverUrl}:{},requiredEnvNames:a.requiredEnvNames,verificationStatus:a.verificationStatus,consent:{allowed:c.allowed,reason:c.reason},installed:d,...e?{error:e}:{}}}let aE=(0,i.h)("backend/services/mcp/autoInstall");async function aF(){try{let a=await (0,K.YM)(am.Z.MCP_AUTO_INSTALL_SETTINGS,aB)??{};return{requireConsent:"boolean"==typeof a.requireConsent?a.requireConsent:aB.requireConsent,namespaceAllowlist:Array.isArray(a.namespaceAllowlist)?a.namespaceAllowlist.filter(a=>"string"==typeof a&&a.length>0):[],trustBrainStem:"boolean"==typeof a.trustBrainStem?a.trustBrainStem:aB.trustBrainStem}}catch(a){return aE.warn("Failed to load mcpAutoInstall settings; using safe defaults",a),{...aB}}}let aG="mcp-install-audit",aH=Promise.resolve();async function aI(){try{return await (0,K.YM)(aG,[])}catch(a){return aE.error("Failed to load MCP install audit log",a),[]}}async function aJ(a){let b=aH.catch(()=>{}).then(async()=>{let b=await aI();b.push(a);let c=b.slice(-300);await (0,K.VH)(aG,c)}).catch(a=>{aE.error("Failed to append MCP install audit entry",a)});aH=b,await b}let aK=(0,i.h)("backend/services/mcp/flowAuthoringTools"),aL=["list_flow_building_blocks","validate_flow_spec","create_flow","search_mcp_marketplace","install_mcp_server","install_best_mcp_server"];function aM(){return{type:"object",properties:{spec:{type:"object",description:"The FlowSpec object (see the tool description for the format)."}},required:["spec"]}}function aN(a,b=!1){return{content:[{type:"text",text:JSON.stringify(a,null,2)}],...b?{isError:!0}:{}}}async function aO(a,b){try{if("list_flow_building_blocks"===a){let a=await G();return aN(a.blocks)}if("search_mcp_marketplace"===a){let a="string"==typeof b?.query?b.query:"",c=await aw(a);return aN(c)}if("install_mcp_server"===a){let a="string"==typeof b?.name?b.name:"",c=b?.env&&"object"==typeof b.env&&!Array.isArray(b.env)?b.env:void 0,d=await az(a,c,{resolveOnly:!0});if(!d.plan)return aN(d,!0);let e=await aF(),f=aC({caller:"authoring-tool",settings:e,registryName:a}),g=(0,T.nO)(d.plan.verificationStatus)?void 0:`Unverified / self-asserted registry entry (status: ${d.plan.verificationStatus}). Registry entries are publisher-asserted — review the command before approving.`;if(await aJ(aD(d.plan,"authoring-tool",f,!1)),!f.allowed)return aN({installed:!1,consentRequired:!0,message:f.message,plan:d.plan,...g?{verificationWarning:g}:{}});let h=await az(a,c);return await aJ(aD(h.plan??d.plan,"authoring-tool",f,h.installed,h.error)),aN({...h,...g?{verificationWarning:g}:{}},!h.installed)}if("install_best_mcp_server"===a){let a="string"==typeof b?.capability?b.capability:"",c=b?.env&&"object"==typeof b.env&&!Array.isArray(b.env)?b.env:void 0;if(!a)return aN({error:'Provide a "capability" search term.'},!0);let d=await aF(),e=aC({caller:"authoring-tool",settings:d,registryName:a});if(!e.allowed){let b=(await aw(a)).find(a=>a.installable),d=b?await az(b.name,c,{resolveOnly:!0}):null;return d?.plan&&await aJ(aD(d.plan,"authoring-tool",e,!1)),aN({installed:!1,consentRequired:!0,message:e.message,...d?.plan?{plan:d.plan}:{}})}let f=await aA(a,c,{onAttempt:async(a,b)=>{a&&await aJ(aD(a,"authoring-tool",e,b.installed,b.error))}}),g=f.installed&&!(0,T.nO)(f.plan?.verificationStatus)?`Installed an unverified / self-asserted registry entry (status: ${f.plan?.verificationStatus}).`:void 0;return aN({...f,...g?{verificationWarning:g}:{}},!f.installed)}if("validate_flow_spec"===a||"create_flow"===a){let c=function(a){let b=a?.spec;if("string"==typeof b)try{return JSON.parse(b)}catch{return null}return b??null}(b);if(!c)return aN({error:'Provide a "spec" argument: a FlowSpec object (or a JSON string of one).'},!0);let d=await I(c,{save:"create_flow"===a});if(!d.success)return aN({error:d.error,issues:d.issues??[]},!0);let e=d.flows.length,f=e>1?` (plus ${e-1} nested subflow flow(s))`:"",g={flowId:d.flow.id,flowName:d.flow.name,nodeCount:d.flow.nodes.length,edgeCount:d.flow.edges.length,...e>1?{flows:d.flows.map(a=>({id:a.id,name:a.name}))}:{},validation:d.validation,..."create_flow"===a?{saved:d.saved,...d.saved?{note:`Flow "${d.flow.name}"${f} was created. It is callable as a tool on this MCP server (after a fresh tools/list) and as model "flow-${d.flow.name}" on the OpenAI-compatible endpoint.`}:{note:"NOT saved: validation found errors. Fix the issues and call create_flow again."}}:{}};return aN(g,"create_flow"===a&&!d.saved)}return aN({error:`Unknown authoring tool: ${a}`},!0)}catch(b){return aK.error("authoringCallTool failed",{toolName:a,err:b}),aN({error:`Authoring tool failed: ${b instanceof Error?b.message:String(b)}`},!0)}}var aP=c(92018);let aQ=(0,i.h)("backend/services/mcp/internalTools");function aR(a,b=!1){return{content:[{type:"text",text:"string"==typeof a?a:JSON.stringify(a,null,2)}],...b?{isError:!0}:{}}}function aS(){return[{name:"list_flow_building_blocks",description:'List everything a FlowSpec may reference in this FLUJO instance: configured models (bind to process steps), MCP servers and their tools (attach via "servers"), and existing flows (usable as subflow targets). Call this before authoring a spec.',inputSchema:{type:"object",properties:{}}},{name:"validate_flow_spec",description:`Compile and validate a FlowSpec WITHOUT saving. Returns the compiled flow's validation result (errors block saving; warnings are advisory) so you can iterate until clean, then call create_flow.
70
76
 
71
77
  ${R}`,inputSchema:aM()},{name:"create_flow",description:`Create a new FLUJO flow from a FlowSpec. The spec is compiled deterministically (layout, ids, and wiring are generated for you) and saved ONLY when validation finds zero errors — otherwise the issues are returned to fix. Use list_flow_building_blocks first to see the models, MCP servers/tools, and existing flows you may reference.
72
78
 
@@ -1 +1 @@
1
- "use strict";exports.id=4671,exports.ids=[4671],exports.modules={84671:(a,b,c)=>{c.d(b,{nC:()=>m});var d=c(49181),e=c(67512),f=c(41527),g=c(58505);function h(a){return a.data?.type??a.type??"unknown"}function i(a){return a.data?.label||a.id}function j(a){return a.data?.edgeType==="mcp"}function k(a){return a.data?.edgeType==="resource"}function l(a){return j(a)||k(a)}function m(a,b={}){let c=[],n=Array.isArray(a?.nodes)?a.nodes:[],o=Array.isArray(a?.edges)?a.edges:[],p=(a,b,d,e)=>c.push({severity:a,code:b,message:d,nodeId:e?.id,nodeLabel:e?i(e):void 0}),q=new Map((b.models??[]).map(a=>[a.id,a])),r=new Map((b.servers??[]).map(a=>[a.name,a])),s=!!b.models,t=!!b.servers,u=n.filter(a=>"start"===h(a)),v=n.filter(a=>"finish"===h(a)),w=n.filter(a=>"process"===h(a)),x=n.filter(a=>"mcp"===h(a));if(0===u.length)p("error","no-start-node","The flow has no Start node, so it has no entry point.");else if(u.length>1)for(let a of u.slice(1))p("error","multiple-start-nodes","The flow has more than one Start node; only one is allowed.",a);for(let a of(0===v.length&&p("warning","no-finish-node","The flow has no Finish node; it will run but never reaches a defined end."),w)){let b=a.data?.properties??{},c=b.boundModel;if(c){if(s&&!q.has(c)){let c="string"==typeof b.modelName&&b.modelName?` (was "${b.modelName}")`:"";p("error","process-model-missing",`Process node "${i(a)}" is bound to a model that no longer exists${c}.`,a)}}else p("error","process-missing-model",`Process node "${i(a)}" has no model bound.`,a)}for(let a of x){let b=(a.data?.properties??{}).boundServer;if(b){if(t&&!r.has(b))p("warning","mcp-server-missing",`MCP node "${i(a)}" is bound to server "${b}", which isn't in your current MCP server list. It may be offline (e.g. not connected), renamed, or removed — if it was renamed or removed, rebind this node.`,a);else if(t){let c=r.get(b);if(c.status&&"connected"!==c.status){let d="disabled"===c.status?"disabled":`not connected (${c.status})`;p("warning","mcp-server-disconnected",`MCP node "${i(a)}" is bound to server "${b}", which is currently ${d}. It will provide tools again once it reconnects.`,a)}}}else p("warning","mcp-missing-server",`MCP node "${i(a)}" is not bound to a server.`,a);o.some(b=>j(b)&&(b.source===a.id||b.target===a.id))||p("warning","mcp-node-unconnected",`MCP node "${i(a)}" is not connected to any Process node.`,a)}for(let a of n.filter(a=>"resource"===h(a))){let b=a.data?.properties??{},c="run"===b.scope?"run":"mcp",d="string"==typeof b.boundServer?b.boundServer:"",e="string"==typeof b.uri?b.uri:"",g="string"==typeof b.runName?b.runName.trim():"";"mcp"!==c||d&&e||p("warning","resource-missing-binding",`Resource node "${i(a)}" has no ${!d?"server":"resource URI"} bound; connected steps will receive nothing.`,a),"run"!==c||g||p("warning","resource-missing-binding",`Resource node "${i(a)}" is a run artifact with no name; give it a name so steps can produce/consume it.`,a),"run"===c&&g&&!(0,f.qW)(g)&&p("warning","resource-run-name",`Resource node "${i(a)}" is named "${g}", which is not a valid artifact name (letters, digits, _ and - only, not starting with a digit); it will be awkward to reference with \${res:...}.`,a),"mcp"===c&&d&&t&&!r.has(d)&&p("warning","resource-server-missing",`Resource node "${i(a)}" is bound to server "${d}", which isn't in your current MCP server list.`,a);let h=o.filter(b=>k(b)&&(b.source===a.id||b.target===a.id));0===h.length&&p("warning","resource-node-unconnected",`Resource node "${i(a)}" is not connected to any Process node.`,a);let j=h.filter(b=>b.target===a.id);"mcp"===c&&j.length>0&&p("error","resource-produce-static",`Resource node "${i(a)}" is a static MCP resource, but a step writes INTO it. Only run artifacts (scope "run") can be produced by a step — flip the edge or make this a run artifact.`,a),"run"===c&&j.length>1&&p("warning","resource-multiple-producers",`Resource node "${i(a)}" is produced by ${j.length} steps; the last writer wins. Consider one producer per artifact.`,a),"run"===c&&0===j.length&&h.length>0&&p("warning","resource-consumed-never-produced",`Resource node "${i(a)}" is consumed but no step in this flow produces it; it resolves to nothing unless an earlier run captured it.`,a)}for(let a of n.filter(a=>"signal"===h(a))){let b=a.data?.properties??{};("string"==typeof b.topic?b.topic.trim():"")||p("warning","signal-missing-topic",`Signal node "${i(a)}" has no topic; it emits nothing. Give it a topic that a flow-event trigger can listen for.`,a)}if(u.length>0){let a=function(a){let b=new Map,c=(a,c)=>{b.has(a)||b.set(a,[]),b.get(a).push(c)};for(let b of a)!l(b)&&(c(b.source,b.target),b.data?.bidirectional&&c(b.target,b.source));return b}(o);for(let b of u)a.get(b.id)?.length||p("error","start-no-outgoing","The Start node is not connected to anything; the flow cannot run.",b);let b=function(a,b){let c=new Set(a),d=[...a];for(;d.length;){let a=d.shift();for(let e of b.get(a)??[])c.has(e)||(c.add(e),d.push(e))}return c}(u.map(a=>a.id),a);for(let a of[...w,...v])b.has(a.id)||p("warning","unreachable-node",`${"finish"===h(a)?"Finish":"Process"} node "${i(a)}" is not reachable from the Start node.`,a)}for(let a of n.filter(a=>"subflow"===h(a))){let b=o.filter(b=>!l(b)&&(b.source===a.id||b.target===a.id&&!!b.data?.bidirectional));b.length>1&&p("error","subflow-multiple-outgoing",`Subflow node "${i(a)}" has ${b.length} outgoing connections; a subflow can only have one (either A > B > C or a bidirectional A <> B).`,a);let c=a.data?.properties??{},d="string"==typeof c.subflowId&&c.subflowId?c.subflowId:void 0,e=Array.isArray(c.parallelSubflowIds)?c.parallelSubflowIds.filter(a=>"string"==typeof a&&!!a):[];d&&e.length>0&&p("error","subflow-both-targets",`Subflow node "${i(a)}" sets both a single "subflowId" and "parallelSubflowIds"; use one or the other.`,a);let g=!0===c.mapOverList;g&&e.length>0&&p("error","subflow-map-and-parallel",`Subflow node "${i(a)}" combines "mapOverList" with "parallelSubflowIds"; map-over-list runs a single child once per item and cannot be combined with parallel fan-out.`,a),g&&!d&&0===e.length&&p("error","subflow-map-no-child",`Subflow node "${i(a)}" has "mapOverList" but no child flow ("subflowId"); select a flow to run once per item.`,a);let h="string"==typeof c.parallelSubflowIdsVar?c.parallelSubflowIdsVar.trim():"";if(h&&((0,f.qW)(h)||p("warning","subflow-parallel-var-name",`Subflow node "${i(a)}" reads dynamic fan-out targets from "${h}", which is not a valid variable name (letters, digits, _ and - only, not starting with a digit); it will be awkward to capture with \${var:...}.`,a),g&&p("error","subflow-map-and-parallel-var",`Subflow node "${i(a)}" combines "mapOverList" with dynamic fan-out ("parallelSubflowIdsVar"); map-over-list runs a single child once per item and cannot be combined with fan-out.`,a),n.some(a=>{let b=a.data?.properties?.captureVariable;return"string"==typeof b&&b.trim()===h})||p("warning","subflow-parallel-var-uncaptured",`Subflow node "${i(a)}" reads dynamic fan-out targets from variable "${h}", but no node captures it (captureVariable). It may be seeded by the caller; otherwise it resolves to no targets and the node falls back to its static parallel list.`,a)),e.length>0){let b=c.concurrencyLimit;"number"==typeof b&&b<1&&p("warning","subflow-concurrency-limit",`Subflow node "${i(a)}" has a concurrencyLimit of ${b}; it must be at least 1 (the runtime default will be used).`,a)}}{let a=new Map(n.map(a=>[a.id,a])),b=new Set;for(let c of o){if(l(c))continue;let d=c.data?.condition;if(!d||"object"!=typeof d)continue;let f=a.get(c.source),g=f?h(f):"unknown";if("process"!==g){p("error","edge-condition-non-process",`An edge leaving ${"unknown"===g?"an unknown":`the ${g}`} node "${f?i(f):c.source}" has a routing condition, but only process nodes can route on a condition.`,f??void 0);continue}b.add(c.source),(0,e.C)(d.kind)?"always"===d.kind||("string"!=typeof d.value||0===d.value.length?p("warning","edge-condition-value",`Process node "${i(f)}" has an outgoing edge condition with no "value"; it will never match.`,f):"regex"!==d.kind||(0,e.wM)(d.value)||p("warning","edge-condition-regex",`Process node "${i(f)}" has an outgoing edge with a regex condition that does not compile; it will never match.`,f)):p("warning","edge-condition-kind",`Process node "${i(f)}" has an outgoing edge whose condition kind "${String(d.kind)}" is unknown; it will never match.`,f)}for(let c of b){let b=a.get(c);b&&(o.some(a=>!l(a)&&(a.source===c?!a.data?.condition||"always"===a.data.condition.kind&&!a.data.condition.negate:!!(a.target===c&&a.data?.bidirectional)))||p("warning","edge-condition-no-fallback",`Process node "${i(b)}" has conditioned outgoing edges but no bare fallback edge; if no condition matches, the flow ends here.`,b))}}{let a=new Set;for(let b of n){let c=b.data?.properties?.captureVariable;if("string"==typeof c&&c.trim()){let d=c.trim();a.add(d),(0,f.qW)(d)||p("warning","capture-var-name",`${"subflow"===h(b)?"Subflow":"Process"} node "${i(b)}" captures into "${d}", which is not a valid variable name (letters, digits, _ and - only, not starting with a digit); it will be awkward to reference with \${var:...}.`,b)}}let b=["promptTemplate","isolatedPrompt"],c=new Set;for(let d of n){let e=d.data?.properties??{};for(let g of b){let b=e[g];if("string"==typeof b&&b)for(let e of(0,f.T9)(b)){if(a.has(e))continue;let b=`${d.id}:${e}`;c.has(b)||(c.add(b),p("warning","var-ref-uncaptured",`${i(d)} references \${var:${e}} but no step in this flow captures "${e}" (via captureVariable). It resolves to empty unless supplied as a run input — check for a typo or a step ordered after this one.`,d))}}}}{for(let a of n){let b=a.data?.properties?.captureKv;if("string"==typeof b&&b.trim()){let{key:c}=(0,g.g2)(b.trim());(0,g.ng)(c)||p("warning","capture-kv-name",`${"subflow"===h(a)?"Subflow":"Process"} node "${i(a)}" captures into kv "${b.trim()}", whose key is not a valid name (letters, digits, _ and - only, not starting with a digit); it will be awkward to reference with \${kv:...}.`,a)}}let a=["promptTemplate","isolatedPrompt"],b=new Set;for(let c of n){let d=c.data?.properties??{};for(let e of a){let a=d[e];if("string"==typeof a&&a)for(let d of(0,g.pf)(a)){let{key:a}=(0,g.g2)(d);if((0,g.ng)(a))continue;let e=`${c.id}:${d}`;b.has(e)||(b.add(e),p("warning","kv-ref-name",`${i(c)} references \${kv:${d}}, whose key is not a valid name (letters, digits, _ and - only, not starting with a digit); it resolves to empty.`,c))}}}}for(let a of w){let c=a.data?.properties?.promptTemplate;if("string"!=typeof c||!c)continue;let e=function(a,b,c){let d=new Map(b.map(a=>[a.id,a])),e=new Set;for(let b of c){if(!j(b))continue;let c=null;if(b.source===a?c=b.target:b.target===a&&(c=b.source),!c)continue;let f=d.get(c);if(!f||"mcp"!==h(f))continue;let g=f.data?.properties?.boundServer;"string"==typeof g&&g&&e.add(g)}return e}(a.id,n,o),f=new Set;for(let g of(0,d.A$)(c)){if("handoff"===g.server)continue;let c=`${g.kind}:${g.server}:${g.name}`;!f.has(c)&&(f.add(c),e.has(g.server)?"tool"===g.kind&&b.serverTools&&b.serverTools[g.server]&&!b.serverTools[g.server].includes(g.name)&&p("warning","tool-unavailable",`Process node "${i(a)}" references tool "${g.name}" which server "${g.server}" no longer provides.`,a):p("error","tool-pill-disconnected",`Process node "${i(a)}" references ${g.kind} "${g.name}" from server "${g.server}", but it is not connected to that server (the MCP node was removed or the server was renamed).`,a))}}let y=c.filter(a=>"error"===a.severity).length,z=c.length-y;return{issues:c,errorCount:y,warningCount:z,isRunnable:0===y}}}};
1
+ "use strict";exports.id=4671,exports.ids=[4671],exports.modules={84671:(a,b,c)=>{c.d(b,{nC:()=>m});var d=c(49181),e=c(67512),f=c(41527),g=c(58505);function h(a){return a.data?.type??a.type??"unknown"}function i(a){return a.data?.label||a.id}function j(a){return a.data?.edgeType==="mcp"}function k(a){return a.data?.edgeType==="resource"}function l(a){return j(a)||k(a)}function m(a,b={}){let c=[],n=Array.isArray(a?.nodes)?a.nodes:[],o=Array.isArray(a?.edges)?a.edges:[],p=(a,b,d,e)=>c.push({severity:a,code:b,message:d,nodeId:e?.id,nodeLabel:e?i(e):void 0}),q=new Map((b.models??[]).map(a=>[a.id,a])),r=new Map((b.servers??[]).map(a=>[a.name,a])),s=!!b.models,t=!!b.servers,u=n.filter(a=>"start"===h(a)),v=n.filter(a=>"finish"===h(a)),w=n.filter(a=>"process"===h(a)),x=n.filter(a=>"mcp"===h(a));if(0===u.length)p("error","no-start-node","The flow has no Start node, so it has no entry point.");else if(u.length>1)for(let a of u.slice(1))p("error","multiple-start-nodes","The flow has more than one Start node; only one is allowed.",a);for(let a of(0===v.length&&p("warning","no-finish-node","The flow has no Finish node; it will run but never reaches a defined end."),w)){let b=a.data?.properties??{},c=b.boundModel;if(c){if(s&&!q.has(c)){let c="string"==typeof b.modelName&&b.modelName?` (was "${b.modelName}")`:"";p("error","process-model-missing",`Process node "${i(a)}" is bound to a model that no longer exists${c}.`,a)}}else p("error","process-missing-model",`Process node "${i(a)}" has no model bound.`,a)}for(let a of x){let b=(a.data?.properties??{}).boundServer;if(b){if(t&&!r.has(b))p("warning","mcp-server-missing",`MCP node "${i(a)}" is bound to server "${b}", which isn't in your current MCP server list. It may be offline (e.g. not connected), renamed, or removed — if it was renamed or removed, rebind this node.`,a);else if(t){let c=r.get(b);if(c.status&&"connected"!==c.status){let d="disabled"===c.status?"disabled":`not connected (${c.status})`;p("warning","mcp-server-disconnected",`MCP node "${i(a)}" is bound to server "${b}", which is currently ${d}. It will provide tools again once it reconnects.`,a)}}}else p("warning","mcp-missing-server",`MCP node "${i(a)}" is not bound to a server.`,a);o.some(b=>j(b)&&(b.source===a.id||b.target===a.id))||p("warning","mcp-node-unconnected",`MCP node "${i(a)}" is not connected to any Process node.`,a)}for(let a of n.filter(a=>"resource"===h(a))){let b=a.data?.properties??{},c="run"===b.scope?"run":"mcp",d="string"==typeof b.boundServer?b.boundServer:"",e="string"==typeof b.uri?b.uri:"",g="string"==typeof b.runName?b.runName.trim():"";"mcp"!==c||d&&e||p("warning","resource-missing-binding",`Resource node "${i(a)}" has no ${!d?"server":"resource URI"} bound; connected steps will receive nothing.`,a),"run"!==c||g||p("warning","resource-missing-binding",`Resource node "${i(a)}" is a run artifact with no name; give it a name so steps can produce/consume it.`,a),"run"===c&&g&&!(0,f.qW)(g)&&p("warning","resource-run-name",`Resource node "${i(a)}" is named "${g}", which is not a valid artifact name (letters, digits, _ and - only, not starting with a digit); it will be awkward to reference with \${res:...}.`,a),"mcp"===c&&d&&t&&!r.has(d)&&p("warning","resource-server-missing",`Resource node "${i(a)}" is bound to server "${d}", which isn't in your current MCP server list.`,a);let h=o.filter(b=>k(b)&&(b.source===a.id||b.target===a.id));0===h.length&&p("warning","resource-node-unconnected",`Resource node "${i(a)}" is not connected to any Process node.`,a);let j=h.filter(b=>b.target===a.id);"mcp"===c&&j.length>0&&p("error","resource-produce-static",`Resource node "${i(a)}" is a static MCP resource, but a step writes INTO it. Only run artifacts (scope "run") can be produced by a step — flip the edge or make this a run artifact.`,a),"run"===c&&j.length>1&&p("warning","resource-multiple-producers",`Resource node "${i(a)}" is produced by ${j.length} steps; the last writer wins. Consider one producer per artifact.`,a),"run"===c&&0===j.length&&h.length>0&&p("warning","resource-consumed-never-produced",`Resource node "${i(a)}" is consumed but no step in this flow produces it; it resolves to nothing unless an earlier run captured it.`,a)}for(let a of n.filter(a=>"signal"===h(a))){let b=a.data?.properties??{};("string"==typeof b.topic?b.topic.trim():"")||p("warning","signal-missing-topic",`Signal node "${i(a)}" has no topic; it emits nothing. Give it a topic that a flow-event trigger can listen for.`,a)}if(u.length>0){let a=function(a){let b=new Map,c=(a,c)=>{b.has(a)||b.set(a,[]),b.get(a).push(c)};for(let b of a)!l(b)&&(c(b.source,b.target),b.data?.bidirectional&&c(b.target,b.source));return b}(o);for(let b of u)a.get(b.id)?.length||p("error","start-no-outgoing","The Start node is not connected to anything; the flow cannot run.",b);let b=function(a,b){let c=new Set(a),d=[...a];for(;d.length;){let a=d.shift();for(let e of b.get(a)??[])c.has(e)||(c.add(e),d.push(e))}return c}(u.map(a=>a.id),a);for(let a of[...w,...v])b.has(a.id)||p("warning","unreachable-node",`${"finish"===h(a)?"Finish":"Process"} node "${i(a)}" is not reachable from the Start node.`,a)}for(let a of n.filter(a=>"subflow"===h(a))){let b=o.filter(b=>!l(b)&&(b.source===a.id||b.target===a.id&&!!b.data?.bidirectional));b.length>1&&p("error","subflow-multiple-outgoing",`Subflow node "${i(a)}" has ${b.length} outgoing connections; a subflow can only have one (either A > B > C or a bidirectional A <> B).`,a);let c=a.data?.properties??{},d="string"==typeof c.subflowId&&c.subflowId?c.subflowId:void 0,e=Array.isArray(c.parallelSubflowIds)?c.parallelSubflowIds.filter(a=>"string"==typeof a&&!!a):[];d&&e.length>0&&p("error","subflow-both-targets",`Subflow node "${i(a)}" sets both a single "subflowId" and "parallelSubflowIds"; use one or the other.`,a);let g=!0===c.mapOverList;g&&e.length>0&&p("error","subflow-map-and-parallel",`Subflow node "${i(a)}" combines "mapOverList" with "parallelSubflowIds"; map-over-list runs a single child once per item and cannot be combined with parallel fan-out.`,a),g&&!d&&0===e.length&&p("error","subflow-map-no-child",`Subflow node "${i(a)}" has "mapOverList" but no child flow ("subflowId"); select a flow to run once per item.`,a);let h="string"==typeof c.parallelSubflowIdsVar?c.parallelSubflowIdsVar.trim():"";if(h&&((0,f.qW)(h)||p("warning","subflow-parallel-var-name",`Subflow node "${i(a)}" reads dynamic fan-out targets from "${h}", which is not a valid variable name (letters, digits, _ and - only, not starting with a digit); it will be awkward to capture with \${var:...}.`,a),g&&p("error","subflow-map-and-parallel-var",`Subflow node "${i(a)}" combines "mapOverList" with dynamic fan-out ("parallelSubflowIdsVar"); map-over-list runs a single child once per item and cannot be combined with fan-out.`,a),n.some(a=>{let b=a.data?.properties?.captureVariable;return"string"==typeof b&&b.trim()===h})||p("warning","subflow-parallel-var-uncaptured",`Subflow node "${i(a)}" reads dynamic fan-out targets from variable "${h}", but no node captures it (captureVariable). It may be seeded by the caller; otherwise it resolves to no targets and the node falls back to its static parallel list.`,a)),!0===c.allowCallerFanout&&g&&p("error","subflow-map-and-caller-fanout",`Subflow node "${i(a)}" combines "mapOverList" with agentic fan-out ("allowCallerFanout"); map-over-list runs a single child once per item and cannot be combined with fan-out.`,a),e.length>0){let b=c.concurrencyLimit;"number"==typeof b&&b<1&&p("warning","subflow-concurrency-limit",`Subflow node "${i(a)}" has a concurrencyLimit of ${b}; it must be at least 1 (the runtime default will be used).`,a)}}{let a=new Map(n.map(a=>[a.id,a])),b=new Set;for(let c of o){if(l(c))continue;let d=c.data?.condition;if(!d||"object"!=typeof d)continue;let f=a.get(c.source),g=f?h(f):"unknown";if("process"!==g){p("error","edge-condition-non-process",`An edge leaving ${"unknown"===g?"an unknown":`the ${g}`} node "${f?i(f):c.source}" has a routing condition, but only process nodes can route on a condition.`,f??void 0);continue}b.add(c.source),(0,e.C)(d.kind)?"always"===d.kind||("string"!=typeof d.value||0===d.value.length?p("warning","edge-condition-value",`Process node "${i(f)}" has an outgoing edge condition with no "value"; it will never match.`,f):"regex"!==d.kind||(0,e.wM)(d.value)||p("warning","edge-condition-regex",`Process node "${i(f)}" has an outgoing edge with a regex condition that does not compile; it will never match.`,f)):p("warning","edge-condition-kind",`Process node "${i(f)}" has an outgoing edge whose condition kind "${String(d.kind)}" is unknown; it will never match.`,f)}for(let c of b){let b=a.get(c);b&&(o.some(a=>!l(a)&&(a.source===c?!a.data?.condition||"always"===a.data.condition.kind&&!a.data.condition.negate:!!(a.target===c&&a.data?.bidirectional)))||p("warning","edge-condition-no-fallback",`Process node "${i(b)}" has conditioned outgoing edges but no bare fallback edge; if no condition matches, the flow ends here.`,b))}}{let a=new Set;for(let b of n){let c=b.data?.properties?.captureVariable;if("string"==typeof c&&c.trim()){let d=c.trim();a.add(d),(0,f.qW)(d)||p("warning","capture-var-name",`${"subflow"===h(b)?"Subflow":"Process"} node "${i(b)}" captures into "${d}", which is not a valid variable name (letters, digits, _ and - only, not starting with a digit); it will be awkward to reference with \${var:...}.`,b)}}let b=["promptTemplate","isolatedPrompt"],c=new Set;for(let d of n){let e=d.data?.properties??{};for(let g of b){let b=e[g];if("string"==typeof b&&b)for(let e of(0,f.T9)(b)){if(a.has(e))continue;let b=`${d.id}:${e}`;c.has(b)||(c.add(b),p("warning","var-ref-uncaptured",`${i(d)} references \${var:${e}} but no step in this flow captures "${e}" (via captureVariable). It resolves to empty unless supplied as a run input — check for a typo or a step ordered after this one.`,d))}}}}{for(let a of n){let b=a.data?.properties?.captureKv;if("string"==typeof b&&b.trim()){let{key:c}=(0,g.g2)(b.trim());(0,g.ng)(c)||p("warning","capture-kv-name",`${"subflow"===h(a)?"Subflow":"Process"} node "${i(a)}" captures into kv "${b.trim()}", whose key is not a valid name (letters, digits, _ and - only, not starting with a digit); it will be awkward to reference with \${kv:...}.`,a)}}let a=["promptTemplate","isolatedPrompt"],b=new Set;for(let c of n){let d=c.data?.properties??{};for(let e of a){let a=d[e];if("string"==typeof a&&a)for(let d of(0,g.pf)(a)){let{key:a}=(0,g.g2)(d);if((0,g.ng)(a))continue;let e=`${c.id}:${d}`;b.has(e)||(b.add(e),p("warning","kv-ref-name",`${i(c)} references \${kv:${d}}, whose key is not a valid name (letters, digits, _ and - only, not starting with a digit); it resolves to empty.`,c))}}}}for(let a of w){let c=a.data?.properties?.promptTemplate;if("string"!=typeof c||!c)continue;let e=function(a,b,c){let d=new Map(b.map(a=>[a.id,a])),e=new Set;for(let b of c){if(!j(b))continue;let c=null;if(b.source===a?c=b.target:b.target===a&&(c=b.source),!c)continue;let f=d.get(c);if(!f||"mcp"!==h(f))continue;let g=f.data?.properties?.boundServer;"string"==typeof g&&g&&e.add(g)}return e}(a.id,n,o),f=new Set;for(let g of(0,d.A$)(c)){if("handoff"===g.server)continue;let c=`${g.kind}:${g.server}:${g.name}`;!f.has(c)&&(f.add(c),e.has(g.server)?"tool"===g.kind&&b.serverTools&&b.serverTools[g.server]&&!b.serverTools[g.server].includes(g.name)&&p("warning","tool-unavailable",`Process node "${i(a)}" references tool "${g.name}" which server "${g.server}" no longer provides.`,a):p("error","tool-pill-disconnected",`Process node "${i(a)}" references ${g.kind} "${g.name}" from server "${g.server}", but it is not connected to that server (the MCP node was removed or the server was renamed).`,a))}}let y=c.filter(a=>"error"===a.severity).length,z=c.length-y;return{issues:c,errorCount:y,warningCount:z,isRunnable:0===y}}}};
@@ -1 +1 @@
1
- "use strict";exports.id=472,exports.ids=[472],exports.modules={2560:(a,b,c)=>{c.d(b,{Xk:()=>e});var d=c(38301);function e(a,b){let[c,e]=(0,d.useState)(()=>b);return[c,(0,d.useCallback)(a=>{e(b=>"function"==typeof a?a(b):a)},[a])]}},7329:(a,b,c)=>{function d(a,b){let c=[],d=new Map;for(let e of a){let{key:a,label:f}=b(e),g=d.get(a);g||(g={key:a,label:f,items:[]},d.set(a,g),c.push(g)),g.items.push(e)}return c}function e(a){let b=(a??"").trim().charAt(0).toUpperCase();return b>="A"&&b<="Z"?{key:`letter:${b}`,label:b}:{key:"letter:#",label:"#"}}function f(a,b,c="Ungrouped"){let d=new Map,e=[];for(let c of a){let a=b(c)?.trim();if(a){let b=d.get(a);b?b.push(c):d.set(a,[c])}else e.push(c)}let g=Array.from(d.keys()).sort((a,b)=>a.localeCompare(b)).map(a=>({key:`folder:${a}`,label:a,items:d.get(a)}));return e.length>0&&g.push({key:"folder:__ungrouped__",label:c,items:e}),g}function g(a,b){let c=new Set;for(let d of a){let a=b(d)?.trim();a&&c.add(a)}return Array.from(c).sort((a,b)=>a.localeCompare(b))}c.d(b,{Az:()=>d,Je:()=>f,po:()=>e,yF:()=>g})},28055:(a,b,c)=>{c.d(b,{S:()=>g});var d=c(50648);let e=(0,c(19815).h)("frontend/services/mcp/index");class f{async loadServerConfigs(){try{let a=await fetch("/api/mcp/servers"),b=await a.json();if(!a.ok||b&&b.error){let a=b&&b.error||"Failed to load server configs";return e.warn("Failed to load server configs:",a),{error:a}}return b}catch(a){return e.warn("Failed to load server configs:",a),{error:"Failed to load server configs"}}}async listServerTools(a){try{let b=this.toolsCache.get(a),c=Date.now();if(b&&c-b.timestamp<this.CACHE_TTL)return e.debug(`Using cached tools for server ${a}`),{tools:b.tools};let d=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/tools`),f=await d.json();if(f.error)return e.warn(`Error listing tools for server ${a}:`,f.error),{tools:[],error:f.error};let g=Array.isArray(f.tools)?f.tools:[];return this.toolsCache.set(a,{tools:g,timestamp:c}),{tools:g}}catch(b){return e.warn(`Failed to list tools for server ${a}:`,b),{tools:[],error:`Failed to list tools: ${b instanceof Error?b.message:"Unknown error"}`}}}clearToolsCache(a){a?(this.toolsCache.delete(a),e.debug(`Cleared tools cache for server ${a}`)):(this.toolsCache.clear(),e.debug("Cleared all tools cache"))}async listServerResources(a){try{let b=this.resourcesCache.get(a),c=Date.now();if(b&&c-b.timestamp<this.CACHE_TTL)return e.debug(`Using cached resources for server ${a}`),b.data;let d=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/resources`),f=await d.json(),g={resources:Array.isArray(f.resources)?f.resources:[],resourceTemplates:Array.isArray(f.resourceTemplates)?f.resourceTemplates:[],error:f.error};return g.error||this.resourcesCache.set(a,{data:g,timestamp:c}),g}catch(b){return e.warn(`Failed to list resources for server ${a}:`,b),{resources:[],resourceTemplates:[],error:`Failed to list resources: ${b instanceof Error?b.message:"Unknown error"}`}}}async readResource(a,b){try{let c=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/resources/read?uri=${encodeURIComponent(b)}`);return await c.json()}catch(c){return e.warn(`Failed to read resource ${b} on server ${a}:`,c),{success:!1,error:"Failed to read resource"}}}async listServerPrompts(a){try{let b=this.promptsCache.get(a),c=Date.now();if(b&&c-b.timestamp<this.CACHE_TTL)return e.debug(`Using cached prompts for server ${a}`),{prompts:b.prompts};let d=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/prompts`),f=await d.json();if(f.error)return e.warn(`Error listing prompts for server ${a}:`,f.error),{prompts:[],error:f.error};let g=Array.isArray(f.prompts)?f.prompts:[];return this.promptsCache.set(a,{prompts:g,timestamp:c}),{prompts:g}}catch(b){return e.warn(`Failed to list prompts for server ${a}:`,b),{prompts:[],error:`Failed to list prompts: ${b instanceof Error?b.message:"Unknown error"}`}}}async getPrompt(a,b,c){try{let d=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/prompts/get`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:b,arguments:c})});return await d.json()}catch(c){return e.warn(`Failed to get prompt ${b} on server ${a}:`,c),{success:!1,error:"Failed to get prompt"}}}clearCapabilitiesCache(a){a?(this.resourcesCache.delete(a),this.promptsCache.delete(a)):(this.resourcesCache.clear(),this.promptsCache.clear())}async callTool(a,b,c,d){try{let e=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/tools/${encodeURIComponent(b)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({args:c,timeout:d})});return await e.json()}catch(c){return e.warn(`Failed to call tool ${b} on server ${a}:`,c),{error:"Failed to call tool"}}}async updateServerConfig(a,b){try{let c=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(b)}),d=await c.json();if(c.ok)return e.info(`Successfully updated server config for ${a}`),{success:!0,data:d};if(void 0!==b.disabled)return e.info(`Config update for ${a} treated as success for toggle operation`),{success:!0,data:{...b,name:a},_originalError:d.error};return e.warn(`Failed to update server config for ${a}:`,d.error),{success:!1,error:d.error}}catch(b){return e.warn(`Failed to update server config for ${a}:`,b),{error:"Failed to update server config"}}}async testConnection(a){try{let b=await fetch("/api/mcp/test-connection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});return await b.json()}catch(b){return e.warn(`Failed to test connection for ${a.name}:`,b),{success:!1,error:`Failed to reach the FLUJO backend to run the connection test: ${b instanceof Error?b.message:"Unknown error"}`}}}async testConnectionStreaming(a,b){try{let c=await fetch("/api/mcp/test-connection/stream",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!c.ok||!c.body)return e.warn(`Streaming test-connection unavailable (status ${c.status}); falling back`),this.testConnection(a);let f=null;if(await (0,d.z)(c,a=>{b(a),"result"===a.type&&(f={success:a.success,error:a.error,requiresAuthentication:a.requiresAuthentication,data:a.data})}),f)return f;return e.warn("Streaming test-connection ended without a result event"),{success:!1,error:"The connection test stream ended unexpectedly without a result."}}catch(b){return e.warn(`Streaming test connection failed for ${a.name}; falling back to non-streaming:`,b),this.testConnection(a)}}async getServerStatus(a){try{let b=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/status`,{headers:{Accept:"application/json"}}),c=await b.json();if(c.error)return e.warn(`Error getting status for server ${a}:`,c.error),{status:"error",message:c.error};return c}catch(b){return e.warn(`Failed to get status for server ${a}:`,b),{status:"error",message:`Failed to get server status: ${b instanceof Error?b.message:"Unknown error"}`}}}async deleteServerConfig(a){try{let b=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}`,{method:"DELETE"});return await b.json()}catch(b){return e.warn(`Failed to delete server config for ${a}:`,b),{error:"Failed to delete server config"}}}async retryServer(a){return e.debug(`Retrying server status for: ${a}`),this.getServerStatus(a)}async restartServer(a){e.debug(`Restarting server: ${a}`);try{let b=await this.updateServerConfig(a,{disabled:!0});if(b.error)return e.warn(`Failed to disable server ${a} during restart:`,b.error),{error:`Failed to restart server: ${b.error}`};await new Promise(a=>setTimeout(a,1e3));let c=await this.updateServerConfig(a,{disabled:!1});if(c.error)return e.warn(`Failed to enable server ${a} during restart:`,c.error),{error:`Failed to restart server: ${c.error}`};return e.info(`Successfully restarted server ${a}`),{success:!0}}catch(b){return e.warn(`Failed to restart server ${a}:`,b),{error:`Failed to restart server: ${b instanceof Error?b.message:"Unknown error"}`}}}constructor(){this.toolsCache=new Map,this.resourcesCache=new Map,this.promptsCache=new Map,this.CACHE_TTL=6e4}}let g=new f},50648:(a,b,c)=>{c.d(b,{z:()=>d});async function d(a,b){if(!a.body||"function"!=typeof a.body.getReader)throw Error("Response has no readable body to stream");let c=a.body.getReader(),d=new TextDecoder,e=function(){let a="",b=(a,b)=>{let c=a.trim();if(c)try{b.push(JSON.parse(c))}catch{}};return{push(c){let d;a+=c;let e=[];for(;(d=a.indexOf("\n"))>=0;)b(a.slice(0,d),e),a=a.slice(d+1);return e},flush(){let c=[];return b(a,c),a="",c}}}();try{for(;;){let{value:a,done:f}=await c.read();if(f)break;for(let c of e.push(d.decode(a,{stream:!0})))b(c)}let a=d.decode();if(a)for(let c of e.push(a))b(c);for(let a of e.flush())b(a)}finally{try{c.releaseLock()}catch{}}}},59737:(a,b,c)=>{c.d(b,{A:()=>m});var d=c(21124);c(38301);var e=c(60839),f=c(81637),g=c(44079),h=c(79515),i=c(41893),j=c(99561),k=c(17332),l=c(18467);let m=({label:a,count:b,expanded:c,onToggle:m,showFolderIcon:n=!1,children:o})=>{let p=(0,e.A)();return(0,d.jsxs)(f.default,{sx:{mb:1.5},children:[(0,d.jsxs)(f.default,{role:"button",tabIndex:0,"aria-expanded":c,onClick:m,onKeyDown:a=>{("Enter"===a.key||" "===a.key)&&(a.preventDefault(),m())},sx:{display:"flex",alignItems:"center",gap:1,px:1,py:.75,borderRadius:1,cursor:"pointer",userSelect:"none",backgroundColor:p.palette.background.default,border:`1px solid ${p.palette.divider}`,"&:hover":{backgroundColor:p.palette.action.hover}},children:[(0,d.jsx)(g.A,{size:"small","aria-label":c?"Collapse section":"Expand section",onClick:a=>{a.stopPropagation(),m()},sx:{transition:"transform 0.2s",transform:c?"rotate(0deg)":"rotate(-90deg)"},children:(0,d.jsx)(k.A,{fontSize:"small"})}),n&&(0,d.jsx)(l.A,{fontSize:"small",sx:{color:"text.secondary"}}),(0,d.jsx)(h.default,{variant:"subtitle2",sx:{fontWeight:600},children:a}),(0,d.jsx)(i.A,{label:b,size:"small",sx:{height:20,fontSize:"0.7rem"}})]}),(0,d.jsx)(j.A,{in:c,unmountOnExit:!0,children:(0,d.jsx)(f.default,{sx:{pt:1.5},children:o})})]})}},61615:(a,b,c)=>{c.d(b,{default:()=>g});var d=c(21124);c(38301);var e=c(81637),f=c(8521);let g=({size:a="medium",color:b="primary",className:c="",sx:g={}})=>{let h="white"===b?{color:"#fff",...g}:g;return(0,d.jsxs)(e.default,{component:"span",sx:{display:"inline-block",lineHeight:0,...h},className:c,role:"status",children:[(0,d.jsx)(f.default,{size:{small:16,medium:24,large:36}[a],color:"white"===b?"inherit":b,"aria-hidden":"true"}),(0,d.jsx)(e.default,{component:"span",sx:{position:"absolute",width:"1px",height:"1px",padding:0,margin:"-1px",overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:0},children:"Loading..."})]})}},78352:(a,b,c)=>{c.d(b,{A:()=>u});var d=c(21124),e=c(38301),f=c(7118),g=c(94367),h=c(50553),i=c(84233),j=c(91059),k=c(40512),l=c(83899),m=c(52327),n=c(37674),o=c(19925),p=c(68730),q=c(36698),r=c(18467),s=c(31973),t=c(354);let u=({anchorEl:a,open:b,currentFolder:c,folders:u,onClose:v,onAssign:w})=>{let[x,y]=(0,e.useState)(!1),[z,A]=(0,e.useState)(""),B=a=>{w(a),v()},C=()=>{let a=z.trim();a&&(y(!1),B(a))};return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsxs)(f.A,{anchorEl:a,open:b,onClose:v,children:[u.length>0&&u.map(a=>(0,d.jsxs)(g.A,{onClick:()=>B(a),selected:a===c,children:[(0,d.jsx)(h.A,{children:a===c?(0,d.jsx)(q.A,{fontSize:"small"}):(0,d.jsx)(r.A,{fontSize:"small"})}),(0,d.jsx)(i.A,{primary:a})]},a)),u.length>0&&(0,d.jsx)(j.A,{}),(0,d.jsxs)(g.A,{onClick:()=>{A(""),y(!0)},children:[(0,d.jsx)(h.A,{children:(0,d.jsx)(s.A,{fontSize:"small"})}),(0,d.jsx)(i.A,{primary:"New folder…"})]}),c&&(0,d.jsxs)(g.A,{onClick:()=>B(void 0),children:[(0,d.jsx)(h.A,{children:(0,d.jsx)(t.A,{fontSize:"small"})}),(0,d.jsx)(i.A,{primary:"Remove from folder"})]})]}),(0,d.jsxs)(k.A,{open:x,onClose:()=>y(!1),maxWidth:"xs",fullWidth:!0,children:[(0,d.jsx)(l.A,{children:"New folder"}),(0,d.jsx)(m.A,{children:(0,d.jsx)(n.A,{autoFocus:!0,margin:"dense",label:"Folder name",fullWidth:!0,value:z,onChange:a=>A(a.target.value),onKeyDown:a=>{"Enter"===a.key&&(a.preventDefault(),C())}})}),(0,d.jsxs)(o.A,{children:[(0,d.jsx)(p.A,{onClick:()=>y(!1),children:"Cancel"}),(0,d.jsx)(p.A,{variant:"contained",disabled:!z.trim(),onClick:C,children:"Create"})]})]})]})}},83434:(a,b,c)=>{c.d(b,{N:()=>e,X:()=>d}),(0,c(19815).h)("frontend/services/model");let d=()=>{throw Error("ModelService can only be used in browser environment")},e=new Proxy({},{get(a,b){let c=d(),e=c[b];return"function"==typeof e?e.bind(c):e}})}};
1
+ "use strict";exports.id=472,exports.ids=[472],exports.modules={2560:(a,b,c)=>{c.d(b,{Xk:()=>e});var d=c(38301);function e(a,b){let[c,e]=(0,d.useState)(()=>b);return[c,(0,d.useCallback)(a=>{e(b=>"function"==typeof a?a(b):a)},[a])]}},7329:(a,b,c)=>{function d(a,b){let c=[],d=new Map;for(let e of a){let{key:a,label:f}=b(e),g=d.get(a);g||(g={key:a,label:f,items:[]},d.set(a,g),c.push(g)),g.items.push(e)}return c}function e(a){let b=(a??"").trim().charAt(0).toUpperCase();return b>="A"&&b<="Z"?{key:`letter:${b}`,label:b}:{key:"letter:#",label:"#"}}function f(a,b,c="Ungrouped"){let d=new Map,e=[];for(let c of a){let a=b(c)?.trim();if(a){let b=d.get(a);b?b.push(c):d.set(a,[c])}else e.push(c)}let g=Array.from(d.keys()).sort((a,b)=>a.localeCompare(b)).map(a=>({key:`folder:${a}`,label:a,items:d.get(a)}));return e.length>0&&g.push({key:"folder:__ungrouped__",label:c,items:e}),g}function g(a,b){let c=new Set;for(let d of a){let a=b(d)?.trim();a&&c.add(a)}return Array.from(c).sort((a,b)=>a.localeCompare(b))}c.d(b,{Az:()=>d,Je:()=>f,po:()=>e,yF:()=>g})},28055:(a,b,c)=>{c.d(b,{S:()=>g});var d=c(50648);let e=(0,c(19815).h)("frontend/services/mcp/index");class f{async loadServerConfigs(){try{let a=await fetch("/api/mcp/servers"),b=await a.json();if(!a.ok||b&&b.error){let a=b&&b.error||"Failed to load server configs";return e.warn("Failed to load server configs:",a),{error:a}}return b}catch(a){return e.warn("Failed to load server configs:",a),{error:"Failed to load server configs"}}}async listServerTools(a){try{let b=this.toolsCache.get(a),c=Date.now();if(b&&c-b.timestamp<this.CACHE_TTL)return e.debug(`Using cached tools for server ${a}`),{tools:b.tools};let d=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/tools`),f=await d.json();if(f.error)return e.warn(`Error listing tools for server ${a}:`,f.error),{tools:[],error:f.error};let g=Array.isArray(f.tools)?f.tools:[];return this.toolsCache.set(a,{tools:g,timestamp:c}),{tools:g}}catch(b){return e.warn(`Failed to list tools for server ${a}:`,b),{tools:[],error:`Failed to list tools: ${b instanceof Error?b.message:"Unknown error"}`}}}clearToolsCache(a){a?(this.toolsCache.delete(a),e.debug(`Cleared tools cache for server ${a}`)):(this.toolsCache.clear(),e.debug("Cleared all tools cache"))}async listServerResources(a){try{let b=this.resourcesCache.get(a),c=Date.now();if(b&&c-b.timestamp<this.CACHE_TTL)return e.debug(`Using cached resources for server ${a}`),b.data;let d=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/resources`),f=await d.json(),g={resources:Array.isArray(f.resources)?f.resources:[],resourceTemplates:Array.isArray(f.resourceTemplates)?f.resourceTemplates:[],error:f.error};return g.error||this.resourcesCache.set(a,{data:g,timestamp:c}),g}catch(b){return e.warn(`Failed to list resources for server ${a}:`,b),{resources:[],resourceTemplates:[],error:`Failed to list resources: ${b instanceof Error?b.message:"Unknown error"}`}}}async readResource(a,b){try{let c=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/resources/read?uri=${encodeURIComponent(b)}`);return await c.json()}catch(c){return e.warn(`Failed to read resource ${b} on server ${a}:`,c),{success:!1,error:"Failed to read resource"}}}async listServerPrompts(a){try{let b=this.promptsCache.get(a),c=Date.now();if(b&&c-b.timestamp<this.CACHE_TTL)return e.debug(`Using cached prompts for server ${a}`),{prompts:b.prompts};let d=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/prompts`),f=await d.json();if(f.error)return e.warn(`Error listing prompts for server ${a}:`,f.error),{prompts:[],error:f.error};let g=Array.isArray(f.prompts)?f.prompts:[];return this.promptsCache.set(a,{prompts:g,timestamp:c}),{prompts:g}}catch(b){return e.warn(`Failed to list prompts for server ${a}:`,b),{prompts:[],error:`Failed to list prompts: ${b instanceof Error?b.message:"Unknown error"}`}}}async getPrompt(a,b,c){try{let d=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/prompts/get`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:b,arguments:c})});return await d.json()}catch(c){return e.warn(`Failed to get prompt ${b} on server ${a}:`,c),{success:!1,error:"Failed to get prompt"}}}clearCapabilitiesCache(a){a?(this.resourcesCache.delete(a),this.promptsCache.delete(a)):(this.resourcesCache.clear(),this.promptsCache.clear())}async callTool(a,b,c,d){try{let e=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/tools/${encodeURIComponent(b)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({args:c,timeout:d})});return await e.json()}catch(c){return e.warn(`Failed to call tool ${b} on server ${a}:`,c),{error:"Failed to call tool"}}}async updateServerConfig(a,b){try{let c=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(b)}),d=await c.json();if(c.ok)return e.info(`Successfully updated server config for ${a}`),{success:!0,data:d};if(void 0!==b.disabled)return e.info(`Config update for ${a} treated as success for toggle operation`),{success:!0,data:{...b,name:a},_originalError:d.error};return e.warn(`Failed to update server config for ${a}:`,d.error),{success:!1,error:d.error}}catch(b){return e.warn(`Failed to update server config for ${a}:`,b),{error:"Failed to update server config"}}}async testConnection(a,b){try{let c=await fetch("/api/mcp/test-connection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...a,storedName:b})});return await c.json()}catch(b){return e.warn(`Failed to test connection for ${a.name}:`,b),{success:!1,error:`Failed to reach the FLUJO backend to run the connection test: ${b instanceof Error?b.message:"Unknown error"}`}}}async testConnectionStreaming(a,b,c){try{let f=await fetch("/api/mcp/test-connection/stream",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...a,storedName:c})});if(!f.ok||!f.body)return e.warn(`Streaming test-connection unavailable (status ${f.status}); falling back`),this.testConnection(a,c);let g=null;if(await (0,d.z)(f,a=>{b(a),"result"===a.type&&(g={success:a.success,error:a.error,requiresAuthentication:a.requiresAuthentication,data:a.data})}),g)return g;return e.warn("Streaming test-connection ended without a result event"),{success:!1,error:"The connection test stream ended unexpectedly without a result."}}catch(b){return e.warn(`Streaming test connection failed for ${a.name}; falling back to non-streaming:`,b),this.testConnection(a,c)}}async getServerStatus(a){try{let b=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}/status`,{headers:{Accept:"application/json"}}),c=await b.json();if(c.error)return e.warn(`Error getting status for server ${a}:`,c.error),{status:"error",message:c.error};return c}catch(b){return e.warn(`Failed to get status for server ${a}:`,b),{status:"error",message:`Failed to get server status: ${b instanceof Error?b.message:"Unknown error"}`}}}async deleteServerConfig(a){try{let b=await fetch(`/api/mcp/servers/${encodeURIComponent(a)}`,{method:"DELETE"});return await b.json()}catch(b){return e.warn(`Failed to delete server config for ${a}:`,b),{error:"Failed to delete server config"}}}async retryServer(a){return e.debug(`Retrying server status for: ${a}`),this.getServerStatus(a)}async restartServer(a){e.debug(`Restarting server: ${a}`);try{let b=await this.updateServerConfig(a,{disabled:!0});if(b.error)return e.warn(`Failed to disable server ${a} during restart:`,b.error),{error:`Failed to restart server: ${b.error}`};await new Promise(a=>setTimeout(a,1e3));let c=await this.updateServerConfig(a,{disabled:!1});if(c.error)return e.warn(`Failed to enable server ${a} during restart:`,c.error),{error:`Failed to restart server: ${c.error}`};return e.info(`Successfully restarted server ${a}`),{success:!0}}catch(b){return e.warn(`Failed to restart server ${a}:`,b),{error:`Failed to restart server: ${b instanceof Error?b.message:"Unknown error"}`}}}constructor(){this.toolsCache=new Map,this.resourcesCache=new Map,this.promptsCache=new Map,this.CACHE_TTL=6e4}}let g=new f},50648:(a,b,c)=>{c.d(b,{z:()=>d});async function d(a,b){if(!a.body||"function"!=typeof a.body.getReader)throw Error("Response has no readable body to stream");let c=a.body.getReader(),d=new TextDecoder,e=function(){let a="",b=(a,b)=>{let c=a.trim();if(c)try{b.push(JSON.parse(c))}catch{}};return{push(c){let d;a+=c;let e=[];for(;(d=a.indexOf("\n"))>=0;)b(a.slice(0,d),e),a=a.slice(d+1);return e},flush(){let c=[];return b(a,c),a="",c}}}();try{for(;;){let{value:a,done:f}=await c.read();if(f)break;for(let c of e.push(d.decode(a,{stream:!0})))b(c)}let a=d.decode();if(a)for(let c of e.push(a))b(c);for(let a of e.flush())b(a)}finally{try{c.releaseLock()}catch{}}}},59737:(a,b,c)=>{c.d(b,{A:()=>m});var d=c(21124);c(38301);var e=c(60839),f=c(81637),g=c(44079),h=c(79515),i=c(41893),j=c(99561),k=c(17332),l=c(18467);let m=({label:a,count:b,expanded:c,onToggle:m,showFolderIcon:n=!1,children:o})=>{let p=(0,e.A)();return(0,d.jsxs)(f.default,{sx:{mb:1.5},children:[(0,d.jsxs)(f.default,{role:"button",tabIndex:0,"aria-expanded":c,onClick:m,onKeyDown:a=>{("Enter"===a.key||" "===a.key)&&(a.preventDefault(),m())},sx:{display:"flex",alignItems:"center",gap:1,px:1,py:.75,borderRadius:1,cursor:"pointer",userSelect:"none",backgroundColor:p.palette.background.default,border:`1px solid ${p.palette.divider}`,"&:hover":{backgroundColor:p.palette.action.hover}},children:[(0,d.jsx)(g.A,{size:"small","aria-label":c?"Collapse section":"Expand section",onClick:a=>{a.stopPropagation(),m()},sx:{transition:"transform 0.2s",transform:c?"rotate(0deg)":"rotate(-90deg)"},children:(0,d.jsx)(k.A,{fontSize:"small"})}),n&&(0,d.jsx)(l.A,{fontSize:"small",sx:{color:"text.secondary"}}),(0,d.jsx)(h.default,{variant:"subtitle2",sx:{fontWeight:600},children:a}),(0,d.jsx)(i.A,{label:b,size:"small",sx:{height:20,fontSize:"0.7rem"}})]}),(0,d.jsx)(j.A,{in:c,unmountOnExit:!0,children:(0,d.jsx)(f.default,{sx:{pt:1.5},children:o})})]})}},61615:(a,b,c)=>{c.d(b,{default:()=>g});var d=c(21124);c(38301);var e=c(81637),f=c(8521);let g=({size:a="medium",color:b="primary",className:c="",sx:g={}})=>{let h="white"===b?{color:"#fff",...g}:g;return(0,d.jsxs)(e.default,{component:"span",sx:{display:"inline-block",lineHeight:0,...h},className:c,role:"status",children:[(0,d.jsx)(f.default,{size:{small:16,medium:24,large:36}[a],color:"white"===b?"inherit":b,"aria-hidden":"true"}),(0,d.jsx)(e.default,{component:"span",sx:{position:"absolute",width:"1px",height:"1px",padding:0,margin:"-1px",overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:0},children:"Loading..."})]})}},78352:(a,b,c)=>{c.d(b,{A:()=>u});var d=c(21124),e=c(38301),f=c(7118),g=c(94367),h=c(50553),i=c(84233),j=c(91059),k=c(40512),l=c(83899),m=c(52327),n=c(37674),o=c(19925),p=c(68730),q=c(36698),r=c(18467),s=c(31973),t=c(354);let u=({anchorEl:a,open:b,currentFolder:c,folders:u,onClose:v,onAssign:w})=>{let[x,y]=(0,e.useState)(!1),[z,A]=(0,e.useState)(""),B=a=>{w(a),v()},C=()=>{let a=z.trim();a&&(y(!1),B(a))};return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsxs)(f.A,{anchorEl:a,open:b,onClose:v,children:[u.length>0&&u.map(a=>(0,d.jsxs)(g.A,{onClick:()=>B(a),selected:a===c,children:[(0,d.jsx)(h.A,{children:a===c?(0,d.jsx)(q.A,{fontSize:"small"}):(0,d.jsx)(r.A,{fontSize:"small"})}),(0,d.jsx)(i.A,{primary:a})]},a)),u.length>0&&(0,d.jsx)(j.A,{}),(0,d.jsxs)(g.A,{onClick:()=>{A(""),y(!0)},children:[(0,d.jsx)(h.A,{children:(0,d.jsx)(s.A,{fontSize:"small"})}),(0,d.jsx)(i.A,{primary:"New folder…"})]}),c&&(0,d.jsxs)(g.A,{onClick:()=>B(void 0),children:[(0,d.jsx)(h.A,{children:(0,d.jsx)(t.A,{fontSize:"small"})}),(0,d.jsx)(i.A,{primary:"Remove from folder"})]})]}),(0,d.jsxs)(k.A,{open:x,onClose:()=>y(!1),maxWidth:"xs",fullWidth:!0,children:[(0,d.jsx)(l.A,{children:"New folder"}),(0,d.jsx)(m.A,{children:(0,d.jsx)(n.A,{autoFocus:!0,margin:"dense",label:"Folder name",fullWidth:!0,value:z,onChange:a=>A(a.target.value),onKeyDown:a=>{"Enter"===a.key&&(a.preventDefault(),C())}})}),(0,d.jsxs)(o.A,{children:[(0,d.jsx)(p.A,{onClick:()=>y(!1),children:"Cancel"}),(0,d.jsx)(p.A,{variant:"contained",disabled:!z.trim(),onClick:C,children:"Create"})]})]})]})}},83434:(a,b,c)=>{c.d(b,{N:()=>e,X:()=>d}),(0,c(19815).h)("frontend/services/model");let d=()=>{throw Error("ModelService can only be used in browser environment")},e=new Proxy({},{get(a,b){let c=d(),e=c[b];return"function"==typeof e?e.bind(c):e}})}};
@@ -1,6 +1,6 @@
1
- "use strict";exports.id=4771,exports.ids=[4771,6793],exports.modules={4771:(a,b,c)=>{c.d(b,{z:()=>F});var d=c(32524),e=c(92372),f=c(46480),g=c(50846),h=c(58739),i=c(52189),j=c(33873),k=c.n(j),l=c(29021),m=c(9794);let n=(0,g.h)("backend/services/scheduler/triggers/fileWatch");var o=c(73736),p=c(10960),q=c(51875);let r=(0,g.h)("backend/services/scheduler/triggers/mcpPoll"),s=(0,g.h)("backend/services/scheduler/triggers/urlWatch");var t=c(56793);let u=(0,g.h)("backend/services/scheduler/triggers/flowEvent");function v(a){return a&&a.length>8192?`${a.slice(0,8192)}… (truncated)`:a}var w=c(59911);let x=(0,g.h)("backend/services/scheduler/state"),y=a=>`planned-execution-state/${a}`;async function z(a){try{return await (0,e.YM)(y(a),{})}catch(b){return x.error(`Failed to load trigger state for ${a}:`,b),{}}}async function A(a,b){await (0,e.VH)(y(a),b)}async function B(a){await (0,e.N2)(y(a))}let C=(0,g.h)("backend/services/scheduler/index"),D={version:1,paused:!1,executions:[]};class E{static{this.MAX_QUEUE_DEPTH=50}async start(){this.started||(this.started=!0,C.info("Starting scheduler"),await this.reconcile())}reconcile(){let a=this.reconcileChain.catch(()=>{}).then(()=>this.doReconcile());return this.reconcileChain=a,a}async doReconcile(){for(let[a,b]of this.armed)try{b.dispose()}catch(b){C.warn(`Failed to dispose trigger for ${a}:`,b)}this.armed.clear();let a=await this.loadFile();if(this.pausedCache=a.paused,a.paused)return void C.info("Scheduler is paused — nothing armed");for(let b of a.executions)if(b.enabled)try{await this.armExecution(b)}catch(c){let a=c instanceof Error?c.message:String(c);this.lastTriggerErrors.set(b.id,a),C.error(`Failed to arm "${b.name}" (${b.id}):`,c)}C.info(`Scheduler armed ${this.armed.size} execution(s)`)}async armExecution(a){let b=a.trigger;switch(b.type){case"schedule":{let c=await z(a.id);c.lastScheduledFireAt?b.catchUp&&(0,i.hk)(b,c.lastScheduledFireAt)&&(await A(a.id,{...c,lastScheduledFireAt:new Date().toISOString()}),C.info(`Catch-up run for "${a.name}" (missed while closed)`),this.fire(a,{kind:"schedule-catchup",summary:"Schedule (missed while FLUJO was closed — ran once at startup)"})):await A(a.id,{...c,lastScheduledFireAt:new Date().toISOString()}),this.armed.set(a.id,(0,i.wv)(b,()=>{(async()=>{let b=await z(a.id);await A(a.id,{...b,lastScheduledFireAt:new Date().toISOString()}),await this.fire(a,{kind:"schedule",summary:"Schedule"})})().catch(b=>C.error(`Scheduled fire failed for ${a.id}:`,b))}));break}case"webhook":break;case"file-watch":this.armed.set(a.id,function(a,b,c){let d,e=a.debounceMs??2e3,f=new Set(a.events),g=a.glob?.trim()?function(a){let b=a.replace(/\\/g,"/"),c="";for(let a=0;a<b.length;a++){let d=b[a];"*"===d?"*"===b[a+1]?"/"===b[++a+1]?(a++,c+="(?:.*/)?"):c+=".*":c+="[^/]*":"?"===d?c+="[^/]":c+=d.replace(/[.+^${}()|[\]\\]/g,"\\$&")}return RegExp(`^${c}$`)}(a.glob.trim()):null;try{d=l.realpathSync.native(a.path)}catch(d){let b=d instanceof Error?d.message:String(d);return n.warn(`Cannot watch "${a.path}": ${b}`),c(`Cannot watch "${a.path}": ${b}`),{dispose:()=>void 0}}let h=[],i=!1,j=null,o=!1,p=()=>{if(j=null,o||0===h.length)return;let a=h;i&&n.info("File-watch batch overflowed; reporting first 50 events"),h=[],i=!1,b({events:a})},q=(0,m.wB)(d,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:100}});return q.on("all",(a,b)=>{if(!o&&("add"===a||"change"===a||"unlink"===a)&&f.has(a)){if(g){let a=k().relative(d,b).replace(/\\/g,"/")||k().basename(b);if(!g.test(a))return}h.length>=50?i=!0:h.push({event:a,path:b}),j&&clearTimeout(j),j=setTimeout(p,e),j.unref?.()}}),q.on("error",a=>{let b=a instanceof Error?a.message:String(a);n.warn(`Watcher error for ${d}: ${b}`),c(b)}),{dispose:()=>{o=!0,j&&(clearTimeout(j),j=null),h=[],q.close().catch(a=>n.warn(`Failed to close watcher for ${d}:`,a))}}}(b,({events:c})=>{this.lastTriggerErrors.delete(a.id),this.fire(a,{kind:"file",summary:1===c.length?`File ${"unlink"===c[0].event?"deleted":"add"===c[0].event?"added":"changed"}`:`${c.length} file changes`,context:{watchedPath:b.path,events:c}})},b=>this.lastTriggerErrors.set(a.id,b)));break;case"mcp-poll":Promise.all([c.e(6996),c.e(1223),c.e(5734),c.e(264)]).then(c.bind(c,61397)).then(async({mcpService:c})=>{await c.isServerDisabled(b.serverName)&&this.lastTriggerErrors.set(a.id,`MCP server '${b.serverName}' is disabled — enable it on the MCP page or change the trigger`)}).catch(b=>C.warn(`Arm-time disabled-server check failed for ${a.id}:`,b)),this.armed.set(a.id,function(a,b){let c=a.cron||(0,p.X)(a.intervalMs),d=!1,e=!1,f=0,g=0,h=async()=>{if(!e&&!d){if(g>0)return void g--;d=!0;try{let c,d=await b.callTool(a.serverName,a.toolName,a.args);if(!d.success)throw Error(d.error||"Tool call failed");f=0;let g=await b.loadState(),h=a.evaluate;if(c="on-change"===h.mode?(0,q.Jl)(d.data,g):"new-items"===h.mode?(0,q.A1)(d.data,h.itemsPath,h.idField,g):b.evaluateAiGate?await b.evaluateAiGate(d.data,h,g):{fire:!1,newState:{},error:'The "AI decides" condition is not available yet'},e)return;if(Object.keys(c.newState).length>0&&await b.saveState(c.newState),c.error)b.onError(c.error);else if(c.fire){let{status:d}=await b.onFire({summary:`Watched tool: ${c.summary??"condition met"}`,context:{server:a.serverName,tool:a.toolName,..."object"==typeof c.context&&null!==c.context?c.context:{result:c.context}}});if(e)return;if(c.pendingState){let{giveUpError:a}=await (0,q.vV)({status:d,pendingState:c.pendingState,priorFailures:g.pendingFailures??0,saveState:b.saveState});a&&b.onError(a)}}else b.onSuccess?.()}catch(d){f++,g=Math.min(2**(f-1),8);let c=d instanceof Error?d.message:String(d);r.warn(`Poll failed for ${a.serverName}/${a.toolName} (backing off ${g} check(s)): ${c}`),b.onError(c)}finally{d=!1}}},i=new o.l4(c,{timezone:a.timezone,unref:!0},()=>void h());return h(),{dispose:()=>{e=!0,i.stop()},nextRun:()=>i.nextRun()?.toISOString()??null}}(b,{callTool:async(a,b,d)=>{let{mcpService:e}=await Promise.all([c.e(6996),c.e(1223),c.e(5734),c.e(264)]).then(c.bind(c,61397)),f=await e.callTool(a,b,d);return{success:!0===f.success,data:f.data,error:"string"==typeof f.error?f.error:void 0}},loadState:()=>z(a.id),saveState:async b=>{let c=await z(a.id);await A(a.id,{...c,...b})},onFire:async({summary:b,context:c})=>(this.lastTriggerErrors.delete(a.id),{status:(await this.fire(a,{kind:"mcp-poll",summary:b,context:c})).status}),onError:b=>this.lastTriggerErrors.set(a.id,b),onSuccess:()=>this.lastTriggerErrors.delete(a.id),evaluateAiGate:async(a,b,d)=>{let{evaluateAiGate:e}=await c.e(7506).then(c.bind(c,47506));return e(a,b,d)}}));break;case"url-watch":this.armed.set(a.id,function(a,b){let c=b.fetchImpl??fetch,d=!1,e=!1,f=async()=>{if(!e&&!d){d=!0;try{let d=await c(a.url,{redirect:"follow",headers:{"User-Agent":"FLUJO-url-watch"},signal:AbortSignal.timeout(3e4)});if(!d.ok)return void b.onError(`The URL answered with HTTP ${d.status}`);let f=await d.text(),g=(0,q.qq)(f),h=await b.loadState();if(e)return;if(!h.lastHash){await b.saveState({lastHash:g}),b.onSuccess?.();return}if(h.lastHash===g)return void b.onSuccess?.();let{status:i}=await b.onFire({summary:"Online content changed",context:{url:a.url,status:d.status,contentType:d.headers.get("content-type")??void 0,content:f.length>8192?`${f.slice(0,8192)}… (truncated, ${f.length} chars total)`:f}});if(e)return;let{giveUpError:j}=await (0,q.vV)({status:i,pendingState:{lastHash:g},priorFailures:h.pendingFailures??0,saveState:b.saveState});j&&b.onError(j)}catch(d){let c=d instanceof Error&&"TimeoutError"===d.name?"The URL did not answer within 30s":d instanceof Error?d.message:String(d);s.warn(`URL check failed for ${a.url}: ${c}`),b.onError(c)}finally{d=!1}}},g=new o.l4(a.cron,{timezone:a.timezone,unref:!0},()=>void f());return f(),{dispose:()=>{e=!0,g.stop()},nextRun:()=>g.nextRun()?.toISOString()??null}}(b,{loadState:()=>z(a.id),saveState:async b=>{let c=await z(a.id);await A(a.id,{...c,...b})},onFire:async({summary:b,context:c})=>(this.lastTriggerErrors.delete(a.id),{status:(await this.fire(a,{kind:"url-watch",summary:b,context:c})).status}),onError:b=>this.lastTriggerErrors.set(a.id,b),onSuccess:()=>this.lastTriggerErrors.delete(a.id)}));break;case"flow-event":this.armed.set(a.id,function(a,b){let c=a.maxChainDepth??5,d="string"==typeof a.source.topic?a.source.topic.trim():"",e=d.length>0,f=0,g=null,h=!1;if(a.outputMatch?.regex)try{g=new RegExp(a.outputMatch.regex)}catch(c){h=!0;let a=`Invalid output-match regex: ${c instanceof Error?c.message:String(c)}`;u.warn(a),b.onError(a)}let i=b=>{if(!a.outputMatch)return!0;if(h)return!1;let c=(b??"").slice(0,16384);return(!a.outputMatch.contains||!!c.includes(a.outputMatch.contains))&&(!g||!!g.test(c))},j=(d,e,g)=>{if(d>=c){let a=`Flow-event chain reached the depth limit (${c}); not firing again`;u.info(a),b.onSkip(a);return}let h=Date.now();if(a.minIntervalMs&&h-f<a.minIntervalMs)return void u.debug(`Flow-event cooldown active (${a.minIntervalMs}ms); ignoring event`);f=h,b.onFire({summary:e,context:g,chainDepth:d+1})},k=(0,t.eh)().subscribe(c=>{try{if(e){if(!(0,t.ru)(c)||c.topic!==d||!i(c.payload))return;j(c.chainDepth,`Signal "${c.topic}"`,{topic:c.topic,payload:v(c.payload),emitterFlowId:c.emitterFlowId,flowName:c.flowName,runId:c.runId,conversationId:c.conversationId,firedBy:c.firedBy});return}if((0,t.ru)(c)||!(b=>{let c=a.source;return c.executionId?b.executionId===c.executionId:c.flowId?b.flowId===c.flowId:!!c.flowName&&b.flowName===c.flowName})(c)||!c.status||!(a.on??[]).includes(c.status)||!i(c.outputText))return;let b=c.flowName??c.flowId;j(c.chainDepth,`Flow "${b}" ${c.status}`,{flowId:c.flowId,flowName:c.flowName,executionId:c.executionId,runId:c.runId,conversationId:c.conversationId,status:c.status,firedBy:c.firedBy,outputText:v(c.outputText),error:c.error})}catch(c){let a=c instanceof Error?c.message:String(c);u.warn(`Flow-event handling failed: ${a}`),b.onError(a)}});return{dispose:()=>k(),nextRun:()=>null}}(b,{onFire:({summary:b,context:c,chainDepth:d})=>{this.lastTriggerErrors.delete(a.id),this.fire(a,{kind:"flow-event",summary:b,context:c,chainDepth:d}).catch(b=>C.error(`Flow-event fire failed for ${a.id}:`,b))},onSkip:b=>{let c=new Date().toISOString();(0,w.Hy)(a.id,{runId:(0,d.A)(),conversationId:"",firedAt:c,finishedAt:c,status:"skipped",triggerSummary:"Flow event",error:b}).catch(b=>C.warn(`Failed to record flow-event skip for ${a.id}:`,b))},onError:b=>this.lastTriggerErrors.set(a.id,b)}));break;default:C.warn(`Trigger type "${b.type}" is not implemented — "${a.name}" not armed`)}}async loadFile(){let a=await (0,e.YM)(f.Z.PLANNED_EXECUTIONS,D);return{version:1,paused:!0===a.paused,executions:Array.isArray(a.executions)?a.executions:[]}}async saveFile(a){await (0,e.VH)(f.Z.PLANNED_EXECUTIONS,a)}async isPaused(){return(await this.loadFile()).paused}async setPaused(a){let b=await this.loadFile();if(await this.saveFile({...b,paused:a}),await this.reconcile(),a)for(let a of[...this.queued.keys()])await this.cancelQueued(a,"scheduler paused",!0)}async list(){return Promise.all((await this.loadFile()).executions.map(async a=>({execution:a,status:this.getStatus(a),lastRun:await (0,w.tl)(a.id)})))}async get(a){return(await this.loadFile()).executions.find(b=>b.id===a)??null}async create(a){let b=this.validateInput(a);if(b)return{error:b};if(void 0!==a.id&&!/^[A-Za-z0-9._:-]{1,128}$/.test(a.id))return{error:"The id must be 1-128 characters of letters, digits, dot, underscore, colon or hyphen"};if("."===a.id||".."===a.id)return{error:'The id must be a safe identifier, not "." or ".."'};let c=new Date().toISOString(),e={...a,trigger:this.normalizeTrigger(a.trigger),id:a.id??(0,d.A)(),createdAt:c,updatedAt:c},f=await this.loadFile();return f.executions.some(a=>a.id===e.id)?{error:`A planned execution with id "${e.id}" already exists`,conflict:!0}:(await this.saveFile({...f,executions:[...f.executions,e]}),await this.reconcile(),{execution:e})}async update(a,b){let c=await this.loadFile(),d=c.executions.findIndex(b=>b.id===a);if(d<0)return{error:`No planned execution with id "${a}"`};let e={...c.executions[d],...b,...b.trigger?{trigger:this.normalizeTrigger(b.trigger)}:{},id:a,createdAt:c.executions[d].createdAt,updatedAt:new Date().toISOString()},f=this.validateInput(e);if(f)return{error:f};let g=[...c.executions];return g[d]=e,await this.saveFile({...c,executions:g}),await this.reconcile(),await this.cancelQueued(a,"execution updated",!0),{execution:e}}async delete(a){let b=await this.loadFile();return b.executions.some(b=>b.id===a)?(await this.saveFile({...b,executions:b.executions.filter(b=>b.id!==a)}),await this.reconcile(),this.lastTriggerErrors.delete(a),await this.cancelQueued(a,"execution deleted",!1),await (0,w.xC)(a),await B(a),{success:!0}):{success:!1,error:`No planned execution with id "${a}"`}}normalizeTrigger(a){return a?.type!=="webhook"||a.token?a?.type!=="mcp-poll"||a.cron?a:{...a,cron:(0,p.X)(a.intervalMs)}:{...a,token:(0,d.A)()}}validateInput(a){if(!a.name?.trim())return"A name is required";if(!a.flowId)return"A flow is required";if("string"!=typeof a.prompt)return"A prompt is required (may be empty)";if(void 0!==a.overlapStrategy&&!["skip","queue","parallel","error"].includes(a.overlapStrategy))return"Overlap strategy must be one of: skip, queue, parallel, error";let b=a.trigger;if(!b||"object"!=typeof b)return"A trigger is required";switch(b.type){case"schedule":{let a=(0,i.O9)(b.cron,b.timezone);return a.valid?null:`Invalid schedule: ${a.error}`}case"webhook":return null;case"file-watch":if(!b.path?.trim())return"A folder or file path to watch is required";if(!Array.isArray(b.events)||0===b.events.length)return"At least one file event is required";return null;case"mcp-poll":if(!b.serverName||!b.toolName)return"A server and tool are required";if(b.cron){let a=(0,i.O9)(b.cron,b.timezone);if(!a.valid)return`Invalid schedule: ${a.error}`}if(b.evaluate?.mode==="new-items"&&!b.evaluate.idField?.trim())return"An id field is required to detect new items";if((b.evaluate?.mode==="llm-gate"||b.evaluate?.mode==="flow-gate")&&!b.evaluate.condition?.trim())return"A condition is required for the AI to check";if(b.evaluate?.mode==="llm-gate"&&!b.evaluate.modelId)return"A model is required to check the condition";if(b.evaluate?.mode==="flow-gate"&&!b.evaluate.flowId)return"A flow is required to check the condition";return null;case"url-watch":{try{let a=new URL(b.url??"");if("http:"!==a.protocol&&"https:"!==a.protocol)return"The URL must start with http:// or https://"}catch{return"A valid URL is required"}let a=(0,i.O9)(b.cron,b.timezone);return a.valid?null:`Invalid schedule: ${a.error}`}case"flow-event":{let a=b.source;if(1!==[a?.executionId,a?.flowId,a?.flowName,a?.topic].filter(a=>"string"==typeof a&&a.trim().length>0).length)return"Choose exactly one source: a flow, a planned execution, or a signal topic";if(!("string"==typeof a?.topic&&a.topic.trim().length>0)){if(!Array.isArray(b.on)||0===b.on.length)return"Select at least one outcome to react to (completed or error)";if(b.on.some(a=>"completed"!==a&&"error"!==a))return'Outcomes must be "completed" or "error"'}if(b.outputMatch?.regex)try{new RegExp(b.outputMatch.regex)}catch(a){return`Invalid output-match regex: ${a instanceof Error?a.message:String(a)}`}if(void 0!==b.maxChainDepth&&(!Number.isInteger(b.maxChainDepth)||b.maxChainDepth<1))return"Max chain depth must be a positive whole number";if(void 0!==b.minIntervalMs&&(!Number.isFinite(b.minIntervalMs)||b.minIntervalMs<0))return"Minimum interval must be zero or more milliseconds";return null}default:return"Unknown trigger type"}}isRunning(a){let b=this.running.get(a);return void 0!==b&&b.size>0}addRunning(a,b,c){let d=this.running.get(a);d||(d=new Map,this.running.set(a,d)),d.set(b,c)}removeRunning(a,b){let c=this.running.get(a);c&&(c.delete(b),0===c.size&&this.running.delete(a))}earliestRunningSince(a){let b,c=this.running.get(a);if(c&&0!==c.size){for(let a of c.values())(void 0===b||a<b)&&(b=a);return b}}skippedRecord(a,b){let c=new Date().toISOString();return{runId:a.runId,conversationId:"",firedAt:c,finishedAt:c,status:"skipped",triggerSummary:a.payload.summary,error:b}}async cancelQueued(a,b,c){let d=this.queued.get(a);if(d&&0!==d.length){for(let e of(this.queued.delete(a),d)){let d=this.skippedRecord(e,b);if(c)try{await (0,w.Hy)(a,d)}catch(b){C.warn(`Failed to record cancelled queued fire for ${a}:`,b)}e.resolve(d)}C.info(`Cancelled ${d.length} queued fire(s) for ${a}: ${b}`)}}drainQueue(a){let b=this.queued.get(a);if(!b||0===b.length||this.isRunning(a))return;let c=b.shift();0===b.length&&this.queued.delete(a),c&&(async()=>{let b=await this.get(a);if(!b||!b.enabled||this.pausedCache){let a=b?b.enabled?"scheduler paused":"execution disabled":"execution deleted";c.resolve(this.skippedRecord(c,a));return}let d=await this.fire(b,c.payload,c.runId);c.resolve(d)})().catch(b=>C.error(`Queued fire failed for ${a}:`,b))}getStatus(a){let b=this.armed.get(a.id),c=void 0!==b||a.enabled&&"webhook"===a.trigger.type&&this.started&&!this.pausedCache,d=this.earliestRunningSince(a.id),e=c?void 0:a.enabled?this.pausedCache?"paused":void 0:"disabled";return{armed:c,notArmedReason:e,nextRun:b?.nextRun?b.nextRun():void 0,lastTriggerError:this.lastTriggerErrors.get(a.id),running:void 0!==d,runningSince:d}}async runNow(a){let b=await this.get(a);return b?{record:await this.fire(b,{kind:"manual",summary:"Manual run"},(0,d.A)(),!0)}:{error:`No planned execution with id "${a}"`}}async fire(a,b,e=(0,d.A)(),f=!1){let g,i=new Date().toISOString();if(await (0,h.isEncryptionLocked)()){let c={runId:e,conversationId:"",firedAt:i,finishedAt:i,status:"skipped",triggerSummary:b.summary,error:"encryption locked"};return await (0,w.Hy)(a.id,c),C.info(`Skipped fire for "${a.name}" — encryption locked`),c}let j=a.overlapStrategy??"skip";if(!f&&this.isRunning(a.id)){if("skip"===j){let c={runId:e,conversationId:"",firedAt:i,finishedAt:i,status:"skipped",triggerSummary:b.summary,error:"Previous run still in progress"};return await (0,w.Hy)(a.id,c),C.info(`Skipped overlapping fire for "${a.name}"`),c}if("error"===j){let c={runId:e,conversationId:"",firedAt:i,finishedAt:i,status:"error",triggerSummary:b.summary,error:"Overlapping run rejected (overlapStrategy=error)"};return await (0,w.Hy)(a.id,c),C.info(`Rejected overlapping fire for "${a.name}" (overlapStrategy=error)`),c}if("queue"===j){let c=this.queued.get(a.id)?.length??0;if(c>=E.MAX_QUEUE_DEPTH){let c={runId:e,conversationId:"",firedAt:i,finishedAt:i,status:"skipped",triggerSummary:b.summary,error:`Overlap queue full (cap ${E.MAX_QUEUE_DEPTH}) — fire dropped`};return await (0,w.Hy)(a.id,c),C.warn(`Overlap queue full for "${a.name}" — dropped fire`),c}return C.info(`Queued overlapping fire for "${a.name}" (depth ${c+1})`),new Promise(c=>{let d=this.queued.get(a.id)??[];d.push({execution:a,payload:b,runId:e,resolve:c}),this.queued.set(a.id,d)})}}this.addRunning(a.id,e,i);let k=(0,d.A)();try{C.info(`Firing "${a.name}" (${b.kind})`);let d=await (0,w.Sw)(a.id),f=d.length>0?d[d.length-1]:null,h=[...d].reverse().find(a=>a.outputText),j={executionName:a.name,trigger:b.kind,now:i,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,lastRun:f?{at:f.firedAt,status:f.status}:null,lastOutput:h?{at:h.firedAt,text:h.outputText}:null,nextPlannedRun:this.getStatus(a).nextRun??null},l=a.approvalPolicy??"auto",m=a.saveConversations||"pause"===l?"conversation":"ephemeral",{runFlow:n}=await Promise.all([c.e(6996),c.e(1223),c.e(5734),c.e(1373),c.e(264),c.e(7821),c.e(724),c.e(3823),c.e(4671),c.e(7214)]).then(c.bind(c,57214)),o=await n({flowId:a.flowId,prompt:this.composePrompt(a.prompt,b,j),mode:m,conversationId:k,source:"schedule",plannedExecutionId:a.id,chainDepth:b.chainDepth??0,requireApproval:"auto"!==l,onApprovalRequired:l,debug:!1,userTurn:!0});g=await this.recordFromResult(a,o,{runId:e,conversationId:k,firedAt:i,payload:b})}catch(c){g={runId:e,conversationId:k,firedAt:i,finishedAt:new Date().toISOString(),status:"error",triggerSummary:b.summary,error:c instanceof Error?c.message:String(c)},C.error(`Run crashed for "${a.name}":`,c)}finally{this.removeRunning(a.id,e),this.drainQueue(a.id)}return await this.get(a.id)===null?C.info(`Dropping run record for deleted execution ${a.id}`):(await (0,w.Hy)(a.id,g),("completed"===g.status||"error"===g.status)&&await this.publishFlowRunEvent(a,g,b)),g}async recordFromResult(a,b,c){let{runId:d,conversationId:e,firedAt:f,payload:g}=c,h=new Date().toISOString(),i="awaiting_tool_approval"===b.status,j="error"===b.status&&b.error?.details?.type==="approval_required";if(i||j){let c=(b.pendingToolCalls??[]).map(a=>({id:a.id,name:"function"===a.type?a.function.name:String(a.type)})),k={runId:d,conversationId:e,firedAt:f,finishedAt:h,status:"needs_approval",triggerSummary:g.summary,outputText:this.truncateOutput(b.outputText),usage:b.usage,error:j?b.error?.message??"Tool approval required":"Awaiting tool approval",pendingApproval:{tool:c[0]?.name??b.error?.details?.name,toolCallId:c[0]?.id,pendingToolCalls:c.length>0?c:void 0}};return i&&await this.registerPendingApproval(a,k,c),k}return{runId:d,conversationId:e,firedAt:f,finishedAt:h,status:"completed"===b.status?"completed":"error",triggerSummary:g.summary,outputText:this.truncateOutput(b.outputText),usage:b.usage,error:"completed"===b.status?void 0:b.error?.message??`Run ended with status "${b.status}"`}}async registerPendingApproval(a,b,d){try{let e;try{let{flowService:b}=await c.e(7821).then(c.bind(c,87821));e=(await b.getFlow(a.flowId))?.name??void 0}catch(b){C.debug(`Could not resolve flow name for pending approval (${a.flowId}):`,b)}let{putPendingApproval:f}=await c.e(9285).then(c.bind(c,39285));await f({approvalId:b.conversationId,conversationId:b.conversationId,plannedExecutionId:a.id,flowId:a.flowId,flowName:e,runId:b.runId,triggerSummary:b.triggerSummary,pendingToolCalls:d,createdAt:b.firedAt})}catch(b){C.warn(`Failed to register pending approval for "${a.name}":`,b)}}async publishFlowRunEvent(a,b,d){try{let e;try{let{flowService:b}=await c.e(7821).then(c.bind(c,87821));e=(await b.getFlow(a.flowId))?.name??void 0}catch(b){C.debug(`Could not resolve flow name for run event (${a.flowId}):`,b)}let f="schedule-catchup"===d.kind?"schedule":d.kind;(0,t.eh)().publish({flowId:a.flowId,flowName:e,executionId:a.id,runId:b.runId,conversationId:b.conversationId,status:"completed"===b.status?"completed":"error",outputText:b.outputText,error:b.error,firedBy:f,chainDepth:d.chainDepth??0,timestamp:b.finishedAt??new Date().toISOString()})}catch(b){C.warn(`Failed to publish flow-run event for "${a.name}":`,b)}}composePrompt(a,b,c){let d=JSON.stringify(void 0===b.context?c:{...c,data:b.context},null,2);return`${a}
1
+ "use strict";exports.id=4771,exports.ids=[4771,6793],exports.modules={4771:(a,b,c)=>{c.d(b,{z:()=>F});var d=c(32524),e=c(92372),f=c(46480),g=c(50846),h=c(58739),i=c(52189),j=c(33873),k=c.n(j),l=c(29021),m=c(9794);let n=(0,g.h)("backend/services/scheduler/triggers/fileWatch");var o=c(73736),p=c(10960),q=c(51875);let r=(0,g.h)("backend/services/scheduler/triggers/mcpPoll"),s=(0,g.h)("backend/services/scheduler/triggers/urlWatch");var t=c(56793);let u=(0,g.h)("backend/services/scheduler/triggers/flowEvent");function v(a){return a&&a.length>8192?`${a.slice(0,8192)}… (truncated)`:a}var w=c(59911);let x=(0,g.h)("backend/services/scheduler/state"),y=a=>`planned-execution-state/${a}`;async function z(a){try{return await (0,e.YM)(y(a),{})}catch(b){return x.error(`Failed to load trigger state for ${a}:`,b),{}}}async function A(a,b){await (0,e.VH)(y(a),b)}async function B(a){await (0,e.N2)(y(a))}let C=(0,g.h)("backend/services/scheduler/index"),D={version:1,paused:!1,executions:[]};class E{static{this.MAX_QUEUE_DEPTH=50}async start(){this.started||(this.started=!0,C.info("Starting scheduler"),await this.reconcile())}reconcile(){let a=this.reconcileChain.catch(()=>{}).then(()=>this.doReconcile());return this.reconcileChain=a,a}async doReconcile(){for(let[a,b]of this.armed)try{b.dispose()}catch(b){C.warn(`Failed to dispose trigger for ${a}:`,b)}this.armed.clear();let a=await this.loadFile();if(this.pausedCache=a.paused,a.paused)return void C.info("Scheduler is paused — nothing armed");for(let b of a.executions)if(b.enabled)try{await this.armExecution(b)}catch(c){let a=c instanceof Error?c.message:String(c);this.lastTriggerErrors.set(b.id,a),C.error(`Failed to arm "${b.name}" (${b.id}):`,c)}C.info(`Scheduler armed ${this.armed.size} execution(s)`)}async armExecution(a){let b=a.trigger;switch(b.type){case"schedule":{let c=await z(a.id);c.lastScheduledFireAt?b.catchUp&&(0,i.hk)(b,c.lastScheduledFireAt)&&(await A(a.id,{...c,lastScheduledFireAt:new Date().toISOString()}),C.info(`Catch-up run for "${a.name}" (missed while closed)`),this.fire(a,{kind:"schedule-catchup",summary:"Schedule (missed while FLUJO was closed — ran once at startup)"})):await A(a.id,{...c,lastScheduledFireAt:new Date().toISOString()}),this.armed.set(a.id,(0,i.wv)(b,()=>{(async()=>{let b=await z(a.id);await A(a.id,{...b,lastScheduledFireAt:new Date().toISOString()}),await this.fire(a,{kind:"schedule",summary:"Schedule"})})().catch(b=>C.error(`Scheduled fire failed for ${a.id}:`,b))}));break}case"webhook":break;case"file-watch":this.armed.set(a.id,function(a,b,c){let d,e=a.debounceMs??2e3,f=new Set(a.events),g=a.glob?.trim()?function(a){let b=a.replace(/\\/g,"/"),c="";for(let a=0;a<b.length;a++){let d=b[a];"*"===d?"*"===b[a+1]?"/"===b[++a+1]?(a++,c+="(?:.*/)?"):c+=".*":c+="[^/]*":"?"===d?c+="[^/]":c+=d.replace(/[.+^${}()|[\]\\]/g,"\\$&")}return RegExp(`^${c}$`)}(a.glob.trim()):null;try{d=l.realpathSync.native(a.path)}catch(d){let b=d instanceof Error?d.message:String(d);return n.warn(`Cannot watch "${a.path}": ${b}`),c(`Cannot watch "${a.path}": ${b}`),{dispose:()=>void 0}}let h=[],i=!1,j=null,o=!1,p=()=>{if(j=null,o||0===h.length)return;let a=h;i&&n.info("File-watch batch overflowed; reporting first 50 events"),h=[],i=!1,b({events:a})},q=(0,m.wB)(d,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:100}});return q.on("all",(a,b)=>{if(!o&&("add"===a||"change"===a||"unlink"===a)&&f.has(a)){if(g){let a=k().relative(d,b).replace(/\\/g,"/")||k().basename(b);if(!g.test(a))return}h.length>=50?i=!0:h.push({event:a,path:b}),j&&clearTimeout(j),j=setTimeout(p,e),j.unref?.()}}),q.on("error",a=>{let b=a instanceof Error?a.message:String(a);n.warn(`Watcher error for ${d}: ${b}`),c(b)}),{dispose:()=>{o=!0,j&&(clearTimeout(j),j=null),h=[],q.close().catch(a=>n.warn(`Failed to close watcher for ${d}:`,a))}}}(b,({events:c})=>{this.lastTriggerErrors.delete(a.id),this.fire(a,{kind:"file",summary:1===c.length?`File ${"unlink"===c[0].event?"deleted":"add"===c[0].event?"added":"changed"}`:`${c.length} file changes`,context:{watchedPath:b.path,events:c}})},b=>this.lastTriggerErrors.set(a.id,b)));break;case"mcp-poll":Promise.all([c.e(6996),c.e(1223),c.e(5734),c.e(264)]).then(c.bind(c,61397)).then(async({mcpService:c})=>{await c.isServerDisabled(b.serverName)&&this.lastTriggerErrors.set(a.id,`MCP server '${b.serverName}' is disabled — enable it on the MCP page or change the trigger`)}).catch(b=>C.warn(`Arm-time disabled-server check failed for ${a.id}:`,b)),this.armed.set(a.id,function(a,b){let c=a.cron||(0,p.X)(a.intervalMs),d=!1,e=!1,f=0,g=0,h=async()=>{if(!e&&!d){if(g>0)return void g--;d=!0;try{let c,d=await b.callTool(a.serverName,a.toolName,a.args);if(!d.success)throw Error(d.error||"Tool call failed");f=0;let g=await b.loadState(),h=a.evaluate;if(c="on-change"===h.mode?(0,q.Jl)(d.data,g):"new-items"===h.mode?(0,q.A1)(d.data,h.itemsPath,h.idField,g):b.evaluateAiGate?await b.evaluateAiGate(d.data,h,g):{fire:!1,newState:{},error:'The "AI decides" condition is not available yet'},e)return;if(Object.keys(c.newState).length>0&&await b.saveState(c.newState),c.error)b.onError(c.error);else if(c.fire){let{status:d}=await b.onFire({summary:`Watched tool: ${c.summary??"condition met"}`,context:{server:a.serverName,tool:a.toolName,..."object"==typeof c.context&&null!==c.context?c.context:{result:c.context}}});if(e)return;if(c.pendingState){let{giveUpError:a}=await (0,q.vV)({status:d,pendingState:c.pendingState,priorFailures:g.pendingFailures??0,saveState:b.saveState});a&&b.onError(a)}}else b.onSuccess?.()}catch(d){f++,g=Math.min(2**(f-1),8);let c=d instanceof Error?d.message:String(d);r.warn(`Poll failed for ${a.serverName}/${a.toolName} (backing off ${g} check(s)): ${c}`),b.onError(c)}finally{d=!1}}},i=new o.l4(c,{timezone:a.timezone,unref:!0},()=>void h());return h(),{dispose:()=>{e=!0,i.stop()},nextRun:()=>i.nextRun()?.toISOString()??null}}(b,{callTool:async(a,b,d)=>{let{mcpService:e}=await Promise.all([c.e(6996),c.e(1223),c.e(5734),c.e(264)]).then(c.bind(c,61397)),f=await e.callTool(a,b,d);return{success:!0===f.success,data:f.data,error:"string"==typeof f.error?f.error:void 0}},loadState:()=>z(a.id),saveState:async b=>{let c=await z(a.id);await A(a.id,{...c,...b})},onFire:async({summary:b,context:c})=>(this.lastTriggerErrors.delete(a.id),{status:(await this.fire(a,{kind:"mcp-poll",summary:b,context:c})).status}),onError:b=>this.lastTriggerErrors.set(a.id,b),onSuccess:()=>this.lastTriggerErrors.delete(a.id),evaluateAiGate:async(a,b,d)=>{let{evaluateAiGate:e}=await c.e(7506).then(c.bind(c,47506));return e(a,b,d)}}));break;case"url-watch":this.armed.set(a.id,function(a,b){let c=b.fetchImpl??fetch,d=!1,e=!1,f=async()=>{if(!e&&!d){d=!0;try{let d=await c(a.url,{redirect:"follow",headers:{"User-Agent":"FLUJO-url-watch"},signal:AbortSignal.timeout(3e4)});if(!d.ok)return void b.onError(`The URL answered with HTTP ${d.status}`);let f=await d.text(),g=(0,q.qq)(f),h=await b.loadState();if(e)return;if(!h.lastHash){await b.saveState({lastHash:g}),b.onSuccess?.();return}if(h.lastHash===g)return void b.onSuccess?.();let{status:i}=await b.onFire({summary:"Online content changed",context:{url:a.url,status:d.status,contentType:d.headers.get("content-type")??void 0,content:f.length>8192?`${f.slice(0,8192)}… (truncated, ${f.length} chars total)`:f}});if(e)return;let{giveUpError:j}=await (0,q.vV)({status:i,pendingState:{lastHash:g},priorFailures:h.pendingFailures??0,saveState:b.saveState});j&&b.onError(j)}catch(d){let c=d instanceof Error&&"TimeoutError"===d.name?"The URL did not answer within 30s":d instanceof Error?d.message:String(d);s.warn(`URL check failed for ${a.url}: ${c}`),b.onError(c)}finally{d=!1}}},g=new o.l4(a.cron,{timezone:a.timezone,unref:!0},()=>void f());return f(),{dispose:()=>{e=!0,g.stop()},nextRun:()=>g.nextRun()?.toISOString()??null}}(b,{loadState:()=>z(a.id),saveState:async b=>{let c=await z(a.id);await A(a.id,{...c,...b})},onFire:async({summary:b,context:c})=>(this.lastTriggerErrors.delete(a.id),{status:(await this.fire(a,{kind:"url-watch",summary:b,context:c})).status}),onError:b=>this.lastTriggerErrors.set(a.id,b),onSuccess:()=>this.lastTriggerErrors.delete(a.id)}));break;case"flow-event":this.armed.set(a.id,function(a,b){let c=a.maxChainDepth??5,d="string"==typeof a.source.topic?a.source.topic.trim():"",e=d.length>0,f=0,g=null,h=!1;if(a.outputMatch?.regex)try{g=new RegExp(a.outputMatch.regex)}catch(c){h=!0;let a=`Invalid output-match regex: ${c instanceof Error?c.message:String(c)}`;u.warn(a),b.onError(a)}let i=b=>{if(!a.outputMatch)return!0;if(h)return!1;let c=(b??"").slice(0,16384);return(!a.outputMatch.contains||!!c.includes(a.outputMatch.contains))&&(!g||!!g.test(c))},j=(d,e,g)=>{if(d>=c){let a=`Flow-event chain reached the depth limit (${c}); not firing again`;u.info(a),b.onSkip(a);return}let h=Date.now();if(a.minIntervalMs&&h-f<a.minIntervalMs)return void u.debug(`Flow-event cooldown active (${a.minIntervalMs}ms); ignoring event`);f=h,b.onFire({summary:e,context:g,chainDepth:d+1})},k=(0,t.eh)().subscribe(c=>{try{if(e){if(!(0,t.ru)(c)||c.topic!==d||!i(c.payload))return;j(c.chainDepth,`Signal "${c.topic}"`,{topic:c.topic,payload:v(c.payload),emitterFlowId:c.emitterFlowId,flowName:c.flowName,runId:c.runId,conversationId:c.conversationId,firedBy:c.firedBy});return}if((0,t.ru)(c)||!(b=>{let c=a.source;return c.executionId?b.executionId===c.executionId:c.flowId?b.flowId===c.flowId:!!c.flowName&&b.flowName===c.flowName})(c)||!c.status||!(a.on??[]).includes(c.status)||!i(c.outputText))return;let b=c.flowName??c.flowId;j(c.chainDepth,`Flow "${b}" ${c.status}`,{flowId:c.flowId,flowName:c.flowName,executionId:c.executionId,runId:c.runId,conversationId:c.conversationId,status:c.status,firedBy:c.firedBy,outputText:v(c.outputText),error:c.error})}catch(c){let a=c instanceof Error?c.message:String(c);u.warn(`Flow-event handling failed: ${a}`),b.onError(a)}});return{dispose:()=>k(),nextRun:()=>null}}(b,{onFire:({summary:b,context:c,chainDepth:d})=>{this.lastTriggerErrors.delete(a.id),this.fire(a,{kind:"flow-event",summary:b,context:c,chainDepth:d}).catch(b=>C.error(`Flow-event fire failed for ${a.id}:`,b))},onSkip:b=>{let c=new Date().toISOString();(0,w.Hy)(a.id,{runId:(0,d.A)(),conversationId:"",firedAt:c,finishedAt:c,status:"skipped",triggerSummary:"Flow event",error:b}).catch(b=>C.warn(`Failed to record flow-event skip for ${a.id}:`,b))},onError:b=>this.lastTriggerErrors.set(a.id,b)}));break;default:C.warn(`Trigger type "${b.type}" is not implemented — "${a.name}" not armed`)}}async loadFile(){let a=await (0,e.YM)(f.Z.PLANNED_EXECUTIONS,D);return{version:1,paused:!0===a.paused,executions:Array.isArray(a.executions)?a.executions:[]}}async saveFile(a){await (0,e.VH)(f.Z.PLANNED_EXECUTIONS,a)}async isPaused(){return(await this.loadFile()).paused}async setPaused(a){let b=await this.loadFile();if(await this.saveFile({...b,paused:a}),await this.reconcile(),a)for(let a of[...this.queued.keys()])await this.cancelQueued(a,"scheduler paused",!0)}async list(){return Promise.all((await this.loadFile()).executions.map(async a=>({execution:a,status:this.getStatus(a),lastRun:await (0,w.tl)(a.id)})))}async get(a){return(await this.loadFile()).executions.find(b=>b.id===a)??null}async create(a){let b=this.validateInput(a);if(b)return{error:b};if(void 0!==a.id&&!/^[A-Za-z0-9._:-]{1,128}$/.test(a.id))return{error:"The id must be 1-128 characters of letters, digits, dot, underscore, colon or hyphen"};if("."===a.id||".."===a.id)return{error:'The id must be a safe identifier, not "." or ".."'};let c=new Date().toISOString(),e={...a,trigger:this.normalizeTrigger(a.trigger),id:a.id??(0,d.A)(),createdAt:c,updatedAt:c},f=await this.loadFile();return f.executions.some(a=>a.id===e.id)?{error:`A planned execution with id "${e.id}" already exists`,conflict:!0}:(await this.saveFile({...f,executions:[...f.executions,e]}),await this.reconcile(),{execution:e})}async update(a,b){let c=await this.loadFile(),d=c.executions.findIndex(b=>b.id===a);if(d<0)return{error:`No planned execution with id "${a}"`};let e={...c.executions[d],...b,...b.trigger?{trigger:this.normalizeTrigger(b.trigger)}:{},id:a,createdAt:c.executions[d].createdAt,updatedAt:new Date().toISOString()},f=this.validateInput(e);if(f)return{error:f};let g=[...c.executions];return g[d]=e,await this.saveFile({...c,executions:g}),await this.reconcile(),await this.cancelQueued(a,"execution updated",!0),{execution:e}}async delete(a){let b=await this.loadFile();return b.executions.some(b=>b.id===a)?(await this.saveFile({...b,executions:b.executions.filter(b=>b.id!==a)}),await this.reconcile(),this.lastTriggerErrors.delete(a),await this.cancelQueued(a,"execution deleted",!1),await (0,w.xC)(a),await B(a),{success:!0}):{success:!1,error:`No planned execution with id "${a}"`}}normalizeTrigger(a){return a?.type!=="webhook"||a.token?a?.type!=="mcp-poll"||a.cron?a:{...a,cron:(0,p.X)(a.intervalMs)}:{...a,token:(0,d.A)()}}validateInput(a){if(!a.name?.trim())return"A name is required";if(!a.flowId)return"A flow is required";if("string"!=typeof a.prompt)return"A prompt is required (may be empty)";if(void 0!==a.overlapStrategy&&!["skip","queue","parallel","error"].includes(a.overlapStrategy))return"Overlap strategy must be one of: skip, queue, parallel, error";let b=a.trigger;if(!b||"object"!=typeof b)return"A trigger is required";switch(b.type){case"schedule":{let a=(0,i.O9)(b.cron,b.timezone);return a.valid?null:`Invalid schedule: ${a.error}`}case"webhook":return null;case"file-watch":if(!b.path?.trim())return"A folder or file path to watch is required";if(!Array.isArray(b.events)||0===b.events.length)return"At least one file event is required";return null;case"mcp-poll":if(!b.serverName||!b.toolName)return"A server and tool are required";if(b.cron){let a=(0,i.O9)(b.cron,b.timezone);if(!a.valid)return`Invalid schedule: ${a.error}`}if(b.evaluate?.mode==="new-items"&&!b.evaluate.idField?.trim())return"An id field is required to detect new items";if((b.evaluate?.mode==="llm-gate"||b.evaluate?.mode==="flow-gate")&&!b.evaluate.condition?.trim())return"A condition is required for the AI to check";if(b.evaluate?.mode==="llm-gate"&&!b.evaluate.modelId)return"A model is required to check the condition";if(b.evaluate?.mode==="flow-gate"&&!b.evaluate.flowId)return"A flow is required to check the condition";return null;case"url-watch":{try{let a=new URL(b.url??"");if("http:"!==a.protocol&&"https:"!==a.protocol)return"The URL must start with http:// or https://"}catch{return"A valid URL is required"}let a=(0,i.O9)(b.cron,b.timezone);return a.valid?null:`Invalid schedule: ${a.error}`}case"flow-event":{let a=b.source;if(1!==[a?.executionId,a?.flowId,a?.flowName,a?.topic].filter(a=>"string"==typeof a&&a.trim().length>0).length)return"Choose exactly one source: a flow, a planned execution, or a signal topic";if(!("string"==typeof a?.topic&&a.topic.trim().length>0)){if(!Array.isArray(b.on)||0===b.on.length)return"Select at least one outcome to react to (completed or error)";if(b.on.some(a=>"completed"!==a&&"error"!==a))return'Outcomes must be "completed" or "error"'}if(b.outputMatch?.regex)try{new RegExp(b.outputMatch.regex)}catch(a){return`Invalid output-match regex: ${a instanceof Error?a.message:String(a)}`}if(void 0!==b.maxChainDepth&&(!Number.isInteger(b.maxChainDepth)||b.maxChainDepth<1))return"Max chain depth must be a positive whole number";if(void 0!==b.minIntervalMs&&(!Number.isFinite(b.minIntervalMs)||b.minIntervalMs<0))return"Minimum interval must be zero or more milliseconds";return null}default:return"Unknown trigger type"}}isRunning(a){let b=this.running.get(a);return void 0!==b&&b.size>0}addRunning(a,b,c){let d=this.running.get(a);d||(d=new Map,this.running.set(a,d)),d.set(b,c)}removeRunning(a,b){let c=this.running.get(a);c&&(c.delete(b),0===c.size&&this.running.delete(a))}earliestRunningSince(a){let b,c=this.running.get(a);if(c&&0!==c.size){for(let a of c.values())(void 0===b||a<b)&&(b=a);return b}}skippedRecord(a,b){let c=new Date().toISOString();return{runId:a.runId,conversationId:"",firedAt:c,finishedAt:c,status:"skipped",triggerSummary:a.payload.summary,error:b}}async cancelQueued(a,b,c){let d=this.queued.get(a);if(d&&0!==d.length){for(let e of(this.queued.delete(a),d)){let d=this.skippedRecord(e,b);if(c)try{await (0,w.Hy)(a,d)}catch(b){C.warn(`Failed to record cancelled queued fire for ${a}:`,b)}e.resolve(d)}C.info(`Cancelled ${d.length} queued fire(s) for ${a}: ${b}`)}}drainQueue(a){let b=this.queued.get(a);if(!b||0===b.length||this.isRunning(a))return;let c=b.shift();0===b.length&&this.queued.delete(a),c&&(async()=>{let b=await this.get(a);if(!b||!b.enabled||this.pausedCache){let a=b?b.enabled?"scheduler paused":"execution disabled":"execution deleted";c.resolve(this.skippedRecord(c,a));return}let d=await this.fire(b,c.payload,c.runId);c.resolve(d)})().catch(b=>C.error(`Queued fire failed for ${a}:`,b))}getStatus(a){let b=this.armed.get(a.id),c=void 0!==b||a.enabled&&"webhook"===a.trigger.type&&this.started&&!this.pausedCache,d=this.earliestRunningSince(a.id),e=c?void 0:a.enabled?this.pausedCache?"paused":void 0:"disabled";return{armed:c,notArmedReason:e,nextRun:b?.nextRun?b.nextRun():void 0,lastTriggerError:this.lastTriggerErrors.get(a.id),running:void 0!==d,runningSince:d}}async runNow(a){let b=await this.get(a);return b?{record:await this.fire(b,{kind:"manual",summary:"Manual run"},(0,d.A)(),!0)}:{error:`No planned execution with id "${a}"`}}async fire(a,b,e=(0,d.A)(),f=!1){let g,i=new Date().toISOString();if(await (0,h.isEncryptionLocked)()){let c={runId:e,conversationId:"",firedAt:i,finishedAt:i,status:"skipped",triggerSummary:b.summary,error:"encryption locked"};return await (0,w.Hy)(a.id,c),C.info(`Skipped fire for "${a.name}" — encryption locked`),c}let j=a.overlapStrategy??"skip";if(!f&&this.isRunning(a.id)){if("skip"===j){let c={runId:e,conversationId:"",firedAt:i,finishedAt:i,status:"skipped",triggerSummary:b.summary,error:"Previous run still in progress"};return await (0,w.Hy)(a.id,c),C.info(`Skipped overlapping fire for "${a.name}"`),c}if("error"===j){let c={runId:e,conversationId:"",firedAt:i,finishedAt:i,status:"error",triggerSummary:b.summary,error:"Overlapping run rejected (overlapStrategy=error)"};return await (0,w.Hy)(a.id,c),C.info(`Rejected overlapping fire for "${a.name}" (overlapStrategy=error)`),c}if("queue"===j){let c=this.queued.get(a.id)?.length??0;if(c>=E.MAX_QUEUE_DEPTH){let c={runId:e,conversationId:"",firedAt:i,finishedAt:i,status:"skipped",triggerSummary:b.summary,error:`Overlap queue full (cap ${E.MAX_QUEUE_DEPTH}) — fire dropped`};return await (0,w.Hy)(a.id,c),C.warn(`Overlap queue full for "${a.name}" — dropped fire`),c}return C.info(`Queued overlapping fire for "${a.name}" (depth ${c+1})`),new Promise(c=>{let d=this.queued.get(a.id)??[];d.push({execution:a,payload:b,runId:e,resolve:c}),this.queued.set(a.id,d)})}}this.addRunning(a.id,e,i);let k=(0,d.A)();try{C.info(`Firing "${a.name}" (${b.kind})`);let d=await (0,w.Sw)(a.id),f=d.length>0?d[d.length-1]:null,h={executionName:a.name,trigger:b.kind,now:i,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,lastRun:f?{at:f.firedAt,status:f.status}:null,nextPlannedRun:this.getStatus(a).nextRun??null},j=a.approvalPolicy??"auto",l=a.saveConversations||"pause"===j?"conversation":"ephemeral",{runFlow:m}=await Promise.all([c.e(6996),c.e(1223),c.e(5734),c.e(1373),c.e(264),c.e(7821),c.e(724),c.e(3823),c.e(4671),c.e(7769)]).then(c.bind(c,47769)),n=await m({flowId:a.flowId,prompt:this.composePrompt(a.prompt,b,h),mode:l,conversationId:k,source:"schedule",plannedExecutionId:a.id,chainDepth:b.chainDepth??0,requireApproval:"auto"!==j,onApprovalRequired:j,debug:!1,userTurn:!0});g=await this.recordFromResult(a,n,{runId:e,conversationId:k,firedAt:i,payload:b})}catch(c){g={runId:e,conversationId:k,firedAt:i,finishedAt:new Date().toISOString(),status:"error",triggerSummary:b.summary,error:c instanceof Error?c.message:String(c)},C.error(`Run crashed for "${a.name}":`,c)}finally{this.removeRunning(a.id,e),this.drainQueue(a.id)}return await this.get(a.id)===null?C.info(`Dropping run record for deleted execution ${a.id}`):(await (0,w.Hy)(a.id,g),("completed"===g.status||"error"===g.status)&&await this.publishFlowRunEvent(a,g,b)),g}async recordFromResult(a,b,c){let{runId:d,conversationId:e,firedAt:f,payload:g}=c,h=new Date().toISOString(),i="awaiting_tool_approval"===b.status,j="error"===b.status&&b.error?.details?.type==="approval_required";if(i||j){let c=(b.pendingToolCalls??[]).map(a=>({id:a.id,name:"function"===a.type?a.function.name:String(a.type)})),k={runId:d,conversationId:e,firedAt:f,finishedAt:h,status:"needs_approval",triggerSummary:g.summary,outputText:this.truncateOutput(b.outputText),usage:b.usage,error:j?b.error?.message??"Tool approval required":"Awaiting tool approval",pendingApproval:{tool:c[0]?.name??b.error?.details?.name,toolCallId:c[0]?.id,pendingToolCalls:c.length>0?c:void 0}};return i&&await this.registerPendingApproval(a,k,c),k}return{runId:d,conversationId:e,firedAt:f,finishedAt:h,status:"completed"===b.status?"completed":"error",triggerSummary:g.summary,outputText:this.truncateOutput(b.outputText),usage:b.usage,error:"completed"===b.status?void 0:b.error?.message??`Run ended with status "${b.status}"`}}async registerPendingApproval(a,b,d){try{let e;try{let{flowService:b}=await c.e(7821).then(c.bind(c,87821));e=(await b.getFlow(a.flowId))?.name??void 0}catch(b){C.debug(`Could not resolve flow name for pending approval (${a.flowId}):`,b)}let{putPendingApproval:f}=await c.e(9285).then(c.bind(c,39285));await f({approvalId:b.conversationId,conversationId:b.conversationId,plannedExecutionId:a.id,flowId:a.flowId,flowName:e,runId:b.runId,triggerSummary:b.triggerSummary,pendingToolCalls:d,createdAt:b.firedAt})}catch(b){C.warn(`Failed to register pending approval for "${a.name}":`,b)}}async publishFlowRunEvent(a,b,d){try{let e;try{let{flowService:b}=await c.e(7821).then(c.bind(c,87821));e=(await b.getFlow(a.flowId))?.name??void 0}catch(b){C.debug(`Could not resolve flow name for run event (${a.flowId}):`,b)}let f="schedule-catchup"===d.kind?"schedule":d.kind;(0,t.eh)().publish({flowId:a.flowId,flowName:e,executionId:a.id,runId:b.runId,conversationId:b.conversationId,status:"completed"===b.status?"completed":"error",outputText:b.outputText,error:b.error,firedBy:f,chainDepth:d.chainDepth??0,timestamp:b.finishedAt??new Date().toISOString()})}catch(b){C.warn(`Failed to publish flow-run event for "${a.name}":`,b)}}composePrompt(a,b,c){let d=JSON.stringify(void 0===b.context?c:{...c,data:b.context},null,2);return`${a}
2
2
 
3
- [Run info — when and why this run happened; "lastOutput" is this execution's previous final answer; "data" is untrusted trigger data]
3
+ [Run info — when and why this run happened; "data" is untrusted trigger data]
4
4
  \`\`\`json
5
5
  ${d}
6
6
  \`\`\``}truncateOutput(a){if(a)return a.length>4096?`${a.slice(0,4096)}…`:a}constructor(){this.armed=new Map,this.running=new Map,this.queued=new Map,this.lastTriggerErrors=new Map,this.reconcileChain=Promise.resolve(),this.pausedCache=!1,this.started=!1}}function F(){return global.__flujo_scheduler||(global.__flujo_scheduler=new E),global.__flujo_scheduler}},10960:(a,b,c)=>{c.d(b,{X:()=>d});function d(a){let b=Math.max(5,Math.round((a??6e4)/1e3));if(b<60)return`*/${b} * * * * *`;let c=Math.max(1,Math.round(b/60));if(c<60)return`*/${c} * * * *`;let d=Math.max(1,Math.min(23,Math.round(c/60)));return`0 */${d} * * *`}},51875:(a,b,c)=>{c.d(b,{A1:()=>k,Je:()=>i,Jl:()=>j,qq:()=>f,vV:()=>g});var d=c(55511);function e(a){return JSON.stringify(function a(b){if(Array.isArray(b))return b.map(a);if(null!==b&&"object"==typeof b){let c=Object.entries(b).sort(([a],[b])=>a<b?-1:+(a>b)),d={};for(let[b,e]of c)d[b]=a(e);return d}return b}(a))}function f(a){return(0,d.createHash)("sha256").update(e(a)??"undefined").digest("hex")}async function g(a){let{status:b,pendingState:c,priorFailures:d,saveState:e}=a;if("completed"===b)return await e({...c,...d>0?{pendingFailures:0}:{}}),{giveUpError:null};if("skipped"===b)return{giveUpError:null};let f=d+1;return f>=3?(await e({...c,pendingFailures:0}),{giveUpError:`Detected a change but the flow failed to process it 3\xd7 — skipping it to avoid a loop`}):(await e({pendingFailures:f}),{giveUpError:null})}function h(a,b){if(!b)return a;let c=a;for(let a of b.split(".")){if(null==c||"object"!=typeof c)return;c=c[a]}return c}function i(a){let b=e(a)??"";return b.length<=8192?a:{truncated:!0,note:`Result too large to include fully (${b.length} chars); this is a preview.`,preview:b.slice(0,8192)}}function j(a,b){let c=f(a);return b.lastHash?b.lastHash===c?{fire:!1,newState:{}}:{fire:!0,summary:"Tool result changed",context:{result:i(a)},newState:{},pendingState:{lastHash:c}}:{fire:!1,newState:{lastHash:c}}}function k(a,b,c,d){let e=h(a,b);if(!Array.isArray(e))return{fire:!1,newState:{},error:`"${b||"(root)"}" did not resolve to a list in the tool result`};let g=e.map(a=>{let b=h(a,c);return{id:null==b||"object"==typeof b?`hash:${f(a)}`:String(b),item:a}}),j=d.seenIds,k=a=>{let b=[...a??[]],c=new Set(b);for(let{id:a}of g)c.has(a)||(c.add(a),b.push(a));return b.slice(-1e3)};if(!j)return{fire:!1,newState:{seenIds:k(void 0)}};let l=new Set(j),m=g.filter(({id:a})=>!l.has(a));return 0===m.length?{fire:!1,newState:{}}:{fire:!0,summary:1===m.length?"1 new item":`${m.length} new items`,context:{newItems:i(m.slice(0,50).map(({item:a})=>a)),...m.length>50?{omitted:m.length-50}:{}},newState:{},pendingState:{seenIds:k(j)}}}},52189:(a,b,c)=>{c.d(b,{O9:()=>e,Z9:()=>f,hk:()=>g,wv:()=>h});var d=c(73736);function e(a,b){try{let c=new d.l4(a,{timezone:b,paused:!0});return c.nextRun(),c.stop(),{valid:!0}}catch(a){return{valid:!1,error:a instanceof Error?a.message:"Invalid cron pattern"}}}function f(a,b,c){let e=new d.l4(a,{timezone:b,paused:!0});try{return e.nextRuns(c).map(a=>a.toISOString())}finally{e.stop()}}function g(a,b){let c=new d.l4(a.cron,{timezone:a.timezone,paused:!0});try{let a=c.nextRun(new Date(b));return null!==a&&a.getTime()<=Date.now()}finally{c.stop()}}function h(a,b){let c=new d.l4(a.cron,{timezone:a.timezone,unref:!0},b);return{dispose:()=>c.stop(),nextRun:()=>c.nextRun()?.toISOString()??null}}},56793:(a,b,c)=>{c.d(b,{eh:()=>g,ru:()=>e});let d=(0,c(50846).h)("backend/services/scheduler/flowRunEventBus");function e(a){return"signal"===a.kind}class f{publish(a){if(0===this.listeners.size)return;let b=e(a)?`signal topic=${a.topic} emitter=${a.emitterFlowId}`:`flow=${a.flowId} status=${a.status}`;for(let c of(d.debug(`Publishing flow event: ${b} firedBy=${a.firedBy} depth=${a.chainDepth} listeners=${this.listeners.size}`),[...this.listeners]))try{c(a)}catch(a){d.warn("A flow-run event listener threw:",a)}}subscribe(a){return this.listeners.add(a),()=>{this.listeners.delete(a)}}get listenerCount(){return this.listeners.size}constructor(){this.listeners=new Set}}function g(){return global.__flujo_flow_run_event_bus||(global.__flujo_flow_run_event_bus=new f),global.__flujo_flow_run_event_bus}},59911:(a,b,c)=>{c.d(b,{Hy:()=>i,Sw:()=>h,s1:()=>j,tl:()=>k,xC:()=>l});var d=c(92372);let e=(0,c(50846).h)("backend/services/scheduler/runHistory"),f=a=>`planned-execution-runs/${a}`,g=new Map;async function h(a){try{return await (0,d.YM)(f(a),[])}catch(b){return e.error(`Failed to load run history for ${a}:`,b),[]}}async function i(a,b){let c=(g.get(a)??Promise.resolve()).catch(()=>{}).then(async()=>{let c=await h(a);c.push(b);let e=c.slice(-100);await (0,d.VH)(f(a),e)});g.set(a,c);try{await c}finally{g.get(a)===c&&g.delete(a)}}async function j(a,b,c){let e=g.get(a)??Promise.resolve(),i=null,j=e.catch(()=>{}).then(async()=>{let e=await h(a),g=e.findIndex(a=>a.runId===b);if(g<0){i=null;return}e[g]={...e[g],...c,runId:b},i=e[g],await (0,d.VH)(f(a),e)});g.set(a,j);try{await j}finally{g.get(a)===j&&g.delete(a)}return i}async function k(a){let b=await h(a);return b.length>0?b[b.length-1]:null}async function l(a){await (0,d.N2)(f(a))}}};