onbuzz 4.9.13 → 4.10.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 (451) hide show
  1. package/node_modules/glob/README.md +31 -5
  2. package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
  3. package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
  4. package/node_modules/glob/dist/commonjs/glob.js +2 -1
  5. package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
  6. package/node_modules/glob/dist/commonjs/index.min.js +3 -3
  7. package/node_modules/glob/dist/commonjs/index.min.js.map +4 -4
  8. package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
  9. package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
  10. package/node_modules/glob/dist/commonjs/pattern.js +4 -0
  11. package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
  12. package/node_modules/glob/dist/esm/glob.d.ts +8 -0
  13. package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
  14. package/node_modules/glob/dist/esm/glob.js +2 -1
  15. package/node_modules/glob/dist/esm/glob.js.map +1 -1
  16. package/node_modules/glob/dist/esm/index.min.js +3 -3
  17. package/node_modules/glob/dist/esm/index.min.js.map +4 -4
  18. package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
  19. package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
  20. package/node_modules/glob/dist/esm/pattern.js +4 -0
  21. package/node_modules/glob/dist/esm/pattern.js.map +1 -1
  22. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/README.md +7 -10
  23. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/package.json +7 -18
  24. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/README.md +3 -6
  25. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.js +6 -4
  26. package/node_modules/glob/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -0
  27. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.js +6 -4
  28. package/node_modules/glob/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
  29. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/package.json +11 -7
  30. package/node_modules/glob/node_modules/minimatch/README.md +76 -1
  31. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +1 -1
  32. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -1
  33. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -1
  34. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +4 -2
  35. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
  36. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +309 -55
  37. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
  38. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
  39. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
  40. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
  41. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +1 -1
  42. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -1
  43. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +4 -4
  44. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
  45. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +81 -1
  46. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
  47. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +232 -134
  48. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
  49. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +1 -1
  50. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -1
  51. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +8 -8
  52. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
  53. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +1 -1
  54. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -1
  55. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -1
  56. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +4 -2
  57. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
  58. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +309 -55
  59. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -1
  60. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
  61. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
  62. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
  63. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +1 -1
  64. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -1
  65. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +4 -4
  66. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -1
  67. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +81 -1
  68. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
  69. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +232 -134
  70. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -1
  71. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +1 -1
  72. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -1
  73. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +8 -8
  74. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
  75. package/node_modules/glob/node_modules/minimatch/package.json +17 -11
  76. package/node_modules/glob/package.json +10 -13
  77. package/node_modules/minipass/LICENSE.md +55 -0
  78. package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
  79. package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
  80. package/node_modules/minipass/dist/commonjs/index.js +13 -3
  81. package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
  82. package/node_modules/minipass/dist/esm/index.d.ts +12 -16
  83. package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
  84. package/node_modules/minipass/dist/esm/index.js +3 -1
  85. package/node_modules/minipass/dist/esm/index.js.map +1 -1
  86. package/node_modules/minipass/package.json +9 -14
  87. package/node_modules/path-scurry/node_modules/lru-cache/README.md +96 -10
  88. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.d.ts.map +1 -0
  89. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.js.map +1 -0
  90. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.d.ts +5 -0
  91. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
  92. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts +1400 -0
  93. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -0
  94. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
  95. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -0
  96. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
  97. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +7 -0
  98. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts +12 -0
  99. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts.map +1 -0
  100. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
  101. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js.map +1 -0
  102. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.cjs.map +1 -0
  103. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
  104. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
  105. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
  106. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +109 -32
  107. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
  108. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +334 -197
  109. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
  110. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
  111. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +4 -4
  112. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.d.ts.map +1 -0
  113. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.js.map +1 -0
  114. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.d.ts +5 -0
  115. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
  116. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts +1400 -0
  117. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -0
  118. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
  119. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -0
  120. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
  121. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +7 -0
  122. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts +12 -0
  123. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts.map +1 -0
  124. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
  125. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js.map +1 -0
  126. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts +12 -0
  127. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts.map +1 -0
  128. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
  129. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js.map +1 -0
  130. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.d.ts.map +1 -0
  131. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.js.map +1 -0
  132. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
  133. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  134. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts +1400 -0
  135. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -0
  136. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
  137. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -0
  138. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  139. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js.map +7 -0
  140. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts +12 -0
  141. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts.map +1 -0
  142. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
  143. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js.map +1 -0
  144. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
  145. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.mjs.map +1 -0
  146. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
  147. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  148. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +109 -32
  149. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
  150. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +333 -196
  151. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -1
  152. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +1 -1
  153. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +4 -4
  154. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.d.ts.map +1 -0
  155. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.js.map +1 -0
  156. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
  157. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
  158. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts +1400 -0
  159. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -0
  160. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
  161. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js.map +1 -0
  162. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  163. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js.map +7 -0
  164. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts +12 -0
  165. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts.map +1 -0
  166. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
  167. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js.map +1 -0
  168. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts +12 -0
  169. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts.map +1 -0
  170. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
  171. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js.map +1 -0
  172. package/node_modules/path-scurry/node_modules/lru-cache/package.json +71 -18
  173. package/node_modules/path-scurry/package.json +8 -24
  174. package/package.json +1 -1
  175. package/scripts/debug-balance-probe.mjs +35 -35
  176. package/scripts/push-image.sh +43 -43
  177. package/scripts/setup-acr.sh +65 -65
  178. package/scripts/verify-optional-deps.js +96 -1
  179. package/src/__tests__/composioCliFlags.test.js +239 -239
  180. package/src/analyzers/CSSAnalyzer.js +298 -297
  181. package/src/analyzers/ConfigValidator.js +691 -690
  182. package/src/analyzers/ESLintAnalyzer.js +320 -320
  183. package/src/analyzers/JavaScriptAnalyzer.js +260 -261
  184. package/src/analyzers/PrettierFormatter.js +246 -247
  185. package/src/analyzers/PythonAnalyzer.js +283 -283
  186. package/src/analyzers/SecurityAnalyzer.js +729 -729
  187. package/src/analyzers/SparrowAnalyzer.js +341 -341
  188. package/src/analyzers/TypeScriptAnalyzer.js +247 -247
  189. package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -41
  190. package/src/analyzers/__tests__/ConfigValidator.test.js +362 -362
  191. package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -40
  192. package/src/analyzers/__tests__/PythonAnalyzer.test.js +205 -208
  193. package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -303
  194. package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -187
  195. package/src/analyzers/codeCloneDetector/analyzer.js +344 -344
  196. package/src/analyzers/codeCloneDetector/detector.js +250 -250
  197. package/src/analyzers/codeCloneDetector/index.js +194 -192
  198. package/src/analyzers/codeCloneDetector/parser.js +199 -199
  199. package/src/core/__tests__/agentPool.test.js +866 -866
  200. package/src/core/__tests__/agentPoolAutoResume.test.js +209 -209
  201. package/src/core/__tests__/agentPoolWakeOnMessage.test.js +315 -315
  202. package/src/core/__tests__/agentScheduler.emptyResponseChatStall.test.js +213 -213
  203. package/src/core/__tests__/agentScheduler.errorCategorisation.test.js +246 -246
  204. package/src/core/__tests__/agentScheduler.firstChunkTimeout.test.js +138 -138
  205. package/src/core/__tests__/agentScheduler.modeTransitions.test.js +233 -233
  206. package/src/core/__tests__/agentScheduler.nativePromptPick.test.js +319 -319
  207. package/src/core/__tests__/agentScheduler.taskLifecycleInstruction.test.js +78 -78
  208. package/src/core/__tests__/agentScheduler.visualizer.test.js +258 -258
  209. package/src/core/__tests__/flowCheckpointStore.test.js +140 -140
  210. package/src/core/__tests__/flowEndToEnd.test.js +565 -565
  211. package/src/core/__tests__/flowFieldMapping.test.js +188 -189
  212. package/src/core/__tests__/flowLintClientMirror.test.js +96 -98
  213. package/src/core/__tests__/flowSavePayload.test.js +170 -169
  214. package/src/core/__tests__/flowTemplates.test.js +311 -311
  215. package/src/core/__tests__/flowVersionStore.test.js +123 -123
  216. package/src/core/__tests__/messageProcessor.test.js +669 -669
  217. package/src/core/__tests__/stateManager.test.js +0 -1
  218. package/src/core/agentPool.js +2474 -2475
  219. package/src/core/agentScheduler.js +1 -4
  220. package/src/core/contextManager.js +708 -708
  221. package/src/core/flowExecutor.js +1510 -1510
  222. package/src/core/flowFieldMapping.js +136 -138
  223. package/src/core/messageProcessor.js +953 -954
  224. package/src/core/orchestrator.js +593 -595
  225. package/src/core/stateManager.js +1765 -1752
  226. package/src/index.js +1221 -1221
  227. package/src/interfaces/__tests__/archivedAgentDelete.test.js +207 -207
  228. package/src/interfaces/__tests__/bulkAgentRoute.test.js +361 -361
  229. package/src/interfaces/__tests__/imageServing.test.js +228 -228
  230. package/src/interfaces/__tests__/remoteSessionAuth.test.js +308 -308
  231. package/src/interfaces/__tests__/videoJobsRoutes.test.js +178 -179
  232. package/src/interfaces/__tests__/webServer.marketplace.test.js +629 -629
  233. package/src/interfaces/schedulerRoutes.js +50 -50
  234. package/src/interfaces/terminal/__tests__/smoke/connection.test.js +341 -350
  235. package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -156
  236. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +325 -330
  237. package/src/interfaces/terminal/__tests__/smoke/tools.test.js +385 -388
  238. package/src/interfaces/terminal/api/session.js +265 -266
  239. package/src/interfaces/terminal/api/websocket.js +496 -497
  240. package/src/interfaces/terminal/components/AgentCreator.js +691 -705
  241. package/src/interfaces/terminal/components/AgentEditor.js +676 -678
  242. package/src/interfaces/terminal/components/AgentSwitcher.js +331 -330
  243. package/src/interfaces/terminal/components/ErrorPanel.js +263 -264
  244. package/src/interfaces/terminal/components/Header.js +28 -28
  245. package/src/interfaces/terminal/components/Layout.js +598 -603
  246. package/src/interfaces/terminal/components/MessageList.js +280 -281
  247. package/src/interfaces/terminal/components/SettingsPanel.js +410 -415
  248. package/src/interfaces/terminal/components/StatusBar.js +2 -0
  249. package/src/interfaces/terminal/index.js +168 -168
  250. package/src/interfaces/terminal/state/useAgentControl.js +496 -496
  251. package/src/interfaces/terminal/state/useAgents.js +537 -537
  252. package/src/interfaces/terminal/state/useMessages.js +629 -630
  253. package/src/interfaces/terminal/state/useTools.js +554 -554
  254. package/src/interfaces/terminal/utils/debugLogger.js +44 -44
  255. package/src/interfaces/terminal/utils/settingsStorage.js +232 -232
  256. package/src/interfaces/webServer.js +7578 -7579
  257. package/src/interfaces/webServer.js.bak +7046 -7046
  258. package/src/modules/fileExplorer/__tests__/zipDownload.test.js +237 -237
  259. package/src/modules/fileExplorer/controller.js +470 -469
  260. package/src/modules/fileExplorer/routes.js +285 -286
  261. package/src/modules/widget/__tests__/isDisabled.test.js +41 -41
  262. package/src/modules/widget/__tests__/routes.test.js +677 -678
  263. package/src/modules/widget/__tests__/runtime.test.js +401 -401
  264. package/src/modules/widget/__tests__/versioning.test.js +309 -309
  265. package/src/modules/widget/__tests__/webComponentRuntime.test.js +565 -565
  266. package/src/modules/widget/__tests__/widgetTool.test.js +316 -316
  267. package/src/modules/widget/routes.js +435 -435
  268. package/src/modules/widget/runtime/bundle.js +640 -640
  269. package/src/modules/widget/runtime/webComponentBundle.js +470 -470
  270. package/src/modules/widget/schema.js +182 -181
  271. package/src/modules/widget/widgetTool.js +1389 -1389
  272. package/src/services/__tests__/agentActivityService.test.js +401 -402
  273. package/src/services/__tests__/benchmarkService.test.js +184 -184
  274. package/src/services/__tests__/contextInjectionService.test.js +246 -246
  275. package/src/services/__tests__/conversationQuery.test.js +721 -723
  276. package/src/services/__tests__/credentialVault.test.js +469 -469
  277. package/src/services/__tests__/discordService.integration.test.js +638 -639
  278. package/src/services/__tests__/flowContextService.test.js +590 -590
  279. package/src/services/__tests__/memoryService.test.js +1 -1
  280. package/src/services/__tests__/messageSource.test.js +380 -380
  281. package/src/services/__tests__/modelRouterNaming.test.js +111 -111
  282. package/src/services/__tests__/projectDetector.test.js +34 -34
  283. package/src/services/__tests__/promptService.test.js +242 -242
  284. package/src/services/__tests__/telegramService.test.js +941 -941
  285. package/src/services/__tests__/tokenCountingService.test.js +48 -48
  286. package/src/services/agentActivityService.js +419 -420
  287. package/src/services/aiService.js +2997 -3001
  288. package/src/services/apiKeyManager.js +359 -359
  289. package/src/services/benchmarkService.js +196 -196
  290. package/src/services/codebaseKnowledgeService.js +2 -2
  291. package/src/services/composioService.js +738 -738
  292. package/src/services/conversationCompactionService.js +1258 -1257
  293. package/src/services/credentialVault.js +685 -685
  294. package/src/services/discordService.js +792 -793
  295. package/src/services/embeddings/__tests__/azureCustomProvider.test.js +232 -232
  296. package/src/services/embeddings/__tests__/embeddingService.test.js +417 -417
  297. package/src/services/embeddings/__tests__/localProvider.test.js +263 -263
  298. package/src/services/embeddings/autoRecall.js +218 -219
  299. package/src/services/embeddings/indexers/__tests__/agentIndexer.test.js +232 -232
  300. package/src/services/embeddings/indexers/__tests__/memoryIndexer.test.js +418 -418
  301. package/src/services/embeddings/indexers/__tests__/reminisceIndexer.test.js +356 -357
  302. package/src/services/embeddings/indexers/__tests__/skillsIndexer.test.js +145 -145
  303. package/src/services/embeddings/indexers/__tests__/taskIndexer.test.js +146 -146
  304. package/src/services/embeddings/indexers/composioIndexer.js +279 -279
  305. package/src/services/embeddings/providerInterface.js +206 -206
  306. package/src/services/embeddings/providers/localProvider.js +11 -7
  307. package/src/services/embeddings/providers/openaiProvider.js +101 -101
  308. package/src/services/embeddings/vectorStore/inMemoryJsonStore.js +356 -356
  309. package/src/services/errorHandler.js +809 -809
  310. package/src/services/flowContextService.js +586 -586
  311. package/src/services/grounding/MockAdapter.js +125 -125
  312. package/src/services/modelRouterService.js +26 -31
  313. package/src/services/modelsService.js +322 -322
  314. package/src/services/ollamaService.js +452 -452
  315. package/src/services/projectDetector.js +403 -404
  316. package/src/services/promptService.js +418 -418
  317. package/src/services/qualityInspector.js +795 -795
  318. package/src/services/scheduleService.js +726 -726
  319. package/src/services/serviceRegistry.js +386 -386
  320. package/src/services/telegrafBot.js +174 -174
  321. package/src/services/telegramService.js +1972 -1972
  322. package/src/services/visualEditorBridge.js +1033 -1033
  323. package/src/services/visualEditorServer.js +1769 -1774
  324. package/src/services/whatsappService.js +667 -668
  325. package/src/tools/__tests__/agentCommunicationTool.findAgent.test.js +226 -226
  326. package/src/tools/__tests__/agentCommunicationTool.test.js +3 -3
  327. package/src/tools/__tests__/agentDelayTool.test.js +342 -342
  328. package/src/tools/__tests__/baseTool.test.js +3 -3
  329. package/src/tools/__tests__/codeMapTool.test.js +915 -915
  330. package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -309
  331. package/src/tools/__tests__/fileTreeTool.test.js +274 -274
  332. package/src/tools/__tests__/filesystemTool.test.js +815 -815
  333. package/src/tools/__tests__/foundryWebSearchTool.test.js +252 -252
  334. package/src/tools/__tests__/imageTool.validator.test.js +194 -194
  335. package/src/tools/__tests__/jobDoneTool.test.js +580 -581
  336. package/src/tools/__tests__/memoryTool.forgetStale.test.js +272 -272
  337. package/src/tools/__tests__/memoryTool.reminisce.test.js +2 -2
  338. package/src/tools/__tests__/memoryTool.reminisceSemanticSearch.test.js +301 -301
  339. package/src/tools/__tests__/memoryTool.semanticSearch.test.js +405 -405
  340. package/src/tools/__tests__/memoryTool.teamPool.test.js +293 -293
  341. package/src/tools/__tests__/memoryTool.test.js +1 -1
  342. package/src/tools/__tests__/seekTool.test.js +282 -282
  343. package/src/tools/__tests__/skillsTool.search.test.js +164 -164
  344. package/src/tools/__tests__/skillsTool.test.js +226 -226
  345. package/src/tools/__tests__/staticAnalysisTool.test.js +509 -509
  346. package/src/tools/__tests__/taskManagerTool.discipline.test.js +137 -137
  347. package/src/tools/__tests__/taskManagerTool.search.test.js +143 -143
  348. package/src/tools/__tests__/taskManagerTool.test.js +866 -866
  349. package/src/tools/__tests__/terminalTool.test.js +448 -448
  350. package/src/tools/__tests__/toolShapeForgiveness.test.js +259 -260
  351. package/src/tools/__tests__/userPromptTool.test.js +297 -297
  352. package/src/tools/__tests__/videoTool.jobs.test.js +147 -147
  353. package/src/tools/__tests__/webTool.e2e.test.js +609 -603
  354. package/src/tools/__tests__/webTool.unit.test.js +195 -195
  355. package/src/tools/__tests__/webTool.visionModel.test.js +75 -75
  356. package/src/tools/agentCommunicationTool.js +8 -10
  357. package/src/tools/agentDelayTool.js +496 -497
  358. package/src/tools/asyncToolManager.js +602 -603
  359. package/src/tools/baseTool.js +12 -11
  360. package/src/tools/cloneDetectionTool.js +576 -581
  361. package/src/tools/codeMapTool.js +0 -6
  362. package/src/tools/composioTool.js +617 -617
  363. package/src/tools/dependencyResolverTool.js +1211 -1212
  364. package/src/tools/desktop/DesktopTool.js +629 -638
  365. package/src/tools/desktop/__tests__/DesktopTool.e2e.test.js +306 -306
  366. package/src/tools/desktop/__tests__/DesktopTool.test.js +507 -507
  367. package/src/tools/desktop/__tests__/osController.test.js +364 -364
  368. package/src/tools/desktop/osController.js +491 -491
  369. package/src/tools/docxTool.js +623 -623
  370. package/src/tools/excelTool.js +636 -636
  371. package/src/tools/fileContentReplaceTool.js +5 -7
  372. package/src/tools/fileSystemTool.js +12 -19
  373. package/src/tools/fileTreeTool.js +840 -840
  374. package/src/tools/foundryWebSearchTool.js +273 -273
  375. package/src/tools/helpTool.js +198 -198
  376. package/src/tools/imageTool.js +1397 -1397
  377. package/src/tools/importAnalyzerTool.js +1056 -1056
  378. package/src/tools/jobDoneTool.js +495 -495
  379. package/src/tools/memoryTool.js +1 -1
  380. package/src/tools/office/pres/__tests__/presSystem.test.js +365 -365
  381. package/src/tools/office/pres/archetypes/agenda.js +61 -61
  382. package/src/tools/office/pres/archetypes/bentoGrid.js +218 -219
  383. package/src/tools/office/pres/archetypes/bigStat.js +140 -142
  384. package/src/tools/office/pres/archetypes/closing.js +70 -70
  385. package/src/tools/office/pres/archetypes/hero.js +70 -70
  386. package/src/tools/office/pres/archetypes/productHero.js +93 -94
  387. package/src/tools/office/pres/archetypes/table.js +73 -74
  388. package/src/tools/office/pres/backgrounds/orb.js +66 -66
  389. package/src/tools/office/pres/components.js +422 -423
  390. package/src/tools/officeTool.js +441 -441
  391. package/src/tools/pdfTool.js +625 -627
  392. package/src/tools/platformControlTool.js +1081 -1081
  393. package/src/tools/seekTool.js +917 -918
  394. package/src/tools/skillsTool.js +1 -1
  395. package/src/tools/staticAnalysisTool.js +2143 -2146
  396. package/src/tools/taskManagerTool.js +3324 -3324
  397. package/src/tools/terminalTool.js +2615 -2618
  398. package/src/tools/videoTool.js +1303 -1303
  399. package/src/tools/visionTool.js +508 -508
  400. package/src/tools/visualEditorTool.js +1289 -1290
  401. package/src/tools/webTool.js +3368 -3368
  402. package/src/tools/whatsappTool.js +464 -464
  403. package/src/types/__tests__/agent.test.js +499 -499
  404. package/src/types/__tests__/contextReference.test.js +606 -606
  405. package/src/types/__tests__/conversation.test.js +555 -555
  406. package/src/types/__tests__/toolCommand.test.js +584 -584
  407. package/src/types/contextReference.js +974 -971
  408. package/src/types/conversation.js +729 -729
  409. package/src/types/toolCommand.js +746 -746
  410. package/src/utilities/__tests__/attachmentValidator.test.js +80 -80
  411. package/src/utilities/__tests__/auditReport.test.js +328 -328
  412. package/src/utilities/__tests__/directoryAccessManager.test.js +388 -388
  413. package/src/utilities/__tests__/jsonRepair.test.js +103 -104
  414. package/src/utilities/__tests__/modeTransitionReasons.test.js +105 -105
  415. package/src/utilities/__tests__/platformUtils.test.js +80 -87
  416. package/src/utilities/__tests__/structuredFileValidator.test.js +261 -263
  417. package/src/utilities/__tests__/toolConstants.test.js +92 -94
  418. package/src/utilities/__tests__/useIsTouchDevice.detect.test.js +114 -114
  419. package/src/utilities/__tests__/webUiUtilSync.test.js +117 -117
  420. package/src/utilities/attachmentValidator.js +284 -288
  421. package/src/utilities/authCache.js.backup-1779570472481 +121 -121
  422. package/src/utilities/browserStealth.js +631 -630
  423. package/src/utilities/configManager.js +616 -617
  424. package/src/utilities/directoryAccessManager.js +564 -565
  425. package/src/utilities/fileProcessor.js +308 -307
  426. package/src/utilities/humanBehavior.js +454 -453
  427. package/src/utilities/logger.js +479 -479
  428. package/src/utilities/structuredFileValidator.js +696 -699
  429. package/src/utilities/tagParser.js +5 -10
  430. package/src/utilities/userDataDir.js +308 -308
  431. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +0 -1
  432. package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +0 -1
  433. package/node_modules/minipass/LICENSE +0 -15
  434. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/LICENSE.md +0 -0
  435. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts +0 -0
  436. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts.map +0 -0
  437. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js +0 -0
  438. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js.map +0 -0
  439. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/package.json +0 -0
  440. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts +0 -0
  441. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts.map +0 -0
  442. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js +0 -0
  443. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js.map +0 -0
  444. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/package.json +0 -0
  445. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/LICENSE +0 -0
  446. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts +0 -0
  447. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts.map +0 -0
  448. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/package.json +0 -0
  449. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts +0 -0
  450. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts.map +0 -0
  451. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/package.json +0 -0
@@ -1,586 +1,586 @@
1
- /**
2
- * FlowContextService - Builds flow-aware context for agents in flow execution
3
- *
4
- * Purpose:
5
- * - Inject flow execution instructions into agent system prompts
6
- * - Pass previous agent data (summary, files created, output) to next agent
7
- * - Guide agents on comprehensive job-done completion
8
- */
9
-
10
- class FlowContextService {
11
- constructor(config = {}, logger = null) {
12
- this.config = config;
13
- this.logger = logger;
14
- }
15
-
16
- /**
17
- * Build flow execution context to inject into agent's system prompt
18
- * @param {Object} flowMetadata - Current flow execution metadata
19
- * @param {Object} previousAgentData - Data from previous agent (if any)
20
- * @param {Object} [nodeContract] - v2 typed I/O { inputs, outputs }; when
21
- * present, the system prompt advertises
22
- * the EXACT named/typed payload the
23
- * agent receives and must produce.
24
- * @returns {string} Context string to inject into system prompt
25
- */
26
- buildFlowAgentContext(flowMetadata, previousAgentData, nodeContract) {
27
- const sections = [];
28
-
29
- // Flow execution header
30
- sections.push(`
31
- <FLOW_EXECUTION_CONTEXT>
32
- You are executing step "${flowMetadata.nodeName || 'Agent'}" (${flowMetadata.nodePosition}/${flowMetadata.totalNodes}) in flow "${flowMetadata.flowName}".`);
33
-
34
- // Phase 7: overall flow goal — orients each agent to the bigger
35
- // picture so they reason about their step in context of the whole.
36
- if (this._hasText(flowMetadata?.flowDescription)) {
37
- sections.push(`
38
- FLOW GOAL:
39
- ${flowMetadata.flowDescription.trim()}`);
40
- }
41
-
42
- // Phase 7: per-node instructions — role, working style, and
43
- // success criteria for THIS step. Distinct from the user prompt
44
- // template (which is the input message). Lives at node.data.instructions.
45
- if (this._hasText(nodeContract?.instructions)) {
46
- sections.push(`
47
- NODE INSTRUCTIONS (your role + how to succeed at this step):
48
- ${nodeContract.instructions.trim()}`);
49
- }
50
-
51
- // Critical instruction about context isolation
52
- sections.push(`
53
- CRITICAL HANDOFF REQUIREMENT:
54
- The NEXT agent in this flow has NO ACCESS to your conversation history or any context you've built up.
55
- Everything you learn, create, or discover must be explicitly passed via job-done.`);
56
-
57
- // v2: declared INPUTS — what payload the agent is receiving.
58
- // Phase 7: each input now optionally carries a description and an
59
- // example. The description tells the agent what this input
60
- // represents AND how to use it; the example shows concrete shape.
61
- if (nodeContract && Array.isArray(nodeContract.inputs) && nodeContract.inputs.length > 0) {
62
- const lines = nodeContract.inputs.map(i => this._renderIOEntry(i, /*required default*/ true));
63
- sections.push(`
64
- INPUTS (this step receives these typed values; reference them in your reasoning):
65
- ${lines.join('\n')}`);
66
- }
67
-
68
- // v2: declared OUTPUTS — exactly what the agent must produce.
69
- // Phase 7 hardening: stronger framing + concrete example shape that
70
- // the model can mimic literally. Empirical finding: weak instruction
71
- // ("you MUST include outputs") + abstract placeholder (`<text value>`)
72
- // → models default to schema-minimum. Loud framing + concrete shape
73
- // dramatically improves first-pass compliance.
74
- if (nodeContract && Array.isArray(nodeContract.outputs) && nodeContract.outputs.length > 0) {
75
- const lines = nodeContract.outputs.map(o => this._renderIOEntry(o, /*required default*/ false));
76
- // Build a concrete example object using each field's example if
77
- // provided, else a typed placeholder. The agent should imitate
78
- // this shape literally — only the values change.
79
- const exampleEntries = nodeContract.outputs.map(o => {
80
- let exVal;
81
- if (o.example !== undefined && o.example !== null) {
82
- try { exVal = JSON.stringify(o.example); }
83
- catch { exVal = this._typedPlaceholder(o.type); }
84
- } else {
85
- exVal = this._typedPlaceholder(o.type);
86
- }
87
- return ` "${o.name}": ${exVal}`;
88
- }).join(',\n');
89
-
90
- sections.push(`
91
- ═══════════════════════════════════════════════════════════════
92
- REQUIRED OUTPUTS — your job-done call MUST populate every field below
93
- ═══════════════════════════════════════════════════════════════
94
-
95
- ${lines.join('\n')}
96
-
97
- THE ONLY VALID FORMAT for ending this task is a jobdone tool call
98
- with this exact shape (replace example values with your real content,
99
- keep field names and structure identical):
100
-
101
- {
102
- "toolId": "jobdone",
103
- "actions": [{
104
- "action": "complete",
105
- "summary": "<your one-paragraph summary>",
106
- "outputs": {
107
- ${exampleEntries}
108
- }
109
- }]
110
- }
111
-
112
- Calls without the "outputs" field — or with "outputs" missing any of
113
- the field names listed above — will be REJECTED and you will be
114
- re-prompted. The flow CANNOT proceed without these structured outputs.
115
- ═══════════════════════════════════════════════════════════════`);
116
- }
117
-
118
- // Job-done instructions
119
- sections.push(`
120
- When you complete your task, you MUST call the job-done tool with comprehensive details:
121
- - summary: A complete summary of what you accomplished (not just "task complete")
122
- - details: ALL information the next agent needs to continue, including:
123
- • Full results, findings, or output content
124
- • File paths of ANY files created or modified (list each explicitly)
125
- • Important decisions made and their rationale
126
- • Any warnings, caveats, or considerations for the next step
127
- • Context that would be lost without explicit documentation`);
128
-
129
- // Previous agent context (if exists)
130
- if (previousAgentData) {
131
- sections.push(`
132
- CONTEXT FROM PREVIOUS AGENT:
133
- ${previousAgentData.agentName ? `- Previous agent: ${previousAgentData.agentName} (${previousAgentData.agentId})` : `- Previous agent ID: ${previousAgentData.agentId}`}
134
- ${previousAgentData.summary ? `- Their summary: ${previousAgentData.summary}` : ''}
135
- ${previousAgentData.filesCreated?.length > 0 ? `- Files they created/modified:\n${previousAgentData.filesCreated.map(f => ` • ${f}`).join('\n')}` : '- No files created by previous agent'}
136
- ${previousAgentData.output ? `
137
- - Their output:
138
- ${this._formatPreviousOutput(previousAgentData.output)}` : ''}`);
139
-
140
- // v2: render the structured outputs bag if upstream agents
141
- // produced one. THIS is the handoff payload — agents downstream
142
- // should reason from these named fields, not from the prose
143
- // summary. Without this section the typed contract is invisible
144
- // at runtime and agents fabricate the data.
145
- if (previousAgentData.outputs && typeof previousAgentData.outputs === 'object'
146
- && Object.keys(previousAgentData.outputs).length > 0) {
147
- const lines = Object.entries(previousAgentData.outputs).map(([k, v]) =>
148
- ` • ${k} = ${this._formatStructuredValue(v)}`
149
- );
150
- sections.push(`
151
- STRUCTURED HANDOFF FROM UPSTREAM (use these as the source of truth — not the summary):
152
- ${lines.join('\n')}`);
153
- }
154
-
155
- // If multiple agents fed this node, list contributors per-agent
156
- // so the model knows who produced what. Helpful for fan-in nodes.
157
- if (Array.isArray(previousAgentData.contributors) && previousAgentData.contributors.length > 1) {
158
- const blocks = previousAgentData.contributors.map(c => {
159
- const outs = c.outputs && Object.keys(c.outputs).length > 0
160
- ? Object.entries(c.outputs).map(([k, v]) => ` ${k} = ${this._formatStructuredValue(v)}`).join('\n')
161
- : ' (no structured outputs)';
162
- return ` - ${c.agentName} (${c.agentId}):\n${outs}`;
163
- });
164
- sections.push(`
165
- ALL UPSTREAM CONTRIBUTORS (this node has multiple inputs):
166
- ${blocks.join('\n')}`);
167
- }
168
- } else {
169
- sections.push(`
170
- This is the FIRST agent in the flow - you are receiving the initial user input.`);
171
- }
172
-
173
- // Closing instructions
174
- sections.push(`
175
- FLOW EXECUTION RULES:
176
- 1. Focus on your specific task in this pipeline step
177
- 2. Be thorough in your job-done summary - err on the side of more detail
178
- 3. List ALL file operations explicitly (create, modify, delete)
179
- 4. Stay in agent mode after job-done in case there are follow-up questions
180
- 5. Do not assume the next agent knows anything about what you did
181
-
182
- </FLOW_EXECUTION_CONTEXT>`);
183
-
184
- return sections.join('');
185
- }
186
-
187
- /**
188
- * Format previous agent's output for display (truncate if very long)
189
- * @param {string|Object} output - Previous output
190
- * @returns {string} Formatted output
191
- */
192
- /**
193
- * Phase 8 (the big one): build a STANDALONE system prompt that REPLACES
194
- * the agent's persisted system prompt for the duration of a flow step.
195
- *
196
- * Why: production findings showed the agent's own system prompt
197
- * ("You are a software developer...") fights the flow contract.
198
- * The agent's training pulls it toward its native role
199
- * (engineering, code, task lists) instead of producing the typed
200
- * outputs the flow declares. Appending flow context didn't beat the
201
- * prior identity.
202
- *
203
- * This builder produces a single self-contained prompt that defines
204
- * the agent's IDENTITY for this step in terms of the flow node's
205
- * declared role + I/O contract. The agent's tools/capabilities/model
206
- * stay the same — only the role-defining prose changes.
207
- *
208
- * Returns null when there's no node-level role to assert (no
209
- * instructions AND no typed I/O). In that case the caller falls back
210
- * to the legacy append-context behavior.
211
- *
212
- * @returns {string|null} the new system prompt, or null to keep native
213
- */
214
- buildFlowAgentSystemPrompt(flowMetadata, previousAgentData, nodeContract) {
215
- if (!nodeContract) return null;
216
- const hasInstructions = this._hasText(nodeContract.instructions);
217
- const hasOutputs = Array.isArray(nodeContract.outputs) && nodeContract.outputs.length > 0;
218
- if (!hasInstructions && !hasOutputs) return null;
219
-
220
- const sections = [];
221
- sections.push(
222
- `You are acting as the "${flowMetadata.nodeName || 'Agent'}" step (${flowMetadata.nodePosition}/${flowMetadata.totalNodes}) of the flow "${flowMetadata.flowName}".`
223
- );
224
-
225
- if (this._hasText(flowMetadata?.flowDescription)) {
226
- sections.push(`OVERALL FLOW GOAL:\n${flowMetadata.flowDescription.trim()}`);
227
- }
228
-
229
- if (hasInstructions) {
230
- sections.push(`YOUR ROLE FOR THIS STEP:\n${nodeContract.instructions.trim()}`);
231
- }
232
-
233
- sections.push(
234
- `HOW TO COMPLETE THIS STEP — the only valid completion:
235
- 1. Read the typed inputs you receive (listed below).
236
- 2. Produce the typed outputs declared below.
237
- 3. Call the job-done tool with summary, details, AND a populated
238
- "outputs" object containing every required field.
239
-
240
- DO NOT:
241
- - Maintain or update task lists. This step has no other steps.
242
- - Write status paragraphs. The flow is autonomous.
243
- - Call any tool other than job-done unless it is genuinely required
244
- to gather data for the structured outputs.
245
- - Defer the work. There is no human in this conversation — produce
246
- the outputs and finish.
247
-
248
- The flow CANNOT proceed until you call job-done with the correct
249
- "outputs" object. Calls that omit any required field will be rejected
250
- and you will be asked to re-emit.`
251
- );
252
-
253
- // INPUTS — typed, with descriptions/examples
254
- if (Array.isArray(nodeContract.inputs) && nodeContract.inputs.length > 0) {
255
- const lines = nodeContract.inputs.map(i => this._renderIOEntry(i, true));
256
- sections.push(`INPUTS (you receive these typed values from upstream):\n${lines.join('\n')}`);
257
- }
258
-
259
- // OUTPUTS — typed, with descriptions/examples + concrete shape
260
- if (hasOutputs) {
261
- const lines = nodeContract.outputs.map(o => this._renderIOEntry(o, false));
262
- const exampleEntries = nodeContract.outputs.map(o => {
263
- let exVal;
264
- if (o.example !== undefined && o.example !== null) {
265
- try { exVal = JSON.stringify(o.example); }
266
- catch { exVal = this._typedPlaceholder(o.type); }
267
- } else {
268
- exVal = this._typedPlaceholder(o.type);
269
- }
270
- return ` "${o.name}": ${exVal}`;
271
- }).join(',\n');
272
-
273
- sections.push(
274
- `REQUIRED OUTPUTS — you MUST populate every field below in job-done's "outputs":
275
- ${lines.join('\n')}
276
-
277
- EXACT job-done call shape (replace example values; keep keys identical):
278
- {
279
- "toolId": "jobdone",
280
- "actions": [{
281
- "action": "complete",
282
- "summary": "<one-paragraph summary of what you produced>",
283
- "outputs": {
284
- ${exampleEntries}
285
- }
286
- }]
287
- }`
288
- );
289
- }
290
-
291
- // Previous agent context — same as in append mode but inside the
292
- // standalone prompt now.
293
- if (previousAgentData) {
294
- const parts = [`Previous step: ${previousAgentData.agentName || previousAgentData.agentId}.`];
295
- if (previousAgentData.summary) parts.push(`Their summary: ${previousAgentData.summary}`);
296
- if (previousAgentData.outputs && Object.keys(previousAgentData.outputs).length > 0) {
297
- const fields = Object.entries(previousAgentData.outputs)
298
- .map(([k, v]) => ` • ${k} = ${this._formatStructuredValue(v)}`)
299
- .join('\n');
300
- parts.push(`Their structured outputs (use as your source of truth):\n${fields}`);
301
- } else if (previousAgentData.output) {
302
- parts.push(`Their output:\n${this._formatPreviousOutput(previousAgentData.output)}`);
303
- }
304
- sections.push(`UPSTREAM CONTEXT:\n${parts.join('\n')}`);
305
- } else {
306
- sections.push(`This is the FIRST step. You receive the user's initial input.`);
307
- }
308
-
309
- return sections.join('\n\n');
310
- }
311
-
312
- /**
313
- * Phase 7 hardening helper: typed placeholder rendering for the
314
- * concrete example block in REQUIRED OUTPUTS. Used only when the
315
- * field has no `example` declared. Each placeholder is itself valid
316
- * JSON of the right type so the agent can copy-paste the structure.
317
- */
318
- _typedPlaceholder(type) {
319
- switch (type) {
320
- case 'text': return '"<your text content here>"';
321
- case 'number': return '0';
322
- case 'boolean': return 'true';
323
- case 'json': return '{ "key": "value" }';
324
- case 'file': return '"/path/to/file.ext"';
325
- case 'file[]': return '["/path/to/file1", "/path/to/file2"]';
326
- case 'list<text>': return '["item 1", "item 2", "item 3"]';
327
- default: return '"<value>"';
328
- }
329
- }
330
-
331
- /**
332
- * Phase 7 helper: true when a string is present and non-empty after
333
- * trimming. Empty/whitespace-only descriptions are treated as absent.
334
- */
335
- _hasText(s) {
336
- return typeof s === 'string' && s.trim().length > 0;
337
- }
338
-
339
- /**
340
- * Phase 7 helper: render one input or output declaration as a multi-
341
- * line block:
342
- *
343
- * • topic (text, required)
344
- * The research topic exactly as provided.
345
- * Example: "AI safety"
346
- *
347
- * Description and example are both optional. Required-marker policy
348
- * differs slightly between inputs (default required:true) and outputs
349
- * (no inherent required flag) — caller passes `requiredByDefault`.
350
- *
351
- * The same description text is read by BOTH the producer (interprets
352
- * as "format thus") and the consumer (interprets as "expect thus").
353
- * That bidirectional contract is the whole point.
354
- */
355
- _renderIOEntry(entry, requiredByDefault) {
356
- if (!entry || typeof entry.name !== 'string') return '';
357
- const reqLabel = (entry.required === true) ? ', required'
358
- : (entry.required === false) ? ', optional'
359
- : (requiredByDefault ? ', required' : '');
360
- const head = ` • ${entry.name} (${entry.type}${reqLabel})`;
361
- const lines = [head];
362
- if (this._hasText(entry.description)) {
363
- // Indent every line of the description by 4 spaces.
364
- const indented = entry.description.trim().split('\n').map(l => ` ${l}`).join('\n');
365
- lines.push(indented);
366
- }
367
- if (entry.example !== undefined && entry.example !== null) {
368
- let rendered;
369
- try {
370
- rendered = (typeof entry.example === 'string')
371
- ? JSON.stringify(entry.example)
372
- : JSON.stringify(entry.example, null, 2);
373
- } catch {
374
- rendered = '<unstringifiable example>';
375
- }
376
- // Single-line examples stay inline; multi-line get their own block.
377
- if (rendered.includes('\n')) {
378
- const indented = rendered.split('\n').map(l => ` ${l}`).join('\n');
379
- lines.push(` Example:\n${indented}`);
380
- } else {
381
- lines.push(` Example: ${rendered}`);
382
- }
383
- }
384
- return lines.join('\n');
385
- }
386
-
387
- /**
388
- * Render a structured output value compactly for the system prompt.
389
- * Strings are quoted, primitives are JSON-ified, arrays/objects are
390
- * pretty-printed and truncated. Goal: the model can READ the value
391
- * without it dominating the prompt.
392
- */
393
- _formatStructuredValue(v) {
394
- if (v === null) return 'null';
395
- if (v === undefined) return 'undefined';
396
- if (typeof v === 'string') {
397
- // Single-line: quote inline. Multi-line: truncate-and-quote.
398
- if (!v.includes('\n') && v.length <= 200) return JSON.stringify(v);
399
- const truncated = v.length > 1500 ? v.slice(0, 1500) + '\n... (truncated)' : v;
400
- return `"""\n${truncated}\n"""`;
401
- }
402
- if (typeof v === 'number' || typeof v === 'boolean') return String(v);
403
- if (Array.isArray(v)) {
404
- if (v.length === 0) return '[]';
405
- if (v.every(x => typeof x === 'string') && v.length <= 10) {
406
- return `[${v.map(x => JSON.stringify(x)).join(', ')}]`;
407
- }
408
- const json = JSON.stringify(v, null, 2);
409
- return json.length > 1500 ? json.slice(0, 1500) + '\n... (truncated)' : json;
410
- }
411
- // Object
412
- try {
413
- const json = JSON.stringify(v, null, 2);
414
- return json.length > 1500 ? json.slice(0, 1500) + '\n... (truncated)' : json;
415
- } catch {
416
- return String(v);
417
- }
418
- }
419
-
420
- _formatPreviousOutput(output) {
421
- if (!output) return '';
422
-
423
- const outputStr = typeof output === 'object'
424
- ? JSON.stringify(output, null, 2)
425
- : String(output);
426
-
427
- // Truncate very long outputs but indicate there's more
428
- const maxLength = 2000;
429
- if (outputStr.length > maxLength) {
430
- return outputStr.substring(0, maxLength) + '\n... (truncated - full output was provided to you)';
431
- }
432
-
433
- return outputStr;
434
- }
435
-
436
- /**
437
- * Build minimal context for logging/debugging
438
- * @param {Object} flowMetadata - Flow metadata
439
- * @returns {Object} Simplified context object
440
- */
441
- buildContextSummary(flowMetadata, previousAgentData) {
442
- return {
443
- flowId: flowMetadata.flowId,
444
- flowName: flowMetadata.flowName,
445
- currentNode: flowMetadata.nodeName,
446
- position: `${flowMetadata.nodePosition}/${flowMetadata.totalNodes}`,
447
- hasPreviousAgent: !!previousAgentData,
448
- previousAgentId: previousAgentData?.agentId || null,
449
- previousFilesCount: previousAgentData?.filesCreated?.length || 0
450
- };
451
- }
452
-
453
- /**
454
- * Validate that job-done result has sufficient detail for flow handoff.
455
- *
456
- * @param {Object} jobDoneResult - The job-done tool result
457
- * @param {Object} [nodeContract] - v2 typed contract { outputs: [...] }.
458
- * When provided, ALSO validates that jobDoneResult.outputs contains
459
- * every declared output field with a type-correct value. The result
460
- * gains a `missingOutputs[]` field listing field names that are
461
- * absent or null.
462
- * @returns {Object} Validation result with warnings if insufficient
463
- */
464
- validateJobDoneForFlow(jobDoneResult, nodeContract) {
465
- const warnings = [];
466
-
467
- if (!jobDoneResult.summary || jobDoneResult.summary.length < 20) {
468
- warnings.push('Summary is too brief - next agent may lack necessary context');
469
- }
470
-
471
- if (!jobDoneResult.details && !jobDoneResult.summary) {
472
- warnings.push('No details provided - next agent will have minimal context');
473
- }
474
-
475
- // Check for file mentions without explicit paths
476
- const summaryText = (jobDoneResult.summary || '') + (jobDoneResult.details || '');
477
- const fileKeywords = ['created', 'wrote', 'saved', 'file', 'generated'];
478
- const hasFileMention = fileKeywords.some(kw => summaryText.toLowerCase().includes(kw));
479
- const hasExplicitPath = /[\/\\][\w-]+\.\w+/.test(summaryText);
480
-
481
- if (hasFileMention && !hasExplicitPath && (!jobDoneResult.filesCreated || jobDoneResult.filesCreated.length === 0)) {
482
- warnings.push('Files mentioned but no explicit paths provided - consider listing created files');
483
- }
484
-
485
- // v2: structured output validation against the node's declared contract.
486
- // We check presence + a basic type sanity on each declared field.
487
- // Type validation here is intentionally permissive (warn, don't reject
488
- // on type widenings) — the goal is to surface real gaps to the agent
489
- // so Phase 2 can re-prompt, not to reject borderline values.
490
- let missingOutputs;
491
- if (nodeContract && Array.isArray(nodeContract.outputs) && nodeContract.outputs.length > 0) {
492
- missingOutputs = [];
493
- const provided = (jobDoneResult.outputs && typeof jobDoneResult.outputs === 'object')
494
- ? jobDoneResult.outputs : {};
495
- for (const decl of nodeContract.outputs) {
496
- if (!decl || typeof decl.name !== 'string') continue;
497
- if (!(decl.name in provided) || provided[decl.name] === null || provided[decl.name] === undefined) {
498
- missingOutputs.push(decl.name);
499
- warnings.push(`Required output "${decl.name}" (${decl.type}) is missing from job-done.outputs`);
500
- continue;
501
- }
502
- const val = provided[decl.name];
503
- if (!_typeMatches(val, decl.type)) {
504
- warnings.push(`Output "${decl.name}" expected ${decl.type} but got ${_describeRuntimeType(val)}`);
505
- }
506
- }
507
- }
508
-
509
- const result = {
510
- valid: warnings.length === 0,
511
- warnings,
512
- suggestions: warnings.length > 0
513
- ? 'Ensure your job-done includes: detailed summary, explicit file paths, and any context the next agent needs.'
514
- : null
515
- };
516
- if (missingOutputs !== undefined) result.missingOutputs = missingOutputs;
517
- return result;
518
- }
519
-
520
- /**
521
- * Extract file paths from agent messages during flow execution
522
- * Used to auto-track files created for the next agent
523
- * @param {Array} messages - Agent conversation messages
524
- * @returns {Array<string>} Detected file paths
525
- */
526
- extractFilePaths(messages) {
527
- const filePaths = new Set();
528
-
529
- // Patterns for detecting file operations
530
- const patterns = [
531
- // Common path patterns
532
- /(?:created|wrote|saved|generated|modified)\s+(?:file\s+)?["']?([\/\\][\w\-\.\/\\]+\.\w+)["']?/gi,
533
- // Direct path mentions
534
- /(?:at|to|in)\s+["']?([\/\\][\w\-\.\/\\]+\.\w+)["']?/gi,
535
- // Tool result file paths (from file tools)
536
- /File\s+(?:created|written|saved):\s*([\/\\][\w\-\.\/\\]+\.\w+)/gi
537
- ];
538
-
539
- for (const message of messages) {
540
- const content = typeof message.content === 'string'
541
- ? message.content
542
- : JSON.stringify(message.content || '');
543
-
544
- for (const pattern of patterns) {
545
- let match;
546
- while ((match = pattern.exec(content)) !== null) {
547
- const path = match[1];
548
- // Filter out obvious non-file paths
549
- if (path && !path.includes('http') && path.length > 3) {
550
- filePaths.add(path);
551
- }
552
- }
553
- }
554
- }
555
-
556
- return Array.from(filePaths);
557
- }
558
- }
559
-
560
- /**
561
- * Runtime type check for declared output values. Permissive on purpose:
562
- * we want to surface clear mismatches (number expected, string given)
563
- * but allow widenings (json accepts anything; text accepts numbers
564
- * because they'll stringify cleanly downstream).
565
- */
566
- function _typeMatches(value, type) {
567
- if (value === null || value === undefined) return false;
568
- switch (type) {
569
- case 'text': return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
570
- case 'number': return typeof value === 'number' && Number.isFinite(value);
571
- case 'boolean': return typeof value === 'boolean';
572
- case 'json': return true;
573
- case 'file': return typeof value === 'string' && value.length > 0;
574
- case 'file[]': return Array.isArray(value) && value.every(v => typeof v === 'string' && v.length > 0);
575
- case 'list<text>': return Array.isArray(value) && value.every(v => typeof v === 'string');
576
- default: return true; // unknown type → don't gatekeep
577
- }
578
- }
579
-
580
- function _describeRuntimeType(value) {
581
- if (value === null) return 'null';
582
- if (Array.isArray(value)) return 'array';
583
- return typeof value;
584
- }
585
-
586
- export default FlowContextService;
1
+ /**
2
+ * FlowContextService - Builds flow-aware context for agents in flow execution
3
+ *
4
+ * Purpose:
5
+ * - Inject flow execution instructions into agent system prompts
6
+ * - Pass previous agent data (summary, files created, output) to next agent
7
+ * - Guide agents on comprehensive job-done completion
8
+ */
9
+
10
+ class FlowContextService {
11
+ constructor(config = {}, logger = null) {
12
+ this.config = config;
13
+ this.logger = logger;
14
+ }
15
+
16
+ /**
17
+ * Build flow execution context to inject into agent's system prompt
18
+ * @param {Object} flowMetadata - Current flow execution metadata
19
+ * @param {Object} previousAgentData - Data from previous agent (if any)
20
+ * @param {Object} [nodeContract] - v2 typed I/O { inputs, outputs }; when
21
+ * present, the system prompt advertises
22
+ * the EXACT named/typed payload the
23
+ * agent receives and must produce.
24
+ * @returns {string} Context string to inject into system prompt
25
+ */
26
+ buildFlowAgentContext(flowMetadata, previousAgentData, nodeContract) {
27
+ const sections = [];
28
+
29
+ // Flow execution header
30
+ sections.push(`
31
+ <FLOW_EXECUTION_CONTEXT>
32
+ You are executing step "${flowMetadata.nodeName || 'Agent'}" (${flowMetadata.nodePosition}/${flowMetadata.totalNodes}) in flow "${flowMetadata.flowName}".`);
33
+
34
+ // Phase 7: overall flow goal — orients each agent to the bigger
35
+ // picture so they reason about their step in context of the whole.
36
+ if (this._hasText(flowMetadata?.flowDescription)) {
37
+ sections.push(`
38
+ FLOW GOAL:
39
+ ${flowMetadata.flowDescription.trim()}`);
40
+ }
41
+
42
+ // Phase 7: per-node instructions — role, working style, and
43
+ // success criteria for THIS step. Distinct from the user prompt
44
+ // template (which is the input message). Lives at node.data.instructions.
45
+ if (this._hasText(nodeContract?.instructions)) {
46
+ sections.push(`
47
+ NODE INSTRUCTIONS (your role + how to succeed at this step):
48
+ ${nodeContract.instructions.trim()}`);
49
+ }
50
+
51
+ // Critical instruction about context isolation
52
+ sections.push(`
53
+ CRITICAL HANDOFF REQUIREMENT:
54
+ The NEXT agent in this flow has NO ACCESS to your conversation history or any context you've built up.
55
+ Everything you learn, create, or discover must be explicitly passed via job-done.`);
56
+
57
+ // v2: declared INPUTS — what payload the agent is receiving.
58
+ // Phase 7: each input now optionally carries a description and an
59
+ // example. The description tells the agent what this input
60
+ // represents AND how to use it; the example shows concrete shape.
61
+ if (nodeContract && Array.isArray(nodeContract.inputs) && nodeContract.inputs.length > 0) {
62
+ const lines = nodeContract.inputs.map(i => this._renderIOEntry(i, /*required default*/ true));
63
+ sections.push(`
64
+ INPUTS (this step receives these typed values; reference them in your reasoning):
65
+ ${lines.join('\n')}`);
66
+ }
67
+
68
+ // v2: declared OUTPUTS — exactly what the agent must produce.
69
+ // Phase 7 hardening: stronger framing + concrete example shape that
70
+ // the model can mimic literally. Empirical finding: weak instruction
71
+ // ("you MUST include outputs") + abstract placeholder (`<text value>`)
72
+ // → models default to schema-minimum. Loud framing + concrete shape
73
+ // dramatically improves first-pass compliance.
74
+ if (nodeContract && Array.isArray(nodeContract.outputs) && nodeContract.outputs.length > 0) {
75
+ const lines = nodeContract.outputs.map(o => this._renderIOEntry(o, /*required default*/ false));
76
+ // Build a concrete example object using each field's example if
77
+ // provided, else a typed placeholder. The agent should imitate
78
+ // this shape literally — only the values change.
79
+ const exampleEntries = nodeContract.outputs.map(o => {
80
+ let exVal;
81
+ if (o.example !== undefined && o.example !== null) {
82
+ try { exVal = JSON.stringify(o.example); }
83
+ catch { exVal = this._typedPlaceholder(o.type); }
84
+ } else {
85
+ exVal = this._typedPlaceholder(o.type);
86
+ }
87
+ return ` "${o.name}": ${exVal}`;
88
+ }).join(',\n');
89
+
90
+ sections.push(`
91
+ ═══════════════════════════════════════════════════════════════
92
+ REQUIRED OUTPUTS — your job-done call MUST populate every field below
93
+ ═══════════════════════════════════════════════════════════════
94
+
95
+ ${lines.join('\n')}
96
+
97
+ THE ONLY VALID FORMAT for ending this task is a jobdone tool call
98
+ with this exact shape (replace example values with your real content,
99
+ keep field names and structure identical):
100
+
101
+ {
102
+ "toolId": "jobdone",
103
+ "actions": [{
104
+ "action": "complete",
105
+ "summary": "<your one-paragraph summary>",
106
+ "outputs": {
107
+ ${exampleEntries}
108
+ }
109
+ }]
110
+ }
111
+
112
+ Calls without the "outputs" field — or with "outputs" missing any of
113
+ the field names listed above — will be REJECTED and you will be
114
+ re-prompted. The flow CANNOT proceed without these structured outputs.
115
+ ═══════════════════════════════════════════════════════════════`);
116
+ }
117
+
118
+ // Job-done instructions
119
+ sections.push(`
120
+ When you complete your task, you MUST call the job-done tool with comprehensive details:
121
+ - summary: A complete summary of what you accomplished (not just "task complete")
122
+ - details: ALL information the next agent needs to continue, including:
123
+ • Full results, findings, or output content
124
+ • File paths of ANY files created or modified (list each explicitly)
125
+ • Important decisions made and their rationale
126
+ • Any warnings, caveats, or considerations for the next step
127
+ • Context that would be lost without explicit documentation`);
128
+
129
+ // Previous agent context (if exists)
130
+ if (previousAgentData) {
131
+ sections.push(`
132
+ CONTEXT FROM PREVIOUS AGENT:
133
+ ${previousAgentData.agentName ? `- Previous agent: ${previousAgentData.agentName} (${previousAgentData.agentId})` : `- Previous agent ID: ${previousAgentData.agentId}`}
134
+ ${previousAgentData.summary ? `- Their summary: ${previousAgentData.summary}` : ''}
135
+ ${previousAgentData.filesCreated?.length > 0 ? `- Files they created/modified:\n${previousAgentData.filesCreated.map(f => ` • ${f}`).join('\n')}` : '- No files created by previous agent'}
136
+ ${previousAgentData.output ? `
137
+ - Their output:
138
+ ${this._formatPreviousOutput(previousAgentData.output)}` : ''}`);
139
+
140
+ // v2: render the structured outputs bag if upstream agents
141
+ // produced one. THIS is the handoff payload — agents downstream
142
+ // should reason from these named fields, not from the prose
143
+ // summary. Without this section the typed contract is invisible
144
+ // at runtime and agents fabricate the data.
145
+ if (previousAgentData.outputs && typeof previousAgentData.outputs === 'object'
146
+ && Object.keys(previousAgentData.outputs).length > 0) {
147
+ const lines = Object.entries(previousAgentData.outputs).map(([k, v]) =>
148
+ ` • ${k} = ${this._formatStructuredValue(v)}`
149
+ );
150
+ sections.push(`
151
+ STRUCTURED HANDOFF FROM UPSTREAM (use these as the source of truth — not the summary):
152
+ ${lines.join('\n')}`);
153
+ }
154
+
155
+ // If multiple agents fed this node, list contributors per-agent
156
+ // so the model knows who produced what. Helpful for fan-in nodes.
157
+ if (Array.isArray(previousAgentData.contributors) && previousAgentData.contributors.length > 1) {
158
+ const blocks = previousAgentData.contributors.map(c => {
159
+ const outs = c.outputs && Object.keys(c.outputs).length > 0
160
+ ? Object.entries(c.outputs).map(([k, v]) => ` ${k} = ${this._formatStructuredValue(v)}`).join('\n')
161
+ : ' (no structured outputs)';
162
+ return ` - ${c.agentName} (${c.agentId}):\n${outs}`;
163
+ });
164
+ sections.push(`
165
+ ALL UPSTREAM CONTRIBUTORS (this node has multiple inputs):
166
+ ${blocks.join('\n')}`);
167
+ }
168
+ } else {
169
+ sections.push(`
170
+ This is the FIRST agent in the flow - you are receiving the initial user input.`);
171
+ }
172
+
173
+ // Closing instructions
174
+ sections.push(`
175
+ FLOW EXECUTION RULES:
176
+ 1. Focus on your specific task in this pipeline step
177
+ 2. Be thorough in your job-done summary - err on the side of more detail
178
+ 3. List ALL file operations explicitly (create, modify, delete)
179
+ 4. Stay in agent mode after job-done in case there are follow-up questions
180
+ 5. Do not assume the next agent knows anything about what you did
181
+
182
+ </FLOW_EXECUTION_CONTEXT>`);
183
+
184
+ return sections.join('');
185
+ }
186
+
187
+ /**
188
+ * Format previous agent's output for display (truncate if very long)
189
+ * @param {string|Object} output - Previous output
190
+ * @returns {string} Formatted output
191
+ */
192
+ /**
193
+ * Phase 8 (the big one): build a STANDALONE system prompt that REPLACES
194
+ * the agent's persisted system prompt for the duration of a flow step.
195
+ *
196
+ * Why: production findings showed the agent's own system prompt
197
+ * ("You are a software developer...") fights the flow contract.
198
+ * The agent's training pulls it toward its native role
199
+ * (engineering, code, task lists) instead of producing the typed
200
+ * outputs the flow declares. Appending flow context didn't beat the
201
+ * prior identity.
202
+ *
203
+ * This builder produces a single self-contained prompt that defines
204
+ * the agent's IDENTITY for this step in terms of the flow node's
205
+ * declared role + I/O contract. The agent's tools/capabilities/model
206
+ * stay the same — only the role-defining prose changes.
207
+ *
208
+ * Returns null when there's no node-level role to assert (no
209
+ * instructions AND no typed I/O). In that case the caller falls back
210
+ * to the legacy append-context behavior.
211
+ *
212
+ * @returns {string|null} the new system prompt, or null to keep native
213
+ */
214
+ buildFlowAgentSystemPrompt(flowMetadata, previousAgentData, nodeContract) {
215
+ if (!nodeContract) return null;
216
+ const hasInstructions = this._hasText(nodeContract.instructions);
217
+ const hasOutputs = Array.isArray(nodeContract.outputs) && nodeContract.outputs.length > 0;
218
+ if (!hasInstructions && !hasOutputs) return null;
219
+
220
+ const sections = [];
221
+ sections.push(
222
+ `You are acting as the "${flowMetadata.nodeName || 'Agent'}" step (${flowMetadata.nodePosition}/${flowMetadata.totalNodes}) of the flow "${flowMetadata.flowName}".`
223
+ );
224
+
225
+ if (this._hasText(flowMetadata?.flowDescription)) {
226
+ sections.push(`OVERALL FLOW GOAL:\n${flowMetadata.flowDescription.trim()}`);
227
+ }
228
+
229
+ if (hasInstructions) {
230
+ sections.push(`YOUR ROLE FOR THIS STEP:\n${nodeContract.instructions.trim()}`);
231
+ }
232
+
233
+ sections.push(
234
+ `HOW TO COMPLETE THIS STEP — the only valid completion:
235
+ 1. Read the typed inputs you receive (listed below).
236
+ 2. Produce the typed outputs declared below.
237
+ 3. Call the job-done tool with summary, details, AND a populated
238
+ "outputs" object containing every required field.
239
+
240
+ DO NOT:
241
+ - Maintain or update task lists. This step has no other steps.
242
+ - Write status paragraphs. The flow is autonomous.
243
+ - Call any tool other than job-done unless it is genuinely required
244
+ to gather data for the structured outputs.
245
+ - Defer the work. There is no human in this conversation — produce
246
+ the outputs and finish.
247
+
248
+ The flow CANNOT proceed until you call job-done with the correct
249
+ "outputs" object. Calls that omit any required field will be rejected
250
+ and you will be asked to re-emit.`
251
+ );
252
+
253
+ // INPUTS — typed, with descriptions/examples
254
+ if (Array.isArray(nodeContract.inputs) && nodeContract.inputs.length > 0) {
255
+ const lines = nodeContract.inputs.map(i => this._renderIOEntry(i, true));
256
+ sections.push(`INPUTS (you receive these typed values from upstream):\n${lines.join('\n')}`);
257
+ }
258
+
259
+ // OUTPUTS — typed, with descriptions/examples + concrete shape
260
+ if (hasOutputs) {
261
+ const lines = nodeContract.outputs.map(o => this._renderIOEntry(o, false));
262
+ const exampleEntries = nodeContract.outputs.map(o => {
263
+ let exVal;
264
+ if (o.example !== undefined && o.example !== null) {
265
+ try { exVal = JSON.stringify(o.example); }
266
+ catch { exVal = this._typedPlaceholder(o.type); }
267
+ } else {
268
+ exVal = this._typedPlaceholder(o.type);
269
+ }
270
+ return ` "${o.name}": ${exVal}`;
271
+ }).join(',\n');
272
+
273
+ sections.push(
274
+ `REQUIRED OUTPUTS — you MUST populate every field below in job-done's "outputs":
275
+ ${lines.join('\n')}
276
+
277
+ EXACT job-done call shape (replace example values; keep keys identical):
278
+ {
279
+ "toolId": "jobdone",
280
+ "actions": [{
281
+ "action": "complete",
282
+ "summary": "<one-paragraph summary of what you produced>",
283
+ "outputs": {
284
+ ${exampleEntries}
285
+ }
286
+ }]
287
+ }`
288
+ );
289
+ }
290
+
291
+ // Previous agent context — same as in append mode but inside the
292
+ // standalone prompt now.
293
+ if (previousAgentData) {
294
+ const parts = [`Previous step: ${previousAgentData.agentName || previousAgentData.agentId}.`];
295
+ if (previousAgentData.summary) parts.push(`Their summary: ${previousAgentData.summary}`);
296
+ if (previousAgentData.outputs && Object.keys(previousAgentData.outputs).length > 0) {
297
+ const fields = Object.entries(previousAgentData.outputs)
298
+ .map(([k, v]) => ` • ${k} = ${this._formatStructuredValue(v)}`)
299
+ .join('\n');
300
+ parts.push(`Their structured outputs (use as your source of truth):\n${fields}`);
301
+ } else if (previousAgentData.output) {
302
+ parts.push(`Their output:\n${this._formatPreviousOutput(previousAgentData.output)}`);
303
+ }
304
+ sections.push(`UPSTREAM CONTEXT:\n${parts.join('\n')}`);
305
+ } else {
306
+ sections.push(`This is the FIRST step. You receive the user's initial input.`);
307
+ }
308
+
309
+ return sections.join('\n\n');
310
+ }
311
+
312
+ /**
313
+ * Phase 7 hardening helper: typed placeholder rendering for the
314
+ * concrete example block in REQUIRED OUTPUTS. Used only when the
315
+ * field has no `example` declared. Each placeholder is itself valid
316
+ * JSON of the right type so the agent can copy-paste the structure.
317
+ */
318
+ _typedPlaceholder(type) {
319
+ switch (type) {
320
+ case 'text': return '"<your text content here>"';
321
+ case 'number': return '0';
322
+ case 'boolean': return 'true';
323
+ case 'json': return '{ "key": "value" }';
324
+ case 'file': return '"/path/to/file.ext"';
325
+ case 'file[]': return '["/path/to/file1", "/path/to/file2"]';
326
+ case 'list<text>': return '["item 1", "item 2", "item 3"]';
327
+ default: return '"<value>"';
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Phase 7 helper: true when a string is present and non-empty after
333
+ * trimming. Empty/whitespace-only descriptions are treated as absent.
334
+ */
335
+ _hasText(s) {
336
+ return typeof s === 'string' && s.trim().length > 0;
337
+ }
338
+
339
+ /**
340
+ * Phase 7 helper: render one input or output declaration as a multi-
341
+ * line block:
342
+ *
343
+ * • topic (text, required)
344
+ * The research topic exactly as provided.
345
+ * Example: "AI safety"
346
+ *
347
+ * Description and example are both optional. Required-marker policy
348
+ * differs slightly between inputs (default required:true) and outputs
349
+ * (no inherent required flag) — caller passes `requiredByDefault`.
350
+ *
351
+ * The same description text is read by BOTH the producer (interprets
352
+ * as "format thus") and the consumer (interprets as "expect thus").
353
+ * That bidirectional contract is the whole point.
354
+ */
355
+ _renderIOEntry(entry, requiredByDefault) {
356
+ if (!entry || typeof entry.name !== 'string') return '';
357
+ const reqLabel = (entry.required === true) ? ', required'
358
+ : (entry.required === false) ? ', optional'
359
+ : (requiredByDefault ? ', required' : '');
360
+ const head = ` • ${entry.name} (${entry.type}${reqLabel})`;
361
+ const lines = [head];
362
+ if (this._hasText(entry.description)) {
363
+ // Indent every line of the description by 4 spaces.
364
+ const indented = entry.description.trim().split('\n').map(l => ` ${l}`).join('\n');
365
+ lines.push(indented);
366
+ }
367
+ if (entry.example !== undefined && entry.example !== null) {
368
+ let rendered;
369
+ try {
370
+ rendered = (typeof entry.example === 'string')
371
+ ? JSON.stringify(entry.example)
372
+ : JSON.stringify(entry.example, null, 2);
373
+ } catch {
374
+ rendered = '<unstringifiable example>';
375
+ }
376
+ // Single-line examples stay inline; multi-line get their own block.
377
+ if (rendered.includes('\n')) {
378
+ const indented = rendered.split('\n').map(l => ` ${l}`).join('\n');
379
+ lines.push(` Example:\n${indented}`);
380
+ } else {
381
+ lines.push(` Example: ${rendered}`);
382
+ }
383
+ }
384
+ return lines.join('\n');
385
+ }
386
+
387
+ /**
388
+ * Render a structured output value compactly for the system prompt.
389
+ * Strings are quoted, primitives are JSON-ified, arrays/objects are
390
+ * pretty-printed and truncated. Goal: the model can READ the value
391
+ * without it dominating the prompt.
392
+ */
393
+ _formatStructuredValue(v) {
394
+ if (v === null) return 'null';
395
+ if (v === undefined) return 'undefined';
396
+ if (typeof v === 'string') {
397
+ // Single-line: quote inline. Multi-line: truncate-and-quote.
398
+ if (!v.includes('\n') && v.length <= 200) return JSON.stringify(v);
399
+ const truncated = v.length > 1500 ? v.slice(0, 1500) + '\n... (truncated)' : v;
400
+ return `"""\n${truncated}\n"""`;
401
+ }
402
+ if (typeof v === 'number' || typeof v === 'boolean') return String(v);
403
+ if (Array.isArray(v)) {
404
+ if (v.length === 0) return '[]';
405
+ if (v.every(x => typeof x === 'string') && v.length <= 10) {
406
+ return `[${v.map(x => JSON.stringify(x)).join(', ')}]`;
407
+ }
408
+ const json = JSON.stringify(v, null, 2);
409
+ return json.length > 1500 ? json.slice(0, 1500) + '\n... (truncated)' : json;
410
+ }
411
+ // Object
412
+ try {
413
+ const json = JSON.stringify(v, null, 2);
414
+ return json.length > 1500 ? json.slice(0, 1500) + '\n... (truncated)' : json;
415
+ } catch {
416
+ return String(v);
417
+ }
418
+ }
419
+
420
+ _formatPreviousOutput(output) {
421
+ if (!output) return '';
422
+
423
+ const outputStr = typeof output === 'object'
424
+ ? JSON.stringify(output, null, 2)
425
+ : String(output);
426
+
427
+ // Truncate very long outputs but indicate there's more
428
+ const maxLength = 2000;
429
+ if (outputStr.length > maxLength) {
430
+ return outputStr.substring(0, maxLength) + '\n... (truncated - full output was provided to you)';
431
+ }
432
+
433
+ return outputStr;
434
+ }
435
+
436
+ /**
437
+ * Build minimal context for logging/debugging
438
+ * @param {Object} flowMetadata - Flow metadata
439
+ * @returns {Object} Simplified context object
440
+ */
441
+ buildContextSummary(flowMetadata, previousAgentData) {
442
+ return {
443
+ flowId: flowMetadata.flowId,
444
+ flowName: flowMetadata.flowName,
445
+ currentNode: flowMetadata.nodeName,
446
+ position: `${flowMetadata.nodePosition}/${flowMetadata.totalNodes}`,
447
+ hasPreviousAgent: !!previousAgentData,
448
+ previousAgentId: previousAgentData?.agentId || null,
449
+ previousFilesCount: previousAgentData?.filesCreated?.length || 0
450
+ };
451
+ }
452
+
453
+ /**
454
+ * Validate that job-done result has sufficient detail for flow handoff.
455
+ *
456
+ * @param {Object} jobDoneResult - The job-done tool result
457
+ * @param {Object} [nodeContract] - v2 typed contract { outputs: [...] }.
458
+ * When provided, ALSO validates that jobDoneResult.outputs contains
459
+ * every declared output field with a type-correct value. The result
460
+ * gains a `missingOutputs[]` field listing field names that are
461
+ * absent or null.
462
+ * @returns {Object} Validation result with warnings if insufficient
463
+ */
464
+ validateJobDoneForFlow(jobDoneResult, nodeContract) {
465
+ const warnings = [];
466
+
467
+ if (!jobDoneResult.summary || jobDoneResult.summary.length < 20) {
468
+ warnings.push('Summary is too brief - next agent may lack necessary context');
469
+ }
470
+
471
+ if (!jobDoneResult.details && !jobDoneResult.summary) {
472
+ warnings.push('No details provided - next agent will have minimal context');
473
+ }
474
+
475
+ // Check for file mentions without explicit paths
476
+ const summaryText = (jobDoneResult.summary || '') + (jobDoneResult.details || '');
477
+ const fileKeywords = ['created', 'wrote', 'saved', 'file', 'generated'];
478
+ const hasFileMention = fileKeywords.some(kw => summaryText.toLowerCase().includes(kw));
479
+ const hasExplicitPath = /[/\\][\w-]+\.\w+/.test(summaryText);
480
+
481
+ if (hasFileMention && !hasExplicitPath && (!jobDoneResult.filesCreated || jobDoneResult.filesCreated.length === 0)) {
482
+ warnings.push('Files mentioned but no explicit paths provided - consider listing created files');
483
+ }
484
+
485
+ // v2: structured output validation against the node's declared contract.
486
+ // We check presence + a basic type sanity on each declared field.
487
+ // Type validation here is intentionally permissive (warn, don't reject
488
+ // on type widenings) — the goal is to surface real gaps to the agent
489
+ // so Phase 2 can re-prompt, not to reject borderline values.
490
+ let missingOutputs;
491
+ if (nodeContract && Array.isArray(nodeContract.outputs) && nodeContract.outputs.length > 0) {
492
+ missingOutputs = [];
493
+ const provided = (jobDoneResult.outputs && typeof jobDoneResult.outputs === 'object')
494
+ ? jobDoneResult.outputs : {};
495
+ for (const decl of nodeContract.outputs) {
496
+ if (!decl || typeof decl.name !== 'string') continue;
497
+ if (!(decl.name in provided) || provided[decl.name] === null || provided[decl.name] === undefined) {
498
+ missingOutputs.push(decl.name);
499
+ warnings.push(`Required output "${decl.name}" (${decl.type}) is missing from job-done.outputs`);
500
+ continue;
501
+ }
502
+ const val = provided[decl.name];
503
+ if (!_typeMatches(val, decl.type)) {
504
+ warnings.push(`Output "${decl.name}" expected ${decl.type} but got ${_describeRuntimeType(val)}`);
505
+ }
506
+ }
507
+ }
508
+
509
+ const result = {
510
+ valid: warnings.length === 0,
511
+ warnings,
512
+ suggestions: warnings.length > 0
513
+ ? 'Ensure your job-done includes: detailed summary, explicit file paths, and any context the next agent needs.'
514
+ : null
515
+ };
516
+ if (missingOutputs !== undefined) result.missingOutputs = missingOutputs;
517
+ return result;
518
+ }
519
+
520
+ /**
521
+ * Extract file paths from agent messages during flow execution
522
+ * Used to auto-track files created for the next agent
523
+ * @param {Array} messages - Agent conversation messages
524
+ * @returns {Array<string>} Detected file paths
525
+ */
526
+ extractFilePaths(messages) {
527
+ const filePaths = new Set();
528
+
529
+ // Patterns for detecting file operations
530
+ const patterns = [
531
+ // Common path patterns
532
+ /(?:created|wrote|saved|generated|modified)\s+(?:file\s+)?["']?([/\\][\w\-./\\]+\.\w+)["']?/gi,
533
+ // Direct path mentions
534
+ /(?:at|to|in)\s+["']?([/\\][\w\-./\\]+\.\w+)["']?/gi,
535
+ // Tool result file paths (from file tools)
536
+ /File\s+(?:created|written|saved):\s*([/\\][\w\-./\\]+\.\w+)/gi
537
+ ];
538
+
539
+ for (const message of messages) {
540
+ const content = typeof message.content === 'string'
541
+ ? message.content
542
+ : JSON.stringify(message.content || '');
543
+
544
+ for (const pattern of patterns) {
545
+ let match;
546
+ while ((match = pattern.exec(content)) !== null) {
547
+ const path = match[1];
548
+ // Filter out obvious non-file paths
549
+ if (path && !path.includes('http') && path.length > 3) {
550
+ filePaths.add(path);
551
+ }
552
+ }
553
+ }
554
+ }
555
+
556
+ return Array.from(filePaths);
557
+ }
558
+ }
559
+
560
+ /**
561
+ * Runtime type check for declared output values. Permissive on purpose:
562
+ * we want to surface clear mismatches (number expected, string given)
563
+ * but allow widenings (json accepts anything; text accepts numbers
564
+ * because they'll stringify cleanly downstream).
565
+ */
566
+ function _typeMatches(value, type) {
567
+ if (value === null || value === undefined) return false;
568
+ switch (type) {
569
+ case 'text': return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
570
+ case 'number': return typeof value === 'number' && Number.isFinite(value);
571
+ case 'boolean': return typeof value === 'boolean';
572
+ case 'json': return true;
573
+ case 'file': return typeof value === 'string' && value.length > 0;
574
+ case 'file[]': return Array.isArray(value) && value.every(v => typeof v === 'string' && v.length > 0);
575
+ case 'list<text>': return Array.isArray(value) && value.every(v => typeof v === 'string');
576
+ default: return true; // unknown type → don't gatekeep
577
+ }
578
+ }
579
+
580
+ function _describeRuntimeType(value) {
581
+ if (value === null) return 'null';
582
+ if (Array.isArray(value)) return 'array';
583
+ return typeof value;
584
+ }
585
+
586
+ export default FlowContextService;