@zerowidth/workbench-sdk 2.0.0-alpha.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 (1266) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +799 -0
  3. package/nodes/absolute/absolute.config.json +24 -0
  4. package/nodes/absolute/absolute.process.js +7 -0
  5. package/nodes/absolute/absolute.tests.json +38 -0
  6. package/nodes/absolute-value/absolute-value.config.json +24 -0
  7. package/nodes/absolute-value/absolute-value.process.js +5 -0
  8. package/nodes/absolute-value/absolute-value.tests.json +17 -0
  9. package/nodes/add/add.config.json +31 -0
  10. package/nodes/add/add.process.js +9 -0
  11. package/nodes/add/add.tests.json +41 -0
  12. package/nodes/airtable-create-record/airtable-create-record.config.json +64 -0
  13. package/nodes/airtable-create-record/airtable-create-record.process.js +44 -0
  14. package/nodes/airtable-create-record/airtable-create-record.tests.json +50 -0
  15. package/nodes/airtable-delete-record/airtable-delete-record.config.json +44 -0
  16. package/nodes/airtable-delete-record/airtable-delete-record.process.js +34 -0
  17. package/nodes/airtable-delete-record/airtable-delete-record.tests.json +23 -0
  18. package/nodes/airtable-get-record/airtable-get-record.config.json +56 -0
  19. package/nodes/airtable-get-record/airtable-get-record.process.js +36 -0
  20. package/nodes/airtable-get-record/airtable-get-record.tests.json +25 -0
  21. package/nodes/airtable-list-records/airtable-list-records.config.json +87 -0
  22. package/nodes/airtable-list-records/airtable-list-records.process.js +52 -0
  23. package/nodes/airtable-list-records/airtable-list-records.tests.json +46 -0
  24. package/nodes/airtable-update-record/airtable-update-record.config.json +71 -0
  25. package/nodes/airtable-update-record/airtable-update-record.process.js +48 -0
  26. package/nodes/airtable-update-record/airtable-update-record.tests.json +44 -0
  27. package/nodes/and-gate/and-gate.config.json +30 -0
  28. package/nodes/and-gate/and-gate.process.js +10 -0
  29. package/nodes/and-gate/and-gate.tests.json +22 -0
  30. package/nodes/anthropic-claude-3-5-haiku/anthropic-claude-3-5-haiku.config.json +155 -0
  31. package/nodes/anthropic-claude-3-5-haiku/anthropic-claude-3-5-haiku.process.js +136 -0
  32. package/nodes/anthropic-claude-3-5-haiku/anthropic-claude-3-5-haiku.tests.json +80 -0
  33. package/nodes/anthropic-claude-3-haiku/anthropic-claude-3-haiku.config.json +155 -0
  34. package/nodes/anthropic-claude-3-haiku/anthropic-claude-3-haiku.process.js +136 -0
  35. package/nodes/anthropic-claude-3-haiku/anthropic-claude-3-haiku.tests.json +80 -0
  36. package/nodes/anthropic-claude-haiku-4-5/anthropic-claude-haiku-4-5.config.json +186 -0
  37. package/nodes/anthropic-claude-haiku-4-5/anthropic-claude-haiku-4-5.process.js +138 -0
  38. package/nodes/anthropic-claude-haiku-4-5/anthropic-claude-haiku-4-5.tests.json +80 -0
  39. package/nodes/anthropic-claude-opus-4/anthropic-claude-opus-4.config.json +176 -0
  40. package/nodes/anthropic-claude-opus-4/anthropic-claude-opus-4.process.js +138 -0
  41. package/nodes/anthropic-claude-opus-4/anthropic-claude-opus-4.tests.json +80 -0
  42. package/nodes/anthropic-claude-opus-4-1/anthropic-claude-opus-4-1.config.json +186 -0
  43. package/nodes/anthropic-claude-opus-4-1/anthropic-claude-opus-4-1.process.js +138 -0
  44. package/nodes/anthropic-claude-opus-4-1/anthropic-claude-opus-4-1.tests.json +80 -0
  45. package/nodes/anthropic-claude-opus-4-5/anthropic-claude-opus-4-5.config.json +179 -0
  46. package/nodes/anthropic-claude-opus-4-5/anthropic-claude-opus-4-5.process.js +138 -0
  47. package/nodes/anthropic-claude-opus-4-5/anthropic-claude-opus-4-5.tests.json +80 -0
  48. package/nodes/anthropic-claude-opus-4-6/anthropic-claude-opus-4-6.config.json +188 -0
  49. package/nodes/anthropic-claude-opus-4-6/anthropic-claude-opus-4-6.process.js +138 -0
  50. package/nodes/anthropic-claude-opus-4-6/anthropic-claude-opus-4-6.tests.json +80 -0
  51. package/nodes/anthropic-claude-opus-4-6-fast/anthropic-claude-opus-4-6-fast.config.json +186 -0
  52. package/nodes/anthropic-claude-opus-4-6-fast/anthropic-claude-opus-4-6-fast.process.js +138 -0
  53. package/nodes/anthropic-claude-opus-4-6-fast/anthropic-claude-opus-4-6-fast.tests.json +80 -0
  54. package/nodes/anthropic-claude-opus-4-7/anthropic-claude-opus-4-7.config.json +170 -0
  55. package/nodes/anthropic-claude-opus-4-7/anthropic-claude-opus-4-7.process.js +138 -0
  56. package/nodes/anthropic-claude-opus-4-7/anthropic-claude-opus-4-7.tests.json +57 -0
  57. package/nodes/anthropic-claude-opus-4-7-fast/anthropic-claude-opus-4-7-fast.config.json +170 -0
  58. package/nodes/anthropic-claude-opus-4-7-fast/anthropic-claude-opus-4-7-fast.process.js +138 -0
  59. package/nodes/anthropic-claude-opus-4-7-fast/anthropic-claude-opus-4-7-fast.tests.json +57 -0
  60. package/nodes/anthropic-claude-opus-4-8/anthropic-claude-opus-4-8.config.json +170 -0
  61. package/nodes/anthropic-claude-opus-4-8/anthropic-claude-opus-4-8.process.js +138 -0
  62. package/nodes/anthropic-claude-opus-4-8/anthropic-claude-opus-4-8.tests.json +57 -0
  63. package/nodes/anthropic-claude-opus-4-8-fast/anthropic-claude-opus-4-8-fast.config.json +170 -0
  64. package/nodes/anthropic-claude-opus-4-8-fast/anthropic-claude-opus-4-8-fast.process.js +138 -0
  65. package/nodes/anthropic-claude-opus-4-8-fast/anthropic-claude-opus-4-8-fast.tests.json +57 -0
  66. package/nodes/anthropic-claude-sonnet-4/anthropic-claude-sonnet-4.config.json +177 -0
  67. package/nodes/anthropic-claude-sonnet-4/anthropic-claude-sonnet-4.process.js +138 -0
  68. package/nodes/anthropic-claude-sonnet-4/anthropic-claude-sonnet-4.tests.json +80 -0
  69. package/nodes/anthropic-claude-sonnet-4-5/anthropic-claude-sonnet-4-5.config.json +186 -0
  70. package/nodes/anthropic-claude-sonnet-4-5/anthropic-claude-sonnet-4-5.process.js +138 -0
  71. package/nodes/anthropic-claude-sonnet-4-5/anthropic-claude-sonnet-4-5.tests.json +80 -0
  72. package/nodes/anthropic-claude-sonnet-4-6/anthropic-claude-sonnet-4-6.config.json +188 -0
  73. package/nodes/anthropic-claude-sonnet-4-6/anthropic-claude-sonnet-4-6.process.js +138 -0
  74. package/nodes/anthropic-claude-sonnet-4-6/anthropic-claude-sonnet-4-6.tests.json +80 -0
  75. package/nodes/array-builder/array-builder.config.json +27 -0
  76. package/nodes/array-builder/array-builder.process.js +12 -0
  77. package/nodes/array-builder/array-builder.tests.json +20 -0
  78. package/nodes/array-filter/array-filter.config.json +47 -0
  79. package/nodes/array-filter/array-filter.process.js +58 -0
  80. package/nodes/array-filter/array-filter.tests.json +67 -0
  81. package/nodes/array-find/array-find.config.json +66 -0
  82. package/nodes/array-find/array-find.process.js +61 -0
  83. package/nodes/array-find/array-find.tests.json +83 -0
  84. package/nodes/array-flatten/array-flatten.config.json +39 -0
  85. package/nodes/array-flatten/array-flatten.process.js +67 -0
  86. package/nodes/array-flatten/array-flatten.tests.json +79 -0
  87. package/nodes/array-index-selector/array-index-selector.config.json +36 -0
  88. package/nodes/array-index-selector/array-index-selector.process.js +20 -0
  89. package/nodes/array-index-selector/array-index-selector.tests.json +37 -0
  90. package/nodes/array-join/array-join.config.json +30 -0
  91. package/nodes/array-join/array-join.process.js +9 -0
  92. package/nodes/array-join/array-join.tests.json +41 -0
  93. package/nodes/array-length/array-length.config.json +28 -0
  94. package/nodes/array-length/array-length.process.js +14 -0
  95. package/nodes/array-length/array-length.tests.json +32 -0
  96. package/nodes/array-map/array-map.config.json +30 -0
  97. package/nodes/array-map/array-map.process.js +16 -0
  98. package/nodes/array-map/array-map.tests.json +44 -0
  99. package/nodes/array-reverse/array-reverse.config.json +29 -0
  100. package/nodes/array-reverse/array-reverse.process.js +12 -0
  101. package/nodes/array-reverse/array-reverse.tests.json +60 -0
  102. package/nodes/array-shuffle/array-shuffle.config.json +38 -0
  103. package/nodes/array-shuffle/array-shuffle.process.js +37 -0
  104. package/nodes/array-shuffle/array-shuffle.tests.json +82 -0
  105. package/nodes/array-slice/array-slice.config.json +37 -0
  106. package/nodes/array-slice/array-slice.process.js +18 -0
  107. package/nodes/array-slice/array-slice.tests.json +43 -0
  108. package/nodes/array-sort/array-sort.config.json +55 -0
  109. package/nodes/array-sort/array-sort.process.js +45 -0
  110. package/nodes/array-sort/array-sort.tests.json +95 -0
  111. package/nodes/array-split/array-split.config.json +50 -0
  112. package/nodes/array-split/array-split.process.js +32 -0
  113. package/nodes/array-split/array-split.tests.json +77 -0
  114. package/nodes/array-unique/array-unique.config.json +65 -0
  115. package/nodes/array-unique/array-unique.process.js +55 -0
  116. package/nodes/array-unique/array-unique.tests.json +96 -0
  117. package/nodes/baai-bge-base-en-v1-5/baai-bge-base-en-v1-5.config.json +90 -0
  118. package/nodes/baai-bge-base-en-v1-5/baai-bge-base-en-v1-5.process.js +36 -0
  119. package/nodes/baai-bge-base-en-v1-5/baai-bge-base-en-v1-5.tests.json +48 -0
  120. package/nodes/baai-bge-large-en-v1-5/baai-bge-large-en-v1-5.config.json +90 -0
  121. package/nodes/baai-bge-large-en-v1-5/baai-bge-large-en-v1-5.process.js +36 -0
  122. package/nodes/baai-bge-large-en-v1-5/baai-bge-large-en-v1-5.tests.json +48 -0
  123. package/nodes/baai-bge-m3/baai-bge-m3.config.json +90 -0
  124. package/nodes/baai-bge-m3/baai-bge-m3.process.js +36 -0
  125. package/nodes/baai-bge-m3/baai-bge-m3.tests.json +48 -0
  126. package/nodes/base64-decode/base64-decode.config.json +36 -0
  127. package/nodes/base64-decode/base64-decode.process.js +18 -0
  128. package/nodes/base64-decode/base64-decode.tests.json +46 -0
  129. package/nodes/base64-encode/base64-encode.config.json +24 -0
  130. package/nodes/base64-encode/base64-encode.process.js +11 -0
  131. package/nodes/base64-encode/base64-encode.tests.json +38 -0
  132. package/nodes/boolean/boolean.config.json +37 -0
  133. package/nodes/boolean/boolean.process.js +9 -0
  134. package/nodes/boolean/boolean.tests.json +47 -0
  135. package/nodes/boolean-inverter/boolean-inverter.config.json +37 -0
  136. package/nodes/boolean-inverter/boolean-inverter.process.js +12 -0
  137. package/nodes/boolean-inverter/boolean-inverter.tests.json +22 -0
  138. package/nodes/ceil/ceil.config.json +23 -0
  139. package/nodes/ceil/ceil.process.js +8 -0
  140. package/nodes/ceil/ceil.tests.json +38 -0
  141. package/nodes/clean-text/clean-text.config.json +126 -0
  142. package/nodes/cohere-rerank-4-fast/cohere-rerank-4-fast.config.json +95 -0
  143. package/nodes/cohere-rerank-4-fast/cohere-rerank-4-fast.process.js +60 -0
  144. package/nodes/cohere-rerank-4-fast/cohere-rerank-4-fast.tests.json +47 -0
  145. package/nodes/cohere-rerank-4-pro/cohere-rerank-4-pro.config.json +95 -0
  146. package/nodes/cohere-rerank-4-pro/cohere-rerank-4-pro.process.js +60 -0
  147. package/nodes/cohere-rerank-4-pro/cohere-rerank-4-pro.tests.json +47 -0
  148. package/nodes/cohere-rerank-v3-5/cohere-rerank-v3-5.config.json +95 -0
  149. package/nodes/cohere-rerank-v3-5/cohere-rerank-v3-5.process.js +60 -0
  150. package/nodes/cohere-rerank-v3-5/cohere-rerank-v3-5.tests.json +47 -0
  151. package/nodes/combine-document-chunks/combine-document-chunks.config.json +58 -0
  152. package/nodes/combine-document-chunks/combine-document-chunks.process.js +119 -0
  153. package/nodes/combine-document-chunks/combine-document-chunks.tests.json +51 -0
  154. package/nodes/condition-check/condition-check.config.json +38 -0
  155. package/nodes/condition-check/condition-check.process.js +16 -0
  156. package/nodes/condition-check/condition-check.tests.json +112 -0
  157. package/nodes/confluence-get-page/confluence-get-page.config.json +45 -0
  158. package/nodes/confluence-get-page/confluence-get-page.process.js +22 -0
  159. package/nodes/confluence-get-page/confluence-get-page.tests.json +12 -0
  160. package/nodes/confluence-search/confluence-search.config.json +53 -0
  161. package/nodes/confluence-search/confluence-search.process.js +21 -0
  162. package/nodes/confluence-search/confluence-search.tests.json +14 -0
  163. package/nodes/conversation-to-text/conversation-to-text.config.json +55 -0
  164. package/nodes/conversation-to-text/conversation-to-text.process.js +58 -0
  165. package/nodes/conversation-to-text/conversation-to-text.tests.json +134 -0
  166. package/nodes/csv-parser/csv-parser.config.json +74 -0
  167. package/nodes/csv-parser/csv-parser.process.js +114 -0
  168. package/nodes/csv-parser/csv-parser.tests.json +93 -0
  169. package/nodes/csv-stringifier/csv-stringifier.config.json +76 -0
  170. package/nodes/csv-stringifier/csv-stringifier.process.js +101 -0
  171. package/nodes/csv-stringifier/csv-stringifier.tests.json +63 -0
  172. package/nodes/date-formatter/date-formatter.config.json +66 -0
  173. package/nodes/date-formatter/date-formatter.process.js +113 -0
  174. package/nodes/date-formatter/date-formatter.tests.json +50 -0
  175. package/nodes/dedupe-messages/dedupe-messages.config.json +30 -0
  176. package/nodes/dedupe-messages/dedupe-messages.process.js +44 -0
  177. package/nodes/dedupe-messages/dedupe-messages.tests.json +149 -0
  178. package/nodes/deepseek-deepseek-chat/deepseek-deepseek-chat.config.json +191 -0
  179. package/nodes/deepseek-deepseek-chat/deepseek-deepseek-chat.process.js +136 -0
  180. package/nodes/deepseek-deepseek-chat/deepseek-deepseek-chat.tests.json +80 -0
  181. package/nodes/deepseek-deepseek-chat-v3-0324/deepseek-deepseek-chat-v3-0324.config.json +191 -0
  182. package/nodes/deepseek-deepseek-chat-v3-0324/deepseek-deepseek-chat-v3-0324.process.js +136 -0
  183. package/nodes/deepseek-deepseek-chat-v3-0324/deepseek-deepseek-chat-v3-0324.tests.json +80 -0
  184. package/nodes/deepseek-deepseek-chat-v3-1/deepseek-deepseek-chat-v3-1.config.json +213 -0
  185. package/nodes/deepseek-deepseek-chat-v3-1/deepseek-deepseek-chat-v3-1.process.js +138 -0
  186. package/nodes/deepseek-deepseek-chat-v3-1/deepseek-deepseek-chat-v3-1.tests.json +80 -0
  187. package/nodes/deepseek-deepseek-r1/deepseek-deepseek-r1.config.json +212 -0
  188. package/nodes/deepseek-deepseek-r1/deepseek-deepseek-r1.process.js +138 -0
  189. package/nodes/deepseek-deepseek-r1/deepseek-deepseek-r1.tests.json +80 -0
  190. package/nodes/deepseek-deepseek-r1-0528/deepseek-deepseek-r1-0528.config.json +213 -0
  191. package/nodes/deepseek-deepseek-r1-0528/deepseek-deepseek-r1-0528.process.js +138 -0
  192. package/nodes/deepseek-deepseek-r1-0528/deepseek-deepseek-r1-0528.tests.json +80 -0
  193. package/nodes/deepseek-deepseek-r1-distill-llama-70b/deepseek-deepseek-r1-distill-llama-70b.config.json +189 -0
  194. package/nodes/deepseek-deepseek-r1-distill-llama-70b/deepseek-deepseek-r1-distill-llama-70b.process.js +138 -0
  195. package/nodes/deepseek-deepseek-r1-distill-llama-70b/deepseek-deepseek-r1-distill-llama-70b.tests.json +80 -0
  196. package/nodes/deepseek-deepseek-r1-distill-qwen-32b/deepseek-deepseek-r1-distill-qwen-32b.config.json +209 -0
  197. package/nodes/deepseek-deepseek-r1-distill-qwen-32b/deepseek-deepseek-r1-distill-qwen-32b.process.js +139 -0
  198. package/nodes/deepseek-deepseek-r1-distill-qwen-32b/deepseek-deepseek-r1-distill-qwen-32b.tests.json +80 -0
  199. package/nodes/deepseek-deepseek-v3-1-terminus/deepseek-deepseek-v3-1-terminus.config.json +213 -0
  200. package/nodes/deepseek-deepseek-v3-1-terminus/deepseek-deepseek-v3-1-terminus.process.js +138 -0
  201. package/nodes/deepseek-deepseek-v3-1-terminus/deepseek-deepseek-v3-1-terminus.tests.json +80 -0
  202. package/nodes/deepseek-deepseek-v3-2/deepseek-deepseek-v3-2.config.json +213 -0
  203. package/nodes/deepseek-deepseek-v3-2/deepseek-deepseek-v3-2.process.js +138 -0
  204. package/nodes/deepseek-deepseek-v3-2/deepseek-deepseek-v3-2.tests.json +80 -0
  205. package/nodes/deepseek-deepseek-v3-2-exp/deepseek-deepseek-v3-2-exp.config.json +213 -0
  206. package/nodes/deepseek-deepseek-v3-2-exp/deepseek-deepseek-v3-2-exp.process.js +138 -0
  207. package/nodes/deepseek-deepseek-v3-2-exp/deepseek-deepseek-v3-2-exp.tests.json +80 -0
  208. package/nodes/deepseek-deepseek-v4-flash/deepseek-deepseek-v4-flash.config.json +235 -0
  209. package/nodes/deepseek-deepseek-v4-flash/deepseek-deepseek-v4-flash.process.js +139 -0
  210. package/nodes/deepseek-deepseek-v4-flash/deepseek-deepseek-v4-flash.tests.json +80 -0
  211. package/nodes/deepseek-deepseek-v4-pro/deepseek-deepseek-v4-pro.config.json +235 -0
  212. package/nodes/deepseek-deepseek-v4-pro/deepseek-deepseek-v4-pro.process.js +139 -0
  213. package/nodes/deepseek-deepseek-v4-pro/deepseek-deepseek-v4-pro.tests.json +80 -0
  214. package/nodes/delay/delay.config.json +43 -0
  215. package/nodes/delay/delay.process.js +16 -0
  216. package/nodes/delay/delay.tests.json +59 -0
  217. package/nodes/delete-object-property/delete-object-property.config.json +53 -0
  218. package/nodes/delete-object-property/delete-object-property.process.js +42 -0
  219. package/nodes/delete-object-property/delete-object-property.tests.json +173 -0
  220. package/nodes/divide/divide.config.json +31 -0
  221. package/nodes/divide/divide.process.js +18 -0
  222. package/nodes/divide/divide.tests.json +42 -0
  223. package/nodes/duration-calculator/duration-calculator.config.json +77 -0
  224. package/nodes/duration-calculator/duration-calculator.process.js +96 -0
  225. package/nodes/duration-calculator/duration-calculator.tests.json +124 -0
  226. package/nodes/embedding/embedding.config.json +81 -0
  227. package/nodes/embedding/embedding.process.js +44 -0
  228. package/nodes/embedding/embedding.tests.json +26 -0
  229. package/nodes/equals/equals.config.json +32 -0
  230. package/nodes/equals/equals.process.js +29 -0
  231. package/nodes/equals/equals.tests.json +111 -0
  232. package/nodes/extract-code/extract-code.config.json +57 -0
  233. package/nodes/extract-code/extract-code.process.js +53 -0
  234. package/nodes/extract-code/extract-code.tests.json +162 -0
  235. package/nodes/extract-emails/extract-emails.config.json +85 -0
  236. package/nodes/extract-hashtags/extract-hashtags.config.json +85 -0
  237. package/nodes/extract-json/extract-json.config.json +44 -0
  238. package/nodes/extract-json/extract-json.process.js +102 -0
  239. package/nodes/extract-json/extract-json.tests.json +146 -0
  240. package/nodes/extract-mentions/extract-mentions.config.json +85 -0
  241. package/nodes/extract-phone-numbers/extract-phone-numbers.config.json +85 -0
  242. package/nodes/extract-text-content/extract-text-content.config.json +56 -0
  243. package/nodes/extract-text-content/extract-text-content.process.js +85 -0
  244. package/nodes/extract-text-content/extract-text-content.tests.json +144 -0
  245. package/nodes/extract-urls/extract-urls.config.json +85 -0
  246. package/nodes/filter-messages/filter-messages.config.json +53 -0
  247. package/nodes/filter-messages/filter-messages.process.js +88 -0
  248. package/nodes/filter-messages/filter-messages.tests.json +125 -0
  249. package/nodes/filter-tools/filter-tools.config.json +29 -0
  250. package/nodes/filter-tools/filter-tools.process.js +13 -0
  251. package/nodes/filter-tools/filter-tools.tests.json +89 -0
  252. package/nodes/firecrawl-scrape/firecrawl-scrape.config.json +171 -0
  253. package/nodes/firecrawl-scrape/firecrawl-scrape.process.js +69 -0
  254. package/nodes/firecrawl-scrape/firecrawl-scrape.tests.json +57 -0
  255. package/nodes/floor/floor.config.json +23 -0
  256. package/nodes/floor/floor.process.js +8 -0
  257. package/nodes/floor/floor.tests.json +38 -0
  258. package/nodes/generate-uuid/generate-uuid.config.json +31 -0
  259. package/nodes/generate-uuid/generate-uuid.process.js +15 -0
  260. package/nodes/generate-uuid/generate-uuid.tests.json +7 -0
  261. package/nodes/get-chunk-by-index/get-chunk-by-index.config.json +45 -0
  262. package/nodes/get-chunk-by-index/get-chunk-by-index.process.js +80 -0
  263. package/nodes/get-chunk-by-index/get-chunk-by-index.tests.json +51 -0
  264. package/nodes/get-first-n-messages/get-first-n-messages.config.json +37 -0
  265. package/nodes/get-first-n-messages/get-first-n-messages.process.js +27 -0
  266. package/nodes/get-first-n-messages/get-first-n-messages.tests.json +66 -0
  267. package/nodes/get-last-n-messages/get-last-n-messages.config.json +37 -0
  268. package/nodes/get-last-n-messages/get-last-n-messages.process.js +27 -0
  269. package/nodes/get-last-n-messages/get-last-n-messages.tests.json +66 -0
  270. package/nodes/get-messages-by-role/get-messages-by-role.config.json +37 -0
  271. package/nodes/get-messages-by-role/get-messages-by-role.process.js +28 -0
  272. package/nodes/get-messages-by-role/get-messages-by-role.tests.json +87 -0
  273. package/nodes/get-messages-range/get-messages-range.config.json +44 -0
  274. package/nodes/get-messages-range/get-messages-range.process.js +44 -0
  275. package/nodes/get-messages-range/get-messages-range.tests.json +104 -0
  276. package/nodes/get-object-property/get-object-property.config.json +54 -0
  277. package/nodes/get-object-property/get-object-property.process.js +35 -0
  278. package/nodes/get-object-property/get-object-property.tests.json +101 -0
  279. package/nodes/github-create-issue/github-create-issue.config.json +72 -0
  280. package/nodes/github-create-issue/github-create-issue.process.js +22 -0
  281. package/nodes/github-create-issue/github-create-issue.tests.json +15 -0
  282. package/nodes/google-custom-search/google-custom-search.config.json +229 -0
  283. package/nodes/google-custom-search/google-custom-search.process.js +96 -0
  284. package/nodes/google-custom-search/google-custom-search.tests.json +69 -0
  285. package/nodes/google-gemini-2-5-flash/google-gemini-2-5-flash.config.json +193 -0
  286. package/nodes/google-gemini-2-5-flash/google-gemini-2-5-flash.process.js +138 -0
  287. package/nodes/google-gemini-2-5-flash/google-gemini-2-5-flash.tests.json +80 -0
  288. package/nodes/google-gemini-2-5-flash-image/google-gemini-2-5-flash-image.config.json +129 -0
  289. package/nodes/google-gemini-2-5-flash-image/google-gemini-2-5-flash-image.process.js +55 -0
  290. package/nodes/google-gemini-2-5-flash-image/google-gemini-2-5-flash-image.tests.json +44 -0
  291. package/nodes/google-gemini-2-5-flash-lite/google-gemini-2-5-flash-lite.config.json +193 -0
  292. package/nodes/google-gemini-2-5-flash-lite/google-gemini-2-5-flash-lite.process.js +138 -0
  293. package/nodes/google-gemini-2-5-flash-lite/google-gemini-2-5-flash-lite.tests.json +80 -0
  294. package/nodes/google-gemini-2-5-flash-lite-preview-09-2025/google-gemini-2-5-flash-lite-preview-09-2025.config.json +193 -0
  295. package/nodes/google-gemini-2-5-flash-lite-preview-09-2025/google-gemini-2-5-flash-lite-preview-09-2025.process.js +138 -0
  296. package/nodes/google-gemini-2-5-flash-lite-preview-09-2025/google-gemini-2-5-flash-lite-preview-09-2025.tests.json +80 -0
  297. package/nodes/google-gemini-2-5-pro/google-gemini-2-5-pro.config.json +193 -0
  298. package/nodes/google-gemini-2-5-pro/google-gemini-2-5-pro.process.js +138 -0
  299. package/nodes/google-gemini-2-5-pro/google-gemini-2-5-pro.tests.json +80 -0
  300. package/nodes/google-gemini-2-5-pro-preview/google-gemini-2-5-pro-preview.config.json +193 -0
  301. package/nodes/google-gemini-2-5-pro-preview/google-gemini-2-5-pro-preview.process.js +138 -0
  302. package/nodes/google-gemini-2-5-pro-preview/google-gemini-2-5-pro-preview.tests.json +80 -0
  303. package/nodes/google-gemini-2-5-pro-preview-05-06/google-gemini-2-5-pro-preview-05-06.config.json +193 -0
  304. package/nodes/google-gemini-2-5-pro-preview-05-06/google-gemini-2-5-pro-preview-05-06.process.js +138 -0
  305. package/nodes/google-gemini-2-5-pro-preview-05-06/google-gemini-2-5-pro-preview-05-06.tests.json +80 -0
  306. package/nodes/google-gemini-3-1-flash-image-preview/google-gemini-3-1-flash-image-preview.config.json +151 -0
  307. package/nodes/google-gemini-3-1-flash-image-preview/google-gemini-3-1-flash-image-preview.process.js +57 -0
  308. package/nodes/google-gemini-3-1-flash-image-preview/google-gemini-3-1-flash-image-preview.tests.json +44 -0
  309. package/nodes/google-gemini-3-1-flash-lite/google-gemini-3-1-flash-lite.config.json +193 -0
  310. package/nodes/google-gemini-3-1-flash-lite/google-gemini-3-1-flash-lite.process.js +138 -0
  311. package/nodes/google-gemini-3-1-flash-lite/google-gemini-3-1-flash-lite.tests.json +80 -0
  312. package/nodes/google-gemini-3-1-flash-lite-preview/google-gemini-3-1-flash-lite-preview.config.json +193 -0
  313. package/nodes/google-gemini-3-1-flash-lite-preview/google-gemini-3-1-flash-lite-preview.process.js +138 -0
  314. package/nodes/google-gemini-3-1-flash-lite-preview/google-gemini-3-1-flash-lite-preview.tests.json +80 -0
  315. package/nodes/google-gemini-3-1-pro-preview/google-gemini-3-1-pro-preview.config.json +193 -0
  316. package/nodes/google-gemini-3-1-pro-preview/google-gemini-3-1-pro-preview.process.js +138 -0
  317. package/nodes/google-gemini-3-1-pro-preview/google-gemini-3-1-pro-preview.tests.json +80 -0
  318. package/nodes/google-gemini-3-1-pro-preview-customtools/google-gemini-3-1-pro-preview-customtools.config.json +193 -0
  319. package/nodes/google-gemini-3-1-pro-preview-customtools/google-gemini-3-1-pro-preview-customtools.process.js +138 -0
  320. package/nodes/google-gemini-3-1-pro-preview-customtools/google-gemini-3-1-pro-preview-customtools.tests.json +80 -0
  321. package/nodes/google-gemini-3-5-flash/google-gemini-3-5-flash.config.json +193 -0
  322. package/nodes/google-gemini-3-5-flash/google-gemini-3-5-flash.process.js +138 -0
  323. package/nodes/google-gemini-3-5-flash/google-gemini-3-5-flash.tests.json +80 -0
  324. package/nodes/google-gemini-3-flash-preview/google-gemini-3-flash-preview.config.json +193 -0
  325. package/nodes/google-gemini-3-flash-preview/google-gemini-3-flash-preview.process.js +138 -0
  326. package/nodes/google-gemini-3-flash-preview/google-gemini-3-flash-preview.tests.json +80 -0
  327. package/nodes/google-gemini-3-pro-image-preview/google-gemini-3-pro-image-preview.config.json +151 -0
  328. package/nodes/google-gemini-3-pro-image-preview/google-gemini-3-pro-image-preview.process.js +57 -0
  329. package/nodes/google-gemini-3-pro-image-preview/google-gemini-3-pro-image-preview.tests.json +44 -0
  330. package/nodes/google-gemini-embedding-001/google-gemini-embedding-001.config.json +90 -0
  331. package/nodes/google-gemini-embedding-001/google-gemini-embedding-001.process.js +36 -0
  332. package/nodes/google-gemini-embedding-001/google-gemini-embedding-001.tests.json +48 -0
  333. package/nodes/google-gemini-embedding-2/google-gemini-embedding-2.config.json +94 -0
  334. package/nodes/google-gemini-embedding-2/google-gemini-embedding-2.process.js +36 -0
  335. package/nodes/google-gemini-embedding-2/google-gemini-embedding-2.tests.json +48 -0
  336. package/nodes/google-gemini-embedding-2-preview/google-gemini-embedding-2-preview.config.json +94 -0
  337. package/nodes/google-gemini-embedding-2-preview/google-gemini-embedding-2-preview.process.js +36 -0
  338. package/nodes/google-gemini-embedding-2-preview/google-gemini-embedding-2-preview.tests.json +48 -0
  339. package/nodes/google-gemma-2-27b-it/google-gemma-2-27b-it.config.json +165 -0
  340. package/nodes/google-gemma-2-27b-it/google-gemma-2-27b-it.process.js +136 -0
  341. package/nodes/google-gemma-2-27b-it/google-gemma-2-27b-it.tests.json +80 -0
  342. package/nodes/google-gemma-3-12b-it/google-gemma-3-12b-it.config.json +191 -0
  343. package/nodes/google-gemma-3-12b-it/google-gemma-3-12b-it.process.js +136 -0
  344. package/nodes/google-gemma-3-12b-it/google-gemma-3-12b-it.tests.json +80 -0
  345. package/nodes/google-gemma-3-27b-it/google-gemma-3-27b-it.config.json +191 -0
  346. package/nodes/google-gemma-3-27b-it/google-gemma-3-27b-it.process.js +136 -0
  347. package/nodes/google-gemma-3-27b-it/google-gemma-3-27b-it.tests.json +80 -0
  348. package/nodes/google-gemma-3-4b-it/google-gemma-3-4b-it.config.json +168 -0
  349. package/nodes/google-gemma-3-4b-it/google-gemma-3-4b-it.process.js +136 -0
  350. package/nodes/google-gemma-3-4b-it/google-gemma-3-4b-it.tests.json +80 -0
  351. package/nodes/google-gemma-3n-e4b-it/google-gemma-3n-e4b-it.config.json +123 -0
  352. package/nodes/google-gemma-3n-e4b-it/google-gemma-3n-e4b-it.process.js +51 -0
  353. package/nodes/google-gemma-3n-e4b-it/google-gemma-3n-e4b-it.tests.json +44 -0
  354. package/nodes/google-gemma-4-26b-a4b-it/google-gemma-4-26b-a4b-it.config.json +235 -0
  355. package/nodes/google-gemma-4-26b-a4b-it/google-gemma-4-26b-a4b-it.process.js +139 -0
  356. package/nodes/google-gemma-4-26b-a4b-it/google-gemma-4-26b-a4b-it.tests.json +80 -0
  357. package/nodes/google-gemma-4-26b-a4b-it-free/google-gemma-4-26b-a4b-it-free.config.json +184 -0
  358. package/nodes/google-gemma-4-26b-a4b-it-free/google-gemma-4-26b-a4b-it-free.process.js +138 -0
  359. package/nodes/google-gemma-4-26b-a4b-it-free/google-gemma-4-26b-a4b-it-free.tests.json +80 -0
  360. package/nodes/google-gemma-4-31b-it/google-gemma-4-31b-it.config.json +235 -0
  361. package/nodes/google-gemma-4-31b-it/google-gemma-4-31b-it.process.js +139 -0
  362. package/nodes/google-gemma-4-31b-it/google-gemma-4-31b-it.tests.json +80 -0
  363. package/nodes/google-gemma-4-31b-it-free/google-gemma-4-31b-it-free.config.json +192 -0
  364. package/nodes/google-gemma-4-31b-it-free/google-gemma-4-31b-it-free.process.js +138 -0
  365. package/nodes/google-gemma-4-31b-it-free/google-gemma-4-31b-it-free.tests.json +80 -0
  366. package/nodes/google-lyria-3-clip-preview/google-lyria-3-clip-preview.config.json +111 -0
  367. package/nodes/google-lyria-3-clip-preview/google-lyria-3-clip-preview.process.js +51 -0
  368. package/nodes/google-lyria-3-clip-preview/google-lyria-3-clip-preview.tests.json +44 -0
  369. package/nodes/google-lyria-3-pro-preview/google-lyria-3-pro-preview.config.json +111 -0
  370. package/nodes/google-lyria-3-pro-preview/google-lyria-3-pro-preview.process.js +51 -0
  371. package/nodes/google-lyria-3-pro-preview/google-lyria-3-pro-preview.tests.json +44 -0
  372. package/nodes/greater-than/greater-than.config.json +33 -0
  373. package/nodes/greater-than/greater-than.process.js +11 -0
  374. package/nodes/greater-than/greater-than.tests.json +47 -0
  375. package/nodes/greater-than-equal/greater-than-equal.config.json +33 -0
  376. package/nodes/greater-than-equal/greater-than-equal.process.js +11 -0
  377. package/nodes/greater-than-equal/greater-than-equal.tests.json +52 -0
  378. package/nodes/has-object-property/has-object-property.config.json +55 -0
  379. package/nodes/has-object-property/has-object-property.process.js +42 -0
  380. package/nodes/has-object-property/has-object-property.tests.json +148 -0
  381. package/nodes/hash-content/hash-content.config.json +33 -0
  382. package/nodes/hash-content/hash-content.process.js +19 -0
  383. package/nodes/hash-content/hash-content.tests.json +51 -0
  384. package/nodes/histogram/histogram.config.json +36 -0
  385. package/nodes/histogram/histogram.process.js +73 -0
  386. package/nodes/histogram/histogram.tests.json +57 -0
  387. package/nodes/histogram-fixed/histogram-fixed.config.json +62 -0
  388. package/nodes/histogram-fixed/histogram-fixed.process.js +42 -0
  389. package/nodes/histogram-fixed/histogram-fixed.tests.json +62 -0
  390. package/nodes/http-request/http-request.config.json +90 -0
  391. package/nodes/http-request/http-request.process.js +89 -0
  392. package/nodes/http-request/http-request.tests.json +115 -0
  393. package/nodes/hubspot-read-contacts/hubspot-read-contacts.config.json +71 -0
  394. package/nodes/hubspot-read-contacts/hubspot-read-contacts.process.js +56 -0
  395. package/nodes/hubspot-search-contacts/hubspot-search-contacts.config.json +89 -0
  396. package/nodes/hubspot-search-contacts/hubspot-search-contacts.process.js +71 -0
  397. package/nodes/if-else/if-else.config.json +50 -0
  398. package/nodes/if-else/if-else.process.js +39 -0
  399. package/nodes/if-else/if-else.tests.json +94 -0
  400. package/nodes/input-chat/input-chat.config.json +45 -0
  401. package/nodes/input-chat/input-chat.process.js +29 -0
  402. package/nodes/input-data/input-data.config.json +59 -0
  403. package/nodes/input-data/input-data.process.js +25 -0
  404. package/nodes/input-data/input-data.tests.json +53 -0
  405. package/nodes/input-plugins/input-plugins.config.json +17 -0
  406. package/nodes/input-plugins/input-plugins.process.js +11 -0
  407. package/nodes/input-prompt/input-prompt.config.json +39 -0
  408. package/nodes/input-prompt/input-prompt.process.js +9 -0
  409. package/nodes/intfloat-e5-base-v2/intfloat-e5-base-v2.config.json +90 -0
  410. package/nodes/intfloat-e5-base-v2/intfloat-e5-base-v2.process.js +36 -0
  411. package/nodes/intfloat-e5-base-v2/intfloat-e5-base-v2.tests.json +48 -0
  412. package/nodes/intfloat-e5-large-v2/intfloat-e5-large-v2.config.json +90 -0
  413. package/nodes/intfloat-e5-large-v2/intfloat-e5-large-v2.process.js +36 -0
  414. package/nodes/intfloat-e5-large-v2/intfloat-e5-large-v2.tests.json +48 -0
  415. package/nodes/intfloat-multilingual-e5-large/intfloat-multilingual-e5-large.config.json +90 -0
  416. package/nodes/intfloat-multilingual-e5-large/intfloat-multilingual-e5-large.process.js +36 -0
  417. package/nodes/intfloat-multilingual-e5-large/intfloat-multilingual-e5-large.tests.json +48 -0
  418. package/nodes/jira-create-issue/jira-create-issue.config.json +87 -0
  419. package/nodes/jira-create-issue/jira-create-issue.process.js +24 -0
  420. package/nodes/jira-create-issue/jira-create-issue.tests.json +14 -0
  421. package/nodes/jira-list-issues/jira-list-issues.config.json +53 -0
  422. package/nodes/jira-list-issues/jira-list-issues.process.js +19 -0
  423. package/nodes/jira-list-issues/jira-list-issues.tests.json +14 -0
  424. package/nodes/jira-update-issue/jira-update-issue.config.json +60 -0
  425. package/nodes/jira-update-issue/jira-update-issue.process.js +30 -0
  426. package/nodes/jira-update-issue/jira-update-issue.tests.json +12 -0
  427. package/nodes/json-parser/json-parser.config.json +47 -0
  428. package/nodes/json-parser/json-parser.process.js +23 -0
  429. package/nodes/json-parser/json-parser.tests.json +57 -0
  430. package/nodes/json-stringifier/json-stringifier.config.json +54 -0
  431. package/nodes/json-stringifier/json-stringifier.process.js +20 -0
  432. package/nodes/json-stringifier/json-stringifier.tests.json +58 -0
  433. package/nodes/json-to-xml/json-to-xml.config.json +48 -0
  434. package/nodes/json-to-xml/json-to-xml.process.js +60 -0
  435. package/nodes/json-to-xml/json-to-xml.tests.json +103 -0
  436. package/nodes/json-to-yaml/json-to-yaml.config.json +32 -0
  437. package/nodes/json-to-yaml/json-to-yaml.process.js +89 -0
  438. package/nodes/json-to-yaml/json-to-yaml.tests.json +101 -0
  439. package/nodes/keep-bookends/keep-bookends.config.json +44 -0
  440. package/nodes/keep-bookends/keep-bookends.process.js +44 -0
  441. package/nodes/keep-bookends/keep-bookends.tests.json +185 -0
  442. package/nodes/less-than/less-than.config.json +33 -0
  443. package/nodes/less-than/less-than.process.js +11 -0
  444. package/nodes/less-than/less-than.tests.json +47 -0
  445. package/nodes/less-than-equal/less-than-equal.config.json +33 -0
  446. package/nodes/less-than-equal/less-than-equal.process.js +11 -0
  447. package/nodes/less-than-equal/less-than-equal.tests.json +52 -0
  448. package/nodes/linear-create-issue/linear-create-issue.config.json +72 -0
  449. package/nodes/linear-create-issue/linear-create-issue.process.js +24 -0
  450. package/nodes/linear-create-issue/linear-create-issue.tests.json +14 -0
  451. package/nodes/linear-list-issues/linear-list-issues.config.json +58 -0
  452. package/nodes/linear-list-issues/linear-list-issues.process.js +22 -0
  453. package/nodes/linear-list-issues/linear-list-issues.tests.json +13 -0
  454. package/nodes/linear-update-issue/linear-update-issue.config.json +72 -0
  455. package/nodes/linear-update-issue/linear-update-issue.process.js +24 -0
  456. package/nodes/linear-update-issue/linear-update-issue.tests.json +13 -0
  457. package/nodes/liquid-lfm-2-24b-a2b/liquid-lfm-2-24b-a2b.config.json +123 -0
  458. package/nodes/liquid-lfm-2-24b-a2b/liquid-lfm-2-24b-a2b.process.js +51 -0
  459. package/nodes/liquid-lfm-2-24b-a2b/liquid-lfm-2-24b-a2b.tests.json +44 -0
  460. package/nodes/liquid-lfm-2-5-1-2b-instruct-free/liquid-lfm-2-5-1-2b-instruct-free.config.json +130 -0
  461. package/nodes/liquid-lfm-2-5-1-2b-instruct-free/liquid-lfm-2-5-1-2b-instruct-free.process.js +51 -0
  462. package/nodes/liquid-lfm-2-5-1-2b-instruct-free/liquid-lfm-2-5-1-2b-instruct-free.tests.json +44 -0
  463. package/nodes/liquid-lfm-2-5-1-2b-thinking-free/liquid-lfm-2-5-1-2b-thinking-free.config.json +152 -0
  464. package/nodes/liquid-lfm-2-5-1-2b-thinking-free/liquid-lfm-2-5-1-2b-thinking-free.process.js +53 -0
  465. package/nodes/liquid-lfm-2-5-1-2b-thinking-free/liquid-lfm-2-5-1-2b-thinking-free.tests.json +44 -0
  466. package/nodes/loop-end/loop-end.config.json +74 -0
  467. package/nodes/loop-end/loop-end.process.js +69 -0
  468. package/nodes/loop-end/loop-end.tests.json +139 -0
  469. package/nodes/loop-start/loop-start.config.json +28 -0
  470. package/nodes/loop-start/loop-start.process.js +18 -0
  471. package/nodes/loop-start/loop-start.tests.json +65 -0
  472. package/nodes/mean/mean.config.json +23 -0
  473. package/nodes/mean/mean.process.js +23 -0
  474. package/nodes/mean/mean.tests.json +45 -0
  475. package/nodes/median/median.config.json +23 -0
  476. package/nodes/median/median.process.js +29 -0
  477. package/nodes/median/median.tests.json +54 -0
  478. package/nodes/merge-consecutive-roles/merge-consecutive-roles.config.json +38 -0
  479. package/nodes/merge-consecutive-roles/merge-consecutive-roles.process.js +75 -0
  480. package/nodes/merge-consecutive-roles/merge-consecutive-roles.tests.json +199 -0
  481. package/nodes/merge-messages/merge-messages.config.json +60 -0
  482. package/nodes/merge-messages/merge-messages.process.js +99 -0
  483. package/nodes/merge-messages/merge-messages.tests.json +174 -0
  484. package/nodes/message/message.config.json +57 -0
  485. package/nodes/message/message.process.js +40 -0
  486. package/nodes/message-bus/message-bus.config.json +103 -0
  487. package/nodes/message-bus/message-bus.process.js +69 -0
  488. package/nodes/message-bus/message-bus.tests.json +150 -0
  489. package/nodes/message-role-swap/message-role-swap.config.json +26 -0
  490. package/nodes/message-role-swap/message-role-swap.process.js +39 -0
  491. package/nodes/message-role-swap/message-role-swap.tests.json +197 -0
  492. package/nodes/mistralai-codestral-2508/mistralai-codestral-2508.config.json +187 -0
  493. package/nodes/mistralai-codestral-2508/mistralai-codestral-2508.process.js +136 -0
  494. package/nodes/mistralai-codestral-2508/mistralai-codestral-2508.tests.json +80 -0
  495. package/nodes/mistralai-codestral-embed-2505/mistralai-codestral-embed-2505.config.json +90 -0
  496. package/nodes/mistralai-codestral-embed-2505/mistralai-codestral-embed-2505.process.js +36 -0
  497. package/nodes/mistralai-codestral-embed-2505/mistralai-codestral-embed-2505.tests.json +48 -0
  498. package/nodes/mistralai-devstral-2512/mistralai-devstral-2512.config.json +187 -0
  499. package/nodes/mistralai-devstral-2512/mistralai-devstral-2512.process.js +136 -0
  500. package/nodes/mistralai-devstral-2512/mistralai-devstral-2512.tests.json +80 -0
  501. package/nodes/mistralai-ministral-14b-2512/mistralai-ministral-14b-2512.config.json +210 -0
  502. package/nodes/mistralai-ministral-14b-2512/mistralai-ministral-14b-2512.process.js +137 -0
  503. package/nodes/mistralai-ministral-14b-2512/mistralai-ministral-14b-2512.tests.json +80 -0
  504. package/nodes/mistralai-ministral-3b-2512/mistralai-ministral-3b-2512.config.json +210 -0
  505. package/nodes/mistralai-ministral-3b-2512/mistralai-ministral-3b-2512.process.js +137 -0
  506. package/nodes/mistralai-ministral-3b-2512/mistralai-ministral-3b-2512.tests.json +80 -0
  507. package/nodes/mistralai-ministral-8b-2512/mistralai-ministral-8b-2512.config.json +210 -0
  508. package/nodes/mistralai-ministral-8b-2512/mistralai-ministral-8b-2512.process.js +137 -0
  509. package/nodes/mistralai-ministral-8b-2512/mistralai-ministral-8b-2512.tests.json +80 -0
  510. package/nodes/mistralai-mistral-embed-2312/mistralai-mistral-embed-2312.config.json +90 -0
  511. package/nodes/mistralai-mistral-embed-2312/mistralai-mistral-embed-2312.process.js +36 -0
  512. package/nodes/mistralai-mistral-embed-2312/mistralai-mistral-embed-2312.tests.json +48 -0
  513. package/nodes/mistralai-mistral-large/mistralai-mistral-large.config.json +187 -0
  514. package/nodes/mistralai-mistral-large/mistralai-mistral-large.process.js +136 -0
  515. package/nodes/mistralai-mistral-large/mistralai-mistral-large.tests.json +80 -0
  516. package/nodes/mistralai-mistral-large-2407/mistralai-mistral-large-2407.config.json +187 -0
  517. package/nodes/mistralai-mistral-large-2407/mistralai-mistral-large-2407.process.js +136 -0
  518. package/nodes/mistralai-mistral-large-2407/mistralai-mistral-large-2407.tests.json +80 -0
  519. package/nodes/mistralai-mistral-large-2512/mistralai-mistral-large-2512.config.json +187 -0
  520. package/nodes/mistralai-mistral-large-2512/mistralai-mistral-large-2512.process.js +136 -0
  521. package/nodes/mistralai-mistral-large-2512/mistralai-mistral-large-2512.tests.json +80 -0
  522. package/nodes/mistralai-mistral-medium-3/mistralai-mistral-medium-3.config.json +187 -0
  523. package/nodes/mistralai-mistral-medium-3/mistralai-mistral-medium-3.process.js +136 -0
  524. package/nodes/mistralai-mistral-medium-3/mistralai-mistral-medium-3.tests.json +80 -0
  525. package/nodes/mistralai-mistral-medium-3-1/mistralai-mistral-medium-3-1.config.json +187 -0
  526. package/nodes/mistralai-mistral-medium-3-1/mistralai-mistral-medium-3-1.process.js +136 -0
  527. package/nodes/mistralai-mistral-medium-3-1/mistralai-mistral-medium-3-1.tests.json +80 -0
  528. package/nodes/mistralai-mistral-medium-3-5/mistralai-mistral-medium-3-5.config.json +209 -0
  529. package/nodes/mistralai-mistral-medium-3-5/mistralai-mistral-medium-3-5.process.js +138 -0
  530. package/nodes/mistralai-mistral-medium-3-5/mistralai-mistral-medium-3-5.tests.json +80 -0
  531. package/nodes/mistralai-mistral-nemo/mistralai-mistral-nemo.config.json +191 -0
  532. package/nodes/mistralai-mistral-nemo/mistralai-mistral-nemo.process.js +136 -0
  533. package/nodes/mistralai-mistral-nemo/mistralai-mistral-nemo.tests.json +80 -0
  534. package/nodes/mistralai-mistral-saba/mistralai-mistral-saba.config.json +187 -0
  535. package/nodes/mistralai-mistral-saba/mistralai-mistral-saba.process.js +136 -0
  536. package/nodes/mistralai-mistral-saba/mistralai-mistral-saba.tests.json +80 -0
  537. package/nodes/mistralai-mistral-small-24b-instruct-2501/mistralai-mistral-small-24b-instruct-2501.config.json +140 -0
  538. package/nodes/mistralai-mistral-small-24b-instruct-2501/mistralai-mistral-small-24b-instruct-2501.process.js +51 -0
  539. package/nodes/mistralai-mistral-small-24b-instruct-2501/mistralai-mistral-small-24b-instruct-2501.tests.json +44 -0
  540. package/nodes/mistralai-mistral-small-2603/mistralai-mistral-small-2603.config.json +210 -0
  541. package/nodes/mistralai-mistral-small-2603/mistralai-mistral-small-2603.process.js +138 -0
  542. package/nodes/mistralai-mistral-small-2603/mistralai-mistral-small-2603.tests.json +80 -0
  543. package/nodes/mistralai-mistral-small-3-1-24b-instruct/mistralai-mistral-small-3-1-24b-instruct.config.json +131 -0
  544. package/nodes/mistralai-mistral-small-3-1-24b-instruct/mistralai-mistral-small-3-1-24b-instruct.process.js +51 -0
  545. package/nodes/mistralai-mistral-small-3-1-24b-instruct/mistralai-mistral-small-3-1-24b-instruct.tests.json +44 -0
  546. package/nodes/mistralai-mistral-small-3-2-24b-instruct/mistralai-mistral-small-3-2-24b-instruct.config.json +191 -0
  547. package/nodes/mistralai-mistral-small-3-2-24b-instruct/mistralai-mistral-small-3-2-24b-instruct.process.js +136 -0
  548. package/nodes/mistralai-mistral-small-3-2-24b-instruct/mistralai-mistral-small-3-2-24b-instruct.tests.json +80 -0
  549. package/nodes/mistralai-mixtral-8x22b-instruct/mistralai-mixtral-8x22b-instruct.config.json +187 -0
  550. package/nodes/mistralai-mixtral-8x22b-instruct/mistralai-mixtral-8x22b-instruct.process.js +136 -0
  551. package/nodes/mistralai-mixtral-8x22b-instruct/mistralai-mixtral-8x22b-instruct.tests.json +80 -0
  552. package/nodes/mistralai-voxtral-small-24b-2507/mistralai-voxtral-small-24b-2507.config.json +187 -0
  553. package/nodes/mistralai-voxtral-small-24b-2507/mistralai-voxtral-small-24b-2507.process.js +136 -0
  554. package/nodes/mistralai-voxtral-small-24b-2507/mistralai-voxtral-small-24b-2507.tests.json +80 -0
  555. package/nodes/mode/mode.config.json +29 -0
  556. package/nodes/mode/mode.process.js +45 -0
  557. package/nodes/mode/mode.tests.json +53 -0
  558. package/nodes/modulo/modulo.config.json +30 -0
  559. package/nodes/modulo/modulo.process.js +31 -0
  560. package/nodes/modulo/modulo.tests.json +66 -0
  561. package/nodes/multiply/multiply.config.json +30 -0
  562. package/nodes/multiply/multiply.process.js +9 -0
  563. package/nodes/multiply/multiply.tests.json +42 -0
  564. package/nodes/nand-gate/nand-gate.config.json +30 -0
  565. package/nodes/nand-gate/nand-gate.process.js +10 -0
  566. package/nodes/nand-gate/nand-gate.tests.json +22 -0
  567. package/nodes/newsdata-io-archive/newsdata-io-archive.config.json +144 -0
  568. package/nodes/newsdata-io-archive/newsdata-io-archive.process.js +60 -0
  569. package/nodes/newsdata-io-archive/newsdata-io-archive.tests.json +68 -0
  570. package/nodes/newsdata-io-latest/newsdata-io-latest.config.json +142 -0
  571. package/nodes/newsdata-io-latest/newsdata-io-latest.process.js +60 -0
  572. package/nodes/newsdata-io-latest/newsdata-io-latest.tests.json +74 -0
  573. package/nodes/newsdata-io-sources/newsdata-io-sources.config.json +51 -0
  574. package/nodes/newsdata-io-sources/newsdata-io-sources.process.js +41 -0
  575. package/nodes/newsdata-io-sources/newsdata-io-sources.tests.json +53 -0
  576. package/nodes/nor-gate/nor-gate.config.json +30 -0
  577. package/nodes/nor-gate/nor-gate.process.js +10 -0
  578. package/nodes/nor-gate/nor-gate.tests.json +22 -0
  579. package/nodes/normalize-whitespace/normalize-whitespace.config.json +56 -0
  580. package/nodes/normalize-whitespace/normalize-whitespace.process.js +36 -0
  581. package/nodes/normalize-whitespace/normalize-whitespace.tests.json +76 -0
  582. package/nodes/not-equals/not-equals.config.json +32 -0
  583. package/nodes/not-equals/not-equals.process.js +29 -0
  584. package/nodes/not-equals/not-equals.tests.json +77 -0
  585. package/nodes/notion-append-blocks/notion-append-blocks.config.json +38 -0
  586. package/nodes/notion-append-blocks/notion-append-blocks.process.js +34 -0
  587. package/nodes/notion-append-blocks/notion-append-blocks.tests.json +80 -0
  588. package/nodes/notion-archive-page/notion-archive-page.config.json +44 -0
  589. package/nodes/notion-archive-page/notion-archive-page.process.js +29 -0
  590. package/nodes/notion-archive-page/notion-archive-page.tests.json +26 -0
  591. package/nodes/notion-create-page/notion-create-page.config.json +71 -0
  592. package/nodes/notion-create-page/notion-create-page.process.js +45 -0
  593. package/nodes/notion-create-page/notion-create-page.tests.json +92 -0
  594. package/nodes/notion-get-blocks/notion-get-blocks.config.json +51 -0
  595. package/nodes/notion-get-blocks/notion-get-blocks.process.js +29 -0
  596. package/nodes/notion-get-blocks/notion-get-blocks.tests.json +22 -0
  597. package/nodes/notion-get-page/notion-get-page.config.json +54 -0
  598. package/nodes/notion-get-page/notion-get-page.process.js +29 -0
  599. package/nodes/notion-get-page/notion-get-page.tests.json +14 -0
  600. package/nodes/notion-query-database/notion-query-database.config.json +65 -0
  601. package/nodes/notion-query-database/notion-query-database.process.js +31 -0
  602. package/nodes/notion-query-database/notion-query-database.tests.json +52 -0
  603. package/nodes/notion-search/notion-search.config.json +74 -0
  604. package/nodes/notion-search/notion-search.process.js +41 -0
  605. package/nodes/notion-search/notion-search.tests.json +46 -0
  606. package/nodes/notion-update-page/notion-update-page.config.json +57 -0
  607. package/nodes/notion-update-page/notion-update-page.process.js +34 -0
  608. package/nodes/notion-update-page/notion-update-page.tests.json +44 -0
  609. package/nodes/null-bomb/null-bomb.config.json +36 -0
  610. package/nodes/null-bomb/null-bomb.process.js +21 -0
  611. package/nodes/null-bomb/null-bomb.tests.json +83 -0
  612. package/nodes/number/number.config.json +59 -0
  613. package/nodes/number/number.process.js +16 -0
  614. package/nodes/number/number.tests.json +46 -0
  615. package/nodes/nvidia-llama-nemotron-embed-vl-1b-v2-free/nvidia-llama-nemotron-embed-vl-1b-v2-free.config.json +91 -0
  616. package/nodes/nvidia-llama-nemotron-embed-vl-1b-v2-free/nvidia-llama-nemotron-embed-vl-1b-v2-free.process.js +36 -0
  617. package/nodes/nvidia-llama-nemotron-embed-vl-1b-v2-free/nvidia-llama-nemotron-embed-vl-1b-v2-free.tests.json +48 -0
  618. package/nodes/nvidia-llama-nemotron-rerank-vl-1b-v2-free/nvidia-llama-nemotron-rerank-vl-1b-v2-free.config.json +95 -0
  619. package/nodes/nvidia-llama-nemotron-rerank-vl-1b-v2-free/nvidia-llama-nemotron-rerank-vl-1b-v2-free.process.js +60 -0
  620. package/nodes/nvidia-llama-nemotron-rerank-vl-1b-v2-free/nvidia-llama-nemotron-rerank-vl-1b-v2-free.tests.json +47 -0
  621. package/nodes/object-merge/object-merge.config.json +34 -0
  622. package/nodes/object-merge/object-merge.process.js +53 -0
  623. package/nodes/object-merge/object-merge.tests.json +146 -0
  624. package/nodes/openai-gpt-3-5-turbo/openai-gpt-3-5-turbo.config.json +210 -0
  625. package/nodes/openai-gpt-3-5-turbo/openai-gpt-3-5-turbo.process.js +137 -0
  626. package/nodes/openai-gpt-3-5-turbo/openai-gpt-3-5-turbo.tests.json +80 -0
  627. package/nodes/openai-gpt-3-5-turbo-0613/openai-gpt-3-5-turbo-0613.config.json +203 -0
  628. package/nodes/openai-gpt-3-5-turbo-0613/openai-gpt-3-5-turbo-0613.process.js +137 -0
  629. package/nodes/openai-gpt-3-5-turbo-0613/openai-gpt-3-5-turbo-0613.tests.json +80 -0
  630. package/nodes/openai-gpt-3-5-turbo-16k/openai-gpt-3-5-turbo-16k.config.json +211 -0
  631. package/nodes/openai-gpt-3-5-turbo-16k/openai-gpt-3-5-turbo-16k.process.js +137 -0
  632. package/nodes/openai-gpt-3-5-turbo-16k/openai-gpt-3-5-turbo-16k.tests.json +80 -0
  633. package/nodes/openai-gpt-3-5-turbo-instruct/openai-gpt-3-5-turbo-instruct.config.json +187 -0
  634. package/nodes/openai-gpt-3-5-turbo-instruct/openai-gpt-3-5-turbo-instruct.process.js +137 -0
  635. package/nodes/openai-gpt-3-5-turbo-instruct/openai-gpt-3-5-turbo-instruct.tests.json +80 -0
  636. package/nodes/openai-gpt-4/openai-gpt-4.config.json +211 -0
  637. package/nodes/openai-gpt-4/openai-gpt-4.process.js +137 -0
  638. package/nodes/openai-gpt-4/openai-gpt-4.tests.json +80 -0
  639. package/nodes/openai-gpt-4-1/openai-gpt-4-1.config.json +164 -0
  640. package/nodes/openai-gpt-4-1/openai-gpt-4-1.process.js +136 -0
  641. package/nodes/openai-gpt-4-1/openai-gpt-4-1.tests.json +80 -0
  642. package/nodes/openai-gpt-4-1-mini/openai-gpt-4-1-mini.config.json +164 -0
  643. package/nodes/openai-gpt-4-1-mini/openai-gpt-4-1-mini.process.js +136 -0
  644. package/nodes/openai-gpt-4-1-mini/openai-gpt-4-1-mini.tests.json +80 -0
  645. package/nodes/openai-gpt-4-1-nano/openai-gpt-4-1-nano.config.json +164 -0
  646. package/nodes/openai-gpt-4-1-nano/openai-gpt-4-1-nano.process.js +136 -0
  647. package/nodes/openai-gpt-4-1-nano/openai-gpt-4-1-nano.tests.json +80 -0
  648. package/nodes/openai-gpt-4-1106-preview/openai-gpt-4-1106-preview.config.json +210 -0
  649. package/nodes/openai-gpt-4-1106-preview/openai-gpt-4-1106-preview.process.js +137 -0
  650. package/nodes/openai-gpt-4-1106-preview/openai-gpt-4-1106-preview.tests.json +80 -0
  651. package/nodes/openai-gpt-4-turbo/openai-gpt-4-turbo.config.json +210 -0
  652. package/nodes/openai-gpt-4-turbo/openai-gpt-4-turbo.process.js +137 -0
  653. package/nodes/openai-gpt-4-turbo/openai-gpt-4-turbo.tests.json +80 -0
  654. package/nodes/openai-gpt-4-turbo-preview/openai-gpt-4-turbo-preview.config.json +210 -0
  655. package/nodes/openai-gpt-4-turbo-preview/openai-gpt-4-turbo-preview.process.js +137 -0
  656. package/nodes/openai-gpt-4-turbo-preview/openai-gpt-4-turbo-preview.tests.json +80 -0
  657. package/nodes/openai-gpt-4o/openai-gpt-4o.config.json +224 -0
  658. package/nodes/openai-gpt-4o/openai-gpt-4o.process.js +139 -0
  659. package/nodes/openai-gpt-4o/openai-gpt-4o.tests.json +80 -0
  660. package/nodes/openai-gpt-4o-2024-05-13/openai-gpt-4o-2024-05-13.config.json +224 -0
  661. package/nodes/openai-gpt-4o-2024-05-13/openai-gpt-4o-2024-05-13.process.js +139 -0
  662. package/nodes/openai-gpt-4o-2024-05-13/openai-gpt-4o-2024-05-13.tests.json +80 -0
  663. package/nodes/openai-gpt-4o-2024-08-06/openai-gpt-4o-2024-08-06.config.json +224 -0
  664. package/nodes/openai-gpt-4o-2024-08-06/openai-gpt-4o-2024-08-06.process.js +139 -0
  665. package/nodes/openai-gpt-4o-2024-08-06/openai-gpt-4o-2024-08-06.tests.json +80 -0
  666. package/nodes/openai-gpt-4o-2024-11-20/openai-gpt-4o-2024-11-20.config.json +223 -0
  667. package/nodes/openai-gpt-4o-2024-11-20/openai-gpt-4o-2024-11-20.process.js +139 -0
  668. package/nodes/openai-gpt-4o-2024-11-20/openai-gpt-4o-2024-11-20.tests.json +80 -0
  669. package/nodes/openai-gpt-4o-mini/openai-gpt-4o-mini.config.json +224 -0
  670. package/nodes/openai-gpt-4o-mini/openai-gpt-4o-mini.process.js +139 -0
  671. package/nodes/openai-gpt-4o-mini/openai-gpt-4o-mini.tests.json +80 -0
  672. package/nodes/openai-gpt-4o-mini-2024-07-18/openai-gpt-4o-mini-2024-07-18.config.json +223 -0
  673. package/nodes/openai-gpt-4o-mini-2024-07-18/openai-gpt-4o-mini-2024-07-18.process.js +139 -0
  674. package/nodes/openai-gpt-4o-mini-2024-07-18/openai-gpt-4o-mini-2024-07-18.tests.json +80 -0
  675. package/nodes/openai-gpt-4o-mini-search-preview/openai-gpt-4o-mini-search-preview.config.json +101 -0
  676. package/nodes/openai-gpt-4o-mini-search-preview/openai-gpt-4o-mini-search-preview.process.js +53 -0
  677. package/nodes/openai-gpt-4o-mini-search-preview/openai-gpt-4o-mini-search-preview.tests.json +26 -0
  678. package/nodes/openai-gpt-4o-search-preview/openai-gpt-4o-search-preview.config.json +101 -0
  679. package/nodes/openai-gpt-4o-search-preview/openai-gpt-4o-search-preview.process.js +53 -0
  680. package/nodes/openai-gpt-4o-search-preview/openai-gpt-4o-search-preview.tests.json +26 -0
  681. package/nodes/openai-gpt-5/openai-gpt-5.config.json +170 -0
  682. package/nodes/openai-gpt-5/openai-gpt-5.process.js +138 -0
  683. package/nodes/openai-gpt-5/openai-gpt-5.tests.json +57 -0
  684. package/nodes/openai-gpt-5-1/openai-gpt-5-1.config.json +170 -0
  685. package/nodes/openai-gpt-5-1/openai-gpt-5-1.process.js +138 -0
  686. package/nodes/openai-gpt-5-1/openai-gpt-5-1.tests.json +57 -0
  687. package/nodes/openai-gpt-5-1-chat/openai-gpt-5-1-chat.config.json +148 -0
  688. package/nodes/openai-gpt-5-1-chat/openai-gpt-5-1-chat.process.js +136 -0
  689. package/nodes/openai-gpt-5-1-chat/openai-gpt-5-1-chat.tests.json +57 -0
  690. package/nodes/openai-gpt-5-1-codex/openai-gpt-5-1-codex.config.json +170 -0
  691. package/nodes/openai-gpt-5-1-codex/openai-gpt-5-1-codex.process.js +138 -0
  692. package/nodes/openai-gpt-5-1-codex/openai-gpt-5-1-codex.tests.json +57 -0
  693. package/nodes/openai-gpt-5-1-codex-max/openai-gpt-5-1-codex-max.config.json +170 -0
  694. package/nodes/openai-gpt-5-1-codex-max/openai-gpt-5-1-codex-max.process.js +138 -0
  695. package/nodes/openai-gpt-5-1-codex-max/openai-gpt-5-1-codex-max.tests.json +57 -0
  696. package/nodes/openai-gpt-5-1-codex-mini/openai-gpt-5-1-codex-mini.config.json +170 -0
  697. package/nodes/openai-gpt-5-1-codex-mini/openai-gpt-5-1-codex-mini.process.js +138 -0
  698. package/nodes/openai-gpt-5-1-codex-mini/openai-gpt-5-1-codex-mini.tests.json +57 -0
  699. package/nodes/openai-gpt-5-2/openai-gpt-5-2.config.json +170 -0
  700. package/nodes/openai-gpt-5-2/openai-gpt-5-2.process.js +138 -0
  701. package/nodes/openai-gpt-5-2/openai-gpt-5-2.tests.json +57 -0
  702. package/nodes/openai-gpt-5-2-chat/openai-gpt-5-2-chat.config.json +148 -0
  703. package/nodes/openai-gpt-5-2-chat/openai-gpt-5-2-chat.process.js +136 -0
  704. package/nodes/openai-gpt-5-2-chat/openai-gpt-5-2-chat.tests.json +57 -0
  705. package/nodes/openai-gpt-5-2-codex/openai-gpt-5-2-codex.config.json +170 -0
  706. package/nodes/openai-gpt-5-2-codex/openai-gpt-5-2-codex.process.js +138 -0
  707. package/nodes/openai-gpt-5-2-codex/openai-gpt-5-2-codex.tests.json +57 -0
  708. package/nodes/openai-gpt-5-2-pro/openai-gpt-5-2-pro.config.json +169 -0
  709. package/nodes/openai-gpt-5-2-pro/openai-gpt-5-2-pro.process.js +138 -0
  710. package/nodes/openai-gpt-5-2-pro/openai-gpt-5-2-pro.tests.json +57 -0
  711. package/nodes/openai-gpt-5-3-chat/openai-gpt-5-3-chat.config.json +148 -0
  712. package/nodes/openai-gpt-5-3-chat/openai-gpt-5-3-chat.process.js +136 -0
  713. package/nodes/openai-gpt-5-3-chat/openai-gpt-5-3-chat.tests.json +57 -0
  714. package/nodes/openai-gpt-5-3-codex/openai-gpt-5-3-codex.config.json +170 -0
  715. package/nodes/openai-gpt-5-3-codex/openai-gpt-5-3-codex.process.js +138 -0
  716. package/nodes/openai-gpt-5-3-codex/openai-gpt-5-3-codex.tests.json +57 -0
  717. package/nodes/openai-gpt-5-4/openai-gpt-5-4.config.json +170 -0
  718. package/nodes/openai-gpt-5-4/openai-gpt-5-4.process.js +138 -0
  719. package/nodes/openai-gpt-5-4/openai-gpt-5-4.tests.json +57 -0
  720. package/nodes/openai-gpt-5-4-image-2/openai-gpt-5-4-image-2.config.json +153 -0
  721. package/nodes/openai-gpt-5-4-image-2/openai-gpt-5-4-image-2.process.js +58 -0
  722. package/nodes/openai-gpt-5-4-image-2/openai-gpt-5-4-image-2.tests.json +26 -0
  723. package/nodes/openai-gpt-5-4-mini/openai-gpt-5-4-mini.config.json +170 -0
  724. package/nodes/openai-gpt-5-4-mini/openai-gpt-5-4-mini.process.js +138 -0
  725. package/nodes/openai-gpt-5-4-mini/openai-gpt-5-4-mini.tests.json +57 -0
  726. package/nodes/openai-gpt-5-4-nano/openai-gpt-5-4-nano.config.json +170 -0
  727. package/nodes/openai-gpt-5-4-nano/openai-gpt-5-4-nano.process.js +138 -0
  728. package/nodes/openai-gpt-5-4-nano/openai-gpt-5-4-nano.tests.json +57 -0
  729. package/nodes/openai-gpt-5-4-pro/openai-gpt-5-4-pro.config.json +170 -0
  730. package/nodes/openai-gpt-5-4-pro/openai-gpt-5-4-pro.process.js +138 -0
  731. package/nodes/openai-gpt-5-4-pro/openai-gpt-5-4-pro.tests.json +57 -0
  732. package/nodes/openai-gpt-5-5/openai-gpt-5-5.config.json +170 -0
  733. package/nodes/openai-gpt-5-5/openai-gpt-5-5.process.js +138 -0
  734. package/nodes/openai-gpt-5-5/openai-gpt-5-5.tests.json +57 -0
  735. package/nodes/openai-gpt-5-5-pro/openai-gpt-5-5-pro.config.json +169 -0
  736. package/nodes/openai-gpt-5-5-pro/openai-gpt-5-5-pro.process.js +138 -0
  737. package/nodes/openai-gpt-5-5-pro/openai-gpt-5-5-pro.tests.json +57 -0
  738. package/nodes/openai-gpt-5-chat/openai-gpt-5-chat.config.json +96 -0
  739. package/nodes/openai-gpt-5-chat/openai-gpt-5-chat.process.js +51 -0
  740. package/nodes/openai-gpt-5-chat/openai-gpt-5-chat.tests.json +26 -0
  741. package/nodes/openai-gpt-5-codex/openai-gpt-5-codex.config.json +169 -0
  742. package/nodes/openai-gpt-5-codex/openai-gpt-5-codex.process.js +138 -0
  743. package/nodes/openai-gpt-5-codex/openai-gpt-5-codex.tests.json +57 -0
  744. package/nodes/openai-gpt-5-image/openai-gpt-5-image.config.json +169 -0
  745. package/nodes/openai-gpt-5-image/openai-gpt-5-image.process.js +58 -0
  746. package/nodes/openai-gpt-5-image/openai-gpt-5-image.tests.json +44 -0
  747. package/nodes/openai-gpt-5-image-mini/openai-gpt-5-image-mini.config.json +169 -0
  748. package/nodes/openai-gpt-5-image-mini/openai-gpt-5-image-mini.process.js +58 -0
  749. package/nodes/openai-gpt-5-image-mini/openai-gpt-5-image-mini.tests.json +44 -0
  750. package/nodes/openai-gpt-5-mini/openai-gpt-5-mini.config.json +170 -0
  751. package/nodes/openai-gpt-5-mini/openai-gpt-5-mini.process.js +138 -0
  752. package/nodes/openai-gpt-5-mini/openai-gpt-5-mini.tests.json +57 -0
  753. package/nodes/openai-gpt-5-nano/openai-gpt-5-nano.config.json +170 -0
  754. package/nodes/openai-gpt-5-nano/openai-gpt-5-nano.process.js +138 -0
  755. package/nodes/openai-gpt-5-nano/openai-gpt-5-nano.tests.json +57 -0
  756. package/nodes/openai-gpt-5-pro/openai-gpt-5-pro.config.json +169 -0
  757. package/nodes/openai-gpt-5-pro/openai-gpt-5-pro.process.js +138 -0
  758. package/nodes/openai-gpt-5-pro/openai-gpt-5-pro.tests.json +57 -0
  759. package/nodes/openai-gpt-audio/openai-gpt-audio.config.json +210 -0
  760. package/nodes/openai-gpt-audio/openai-gpt-audio.process.js +137 -0
  761. package/nodes/openai-gpt-audio/openai-gpt-audio.tests.json +80 -0
  762. package/nodes/openai-gpt-audio-mini/openai-gpt-audio-mini.config.json +210 -0
  763. package/nodes/openai-gpt-audio-mini/openai-gpt-audio-mini.process.js +137 -0
  764. package/nodes/openai-gpt-audio-mini/openai-gpt-audio-mini.tests.json +80 -0
  765. package/nodes/openai-gpt-chat-latest/openai-gpt-chat-latest.config.json +194 -0
  766. package/nodes/openai-gpt-chat-latest/openai-gpt-chat-latest.process.js +137 -0
  767. package/nodes/openai-gpt-chat-latest/openai-gpt-chat-latest.tests.json +57 -0
  768. package/nodes/openai-gpt-oss-120b/openai-gpt-oss-120b.config.json +235 -0
  769. package/nodes/openai-gpt-oss-120b/openai-gpt-oss-120b.process.js +139 -0
  770. package/nodes/openai-gpt-oss-120b/openai-gpt-oss-120b.tests.json +80 -0
  771. package/nodes/openai-gpt-oss-120b-free/openai-gpt-oss-120b-free.config.json +176 -0
  772. package/nodes/openai-gpt-oss-120b-free/openai-gpt-oss-120b-free.process.js +138 -0
  773. package/nodes/openai-gpt-oss-120b-free/openai-gpt-oss-120b-free.tests.json +80 -0
  774. package/nodes/openai-gpt-oss-20b/openai-gpt-oss-20b.config.json +235 -0
  775. package/nodes/openai-gpt-oss-20b/openai-gpt-oss-20b.process.js +139 -0
  776. package/nodes/openai-gpt-oss-20b/openai-gpt-oss-20b.tests.json +80 -0
  777. package/nodes/openai-gpt-oss-20b-free/openai-gpt-oss-20b-free.config.json +176 -0
  778. package/nodes/openai-gpt-oss-20b-free/openai-gpt-oss-20b-free.process.js +138 -0
  779. package/nodes/openai-gpt-oss-20b-free/openai-gpt-oss-20b-free.tests.json +80 -0
  780. package/nodes/openai-gpt-oss-safeguard-20b/openai-gpt-oss-safeguard-20b.config.json +192 -0
  781. package/nodes/openai-gpt-oss-safeguard-20b/openai-gpt-oss-safeguard-20b.process.js +138 -0
  782. package/nodes/openai-gpt-oss-safeguard-20b/openai-gpt-oss-safeguard-20b.tests.json +80 -0
  783. package/nodes/openai-o1/openai-o1.config.json +169 -0
  784. package/nodes/openai-o1/openai-o1.process.js +138 -0
  785. package/nodes/openai-o1/openai-o1.tests.json +57 -0
  786. package/nodes/openai-o1-pro/openai-o1-pro.config.json +118 -0
  787. package/nodes/openai-o1-pro/openai-o1-pro.process.js +53 -0
  788. package/nodes/openai-o1-pro/openai-o1-pro.tests.json +26 -0
  789. package/nodes/openai-o3/openai-o3.config.json +169 -0
  790. package/nodes/openai-o3/openai-o3.process.js +138 -0
  791. package/nodes/openai-o3/openai-o3.tests.json +57 -0
  792. package/nodes/openai-o3-deep-research/openai-o3-deep-research.config.json +244 -0
  793. package/nodes/openai-o3-deep-research/openai-o3-deep-research.process.js +141 -0
  794. package/nodes/openai-o3-deep-research/openai-o3-deep-research.tests.json +80 -0
  795. package/nodes/openai-o3-mini/openai-o3-mini.config.json +169 -0
  796. package/nodes/openai-o3-mini/openai-o3-mini.process.js +138 -0
  797. package/nodes/openai-o3-mini/openai-o3-mini.tests.json +57 -0
  798. package/nodes/openai-o3-mini-high/openai-o3-mini-high.config.json +169 -0
  799. package/nodes/openai-o3-mini-high/openai-o3-mini-high.process.js +138 -0
  800. package/nodes/openai-o3-mini-high/openai-o3-mini-high.tests.json +57 -0
  801. package/nodes/openai-o3-pro/openai-o3-pro.config.json +169 -0
  802. package/nodes/openai-o3-pro/openai-o3-pro.process.js +138 -0
  803. package/nodes/openai-o3-pro/openai-o3-pro.tests.json +57 -0
  804. package/nodes/openai-o4-mini/openai-o4-mini.config.json +169 -0
  805. package/nodes/openai-o4-mini/openai-o4-mini.process.js +138 -0
  806. package/nodes/openai-o4-mini/openai-o4-mini.tests.json +57 -0
  807. package/nodes/openai-o4-mini-deep-research/openai-o4-mini-deep-research.config.json +244 -0
  808. package/nodes/openai-o4-mini-deep-research/openai-o4-mini-deep-research.process.js +141 -0
  809. package/nodes/openai-o4-mini-deep-research/openai-o4-mini-deep-research.tests.json +80 -0
  810. package/nodes/openai-o4-mini-high/openai-o4-mini-high.config.json +169 -0
  811. package/nodes/openai-o4-mini-high/openai-o4-mini-high.process.js +138 -0
  812. package/nodes/openai-o4-mini-high/openai-o4-mini-high.tests.json +57 -0
  813. package/nodes/openai-omni-moderation-latest/openai-omni-moderation-latest.config.json +183 -0
  814. package/nodes/openai-omni-moderation-latest/openai-omni-moderation-latest.process.js +93 -0
  815. package/nodes/openai-omni-moderation-latest/openai-omni-moderation-latest.tests.json +103 -0
  816. package/nodes/openai-text-embedding-3-large/openai-text-embedding-3-large.config.json +90 -0
  817. package/nodes/openai-text-embedding-3-large/openai-text-embedding-3-large.process.js +36 -0
  818. package/nodes/openai-text-embedding-3-large/openai-text-embedding-3-large.tests.json +48 -0
  819. package/nodes/openai-text-embedding-3-small/openai-text-embedding-3-small.config.json +90 -0
  820. package/nodes/openai-text-embedding-3-small/openai-text-embedding-3-small.process.js +36 -0
  821. package/nodes/openai-text-embedding-3-small/openai-text-embedding-3-small.tests.json +48 -0
  822. package/nodes/openai-text-embedding-ada-002/openai-text-embedding-ada-002.config.json +90 -0
  823. package/nodes/openai-text-embedding-ada-002/openai-text-embedding-ada-002.process.js +36 -0
  824. package/nodes/openai-text-embedding-ada-002/openai-text-embedding-ada-002.tests.json +48 -0
  825. package/nodes/or-gate/or-gate.config.json +30 -0
  826. package/nodes/or-gate/or-gate.process.js +10 -0
  827. package/nodes/or-gate/or-gate.tests.json +22 -0
  828. package/nodes/output-chat/output-chat.config.json +49 -0
  829. package/nodes/output-chat/output-chat.process.js +20 -0
  830. package/nodes/output-data/output-data.config.json +59 -0
  831. package/nodes/output-data/output-data.process.js +10 -0
  832. package/nodes/parse-url/parse-url.config.json +72 -0
  833. package/nodes/parse-url/parse-url.process.js +61 -0
  834. package/nodes/parse-url/parse-url.tests.json +121 -0
  835. package/nodes/perplexity-pplx-embed-v1-0-6b/perplexity-pplx-embed-v1-0-6b.config.json +90 -0
  836. package/nodes/perplexity-pplx-embed-v1-0-6b/perplexity-pplx-embed-v1-0-6b.process.js +36 -0
  837. package/nodes/perplexity-pplx-embed-v1-0-6b/perplexity-pplx-embed-v1-0-6b.tests.json +48 -0
  838. package/nodes/perplexity-pplx-embed-v1-4b/perplexity-pplx-embed-v1-4b.config.json +90 -0
  839. package/nodes/perplexity-pplx-embed-v1-4b/perplexity-pplx-embed-v1-4b.process.js +36 -0
  840. package/nodes/perplexity-pplx-embed-v1-4b/perplexity-pplx-embed-v1-4b.tests.json +48 -0
  841. package/nodes/perplexity-sonar/perplexity-sonar.config.json +125 -0
  842. package/nodes/perplexity-sonar/perplexity-sonar.process.js +53 -0
  843. package/nodes/perplexity-sonar/perplexity-sonar.tests.json +44 -0
  844. package/nodes/perplexity-sonar-deep-research/perplexity-sonar-deep-research.config.json +175 -0
  845. package/nodes/perplexity-sonar-deep-research/perplexity-sonar-deep-research.process.js +140 -0
  846. package/nodes/perplexity-sonar-deep-research/perplexity-sonar-deep-research.tests.json +80 -0
  847. package/nodes/perplexity-sonar-pro/perplexity-sonar-pro.config.json +125 -0
  848. package/nodes/perplexity-sonar-pro/perplexity-sonar-pro.process.js +53 -0
  849. package/nodes/perplexity-sonar-pro/perplexity-sonar-pro.tests.json +44 -0
  850. package/nodes/perplexity-sonar-pro-search/perplexity-sonar-pro-search.config.json +148 -0
  851. package/nodes/perplexity-sonar-pro-search/perplexity-sonar-pro-search.process.js +55 -0
  852. package/nodes/perplexity-sonar-pro-search/perplexity-sonar-pro-search.tests.json +44 -0
  853. package/nodes/perplexity-sonar-reasoning-pro/perplexity-sonar-reasoning-pro.config.json +175 -0
  854. package/nodes/perplexity-sonar-reasoning-pro/perplexity-sonar-reasoning-pro.process.js +140 -0
  855. package/nodes/perplexity-sonar-reasoning-pro/perplexity-sonar-reasoning-pro.tests.json +80 -0
  856. package/nodes/pinecone-query/pinecone-query.config.json +69 -0
  857. package/nodes/pinecone-query/pinecone-query.process.js +22 -0
  858. package/nodes/pinecone-query/pinecone-query.tests.json +12 -0
  859. package/nodes/pinecone-upsert/pinecone-upsert.config.json +32 -0
  860. package/nodes/pinecone-upsert/pinecone-upsert.process.js +15 -0
  861. package/nodes/pinecone-upsert/pinecone-upsert.tests.json +13 -0
  862. package/nodes/power/power.config.json +30 -0
  863. package/nodes/power/power.process.js +9 -0
  864. package/nodes/power/power.tests.json +42 -0
  865. package/nodes/quartiles/quartiles.config.json +41 -0
  866. package/nodes/quartiles/quartiles.process.js +62 -0
  867. package/nodes/quartiles/quartiles.tests.json +69 -0
  868. package/nodes/query-knowledge-base/query-knowledge-base.config.json +81 -0
  869. package/nodes/query-knowledge-base/query-knowledge-base.process.js +35 -0
  870. package/nodes/qwen-qwen-2-5-72b-instruct/qwen-qwen-2-5-72b-instruct.config.json +191 -0
  871. package/nodes/qwen-qwen-2-5-72b-instruct/qwen-qwen-2-5-72b-instruct.process.js +136 -0
  872. package/nodes/qwen-qwen-2-5-72b-instruct/qwen-qwen-2-5-72b-instruct.tests.json +80 -0
  873. package/nodes/qwen-qwen-2-5-7b-instruct/qwen-qwen-2-5-7b-instruct.config.json +159 -0
  874. package/nodes/qwen-qwen-2-5-7b-instruct/qwen-qwen-2-5-7b-instruct.process.js +136 -0
  875. package/nodes/qwen-qwen-2-5-7b-instruct/qwen-qwen-2-5-7b-instruct.tests.json +80 -0
  876. package/nodes/qwen-qwen-2-5-coder-32b-instruct/qwen-qwen-2-5-coder-32b-instruct.config.json +159 -0
  877. package/nodes/qwen-qwen-2-5-coder-32b-instruct/qwen-qwen-2-5-coder-32b-instruct.process.js +136 -0
  878. package/nodes/qwen-qwen-2-5-coder-32b-instruct/qwen-qwen-2-5-coder-32b-instruct.tests.json +80 -0
  879. package/nodes/qwen-qwen-plus/qwen-qwen-plus.config.json +170 -0
  880. package/nodes/qwen-qwen-plus/qwen-qwen-plus.process.js +136 -0
  881. package/nodes/qwen-qwen-plus/qwen-qwen-plus.tests.json +80 -0
  882. package/nodes/qwen-qwen-plus-2025-07-28/qwen-qwen-plus-2025-07-28.config.json +171 -0
  883. package/nodes/qwen-qwen-plus-2025-07-28/qwen-qwen-plus-2025-07-28.process.js +136 -0
  884. package/nodes/qwen-qwen-plus-2025-07-28/qwen-qwen-plus-2025-07-28.tests.json +80 -0
  885. package/nodes/qwen-qwen-plus-2025-07-28-thinking/qwen-qwen-plus-2025-07-28-thinking.config.json +193 -0
  886. package/nodes/qwen-qwen-plus-2025-07-28-thinking/qwen-qwen-plus-2025-07-28-thinking.process.js +138 -0
  887. package/nodes/qwen-qwen-plus-2025-07-28-thinking/qwen-qwen-plus-2025-07-28-thinking.tests.json +80 -0
  888. package/nodes/qwen-qwen2-5-vl-72b-instruct/qwen-qwen2-5-vl-72b-instruct.config.json +139 -0
  889. package/nodes/qwen-qwen2-5-vl-72b-instruct/qwen-qwen2-5-vl-72b-instruct.process.js +51 -0
  890. package/nodes/qwen-qwen2-5-vl-72b-instruct/qwen-qwen2-5-vl-72b-instruct.tests.json +44 -0
  891. package/nodes/qwen-qwen3-14b/qwen-qwen3-14b.config.json +235 -0
  892. package/nodes/qwen-qwen3-14b/qwen-qwen3-14b.process.js +139 -0
  893. package/nodes/qwen-qwen3-14b/qwen-qwen3-14b.tests.json +80 -0
  894. package/nodes/qwen-qwen3-235b-a22b/qwen-qwen3-235b-a22b.config.json +192 -0
  895. package/nodes/qwen-qwen3-235b-a22b/qwen-qwen3-235b-a22b.process.js +138 -0
  896. package/nodes/qwen-qwen3-235b-a22b/qwen-qwen3-235b-a22b.tests.json +80 -0
  897. package/nodes/qwen-qwen3-235b-a22b-2507/qwen-qwen3-235b-a22b-2507.config.json +191 -0
  898. package/nodes/qwen-qwen3-235b-a22b-2507/qwen-qwen3-235b-a22b-2507.process.js +136 -0
  899. package/nodes/qwen-qwen3-235b-a22b-2507/qwen-qwen3-235b-a22b-2507.tests.json +80 -0
  900. package/nodes/qwen-qwen3-235b-a22b-thinking-2507/qwen-qwen3-235b-a22b-thinking-2507.config.json +213 -0
  901. package/nodes/qwen-qwen3-235b-a22b-thinking-2507/qwen-qwen3-235b-a22b-thinking-2507.process.js +138 -0
  902. package/nodes/qwen-qwen3-235b-a22b-thinking-2507/qwen-qwen3-235b-a22b-thinking-2507.tests.json +80 -0
  903. package/nodes/qwen-qwen3-30b-a3b/qwen-qwen3-30b-a3b.config.json +235 -0
  904. package/nodes/qwen-qwen3-30b-a3b/qwen-qwen3-30b-a3b.process.js +139 -0
  905. package/nodes/qwen-qwen3-30b-a3b/qwen-qwen3-30b-a3b.tests.json +80 -0
  906. package/nodes/qwen-qwen3-30b-a3b-instruct-2507/qwen-qwen3-30b-a3b-instruct-2507.config.json +191 -0
  907. package/nodes/qwen-qwen3-30b-a3b-instruct-2507/qwen-qwen3-30b-a3b-instruct-2507.process.js +136 -0
  908. package/nodes/qwen-qwen3-30b-a3b-instruct-2507/qwen-qwen3-30b-a3b-instruct-2507.tests.json +80 -0
  909. package/nodes/qwen-qwen3-30b-a3b-thinking-2507/qwen-qwen3-30b-a3b-thinking-2507.config.json +213 -0
  910. package/nodes/qwen-qwen3-30b-a3b-thinking-2507/qwen-qwen3-30b-a3b-thinking-2507.process.js +138 -0
  911. package/nodes/qwen-qwen3-30b-a3b-thinking-2507/qwen-qwen3-30b-a3b-thinking-2507.tests.json +80 -0
  912. package/nodes/qwen-qwen3-32b/qwen-qwen3-32b.config.json +213 -0
  913. package/nodes/qwen-qwen3-32b/qwen-qwen3-32b.process.js +138 -0
  914. package/nodes/qwen-qwen3-32b/qwen-qwen3-32b.tests.json +80 -0
  915. package/nodes/qwen-qwen3-5-122b-a10b/qwen-qwen3-5-122b-a10b.config.json +235 -0
  916. package/nodes/qwen-qwen3-5-122b-a10b/qwen-qwen3-5-122b-a10b.process.js +139 -0
  917. package/nodes/qwen-qwen3-5-122b-a10b/qwen-qwen3-5-122b-a10b.tests.json +80 -0
  918. package/nodes/qwen-qwen3-5-27b/qwen-qwen3-5-27b.config.json +235 -0
  919. package/nodes/qwen-qwen3-5-27b/qwen-qwen3-5-27b.process.js +139 -0
  920. package/nodes/qwen-qwen3-5-27b/qwen-qwen3-5-27b.tests.json +80 -0
  921. package/nodes/qwen-qwen3-5-35b-a3b/qwen-qwen3-5-35b-a3b.config.json +235 -0
  922. package/nodes/qwen-qwen3-5-35b-a3b/qwen-qwen3-5-35b-a3b.process.js +139 -0
  923. package/nodes/qwen-qwen3-5-35b-a3b/qwen-qwen3-5-35b-a3b.tests.json +80 -0
  924. package/nodes/qwen-qwen3-5-397b-a17b/qwen-qwen3-5-397b-a17b.config.json +235 -0
  925. package/nodes/qwen-qwen3-5-397b-a17b/qwen-qwen3-5-397b-a17b.process.js +139 -0
  926. package/nodes/qwen-qwen3-5-397b-a17b/qwen-qwen3-5-397b-a17b.tests.json +80 -0
  927. package/nodes/qwen-qwen3-5-9b/qwen-qwen3-5-9b.config.json +235 -0
  928. package/nodes/qwen-qwen3-5-9b/qwen-qwen3-5-9b.process.js +139 -0
  929. package/nodes/qwen-qwen3-5-9b/qwen-qwen3-5-9b.tests.json +80 -0
  930. package/nodes/qwen-qwen3-5-flash-02-23/qwen-qwen3-5-flash-02-23.config.json +193 -0
  931. package/nodes/qwen-qwen3-5-flash-02-23/qwen-qwen3-5-flash-02-23.process.js +138 -0
  932. package/nodes/qwen-qwen3-5-flash-02-23/qwen-qwen3-5-flash-02-23.tests.json +80 -0
  933. package/nodes/qwen-qwen3-5-plus-02-15/qwen-qwen3-5-plus-02-15.config.json +193 -0
  934. package/nodes/qwen-qwen3-5-plus-02-15/qwen-qwen3-5-plus-02-15.process.js +138 -0
  935. package/nodes/qwen-qwen3-5-plus-02-15/qwen-qwen3-5-plus-02-15.tests.json +80 -0
  936. package/nodes/qwen-qwen3-5-plus-20260420/qwen-qwen3-5-plus-20260420.config.json +193 -0
  937. package/nodes/qwen-qwen3-5-plus-20260420/qwen-qwen3-5-plus-20260420.process.js +138 -0
  938. package/nodes/qwen-qwen3-5-plus-20260420/qwen-qwen3-5-plus-20260420.tests.json +80 -0
  939. package/nodes/qwen-qwen3-6-27b/qwen-qwen3-6-27b.config.json +235 -0
  940. package/nodes/qwen-qwen3-6-27b/qwen-qwen3-6-27b.process.js +139 -0
  941. package/nodes/qwen-qwen3-6-27b/qwen-qwen3-6-27b.tests.json +80 -0
  942. package/nodes/qwen-qwen3-6-35b-a3b/qwen-qwen3-6-35b-a3b.config.json +235 -0
  943. package/nodes/qwen-qwen3-6-35b-a3b/qwen-qwen3-6-35b-a3b.process.js +139 -0
  944. package/nodes/qwen-qwen3-6-35b-a3b/qwen-qwen3-6-35b-a3b.tests.json +80 -0
  945. package/nodes/qwen-qwen3-6-flash/qwen-qwen3-6-flash.config.json +193 -0
  946. package/nodes/qwen-qwen3-6-flash/qwen-qwen3-6-flash.process.js +138 -0
  947. package/nodes/qwen-qwen3-6-flash/qwen-qwen3-6-flash.tests.json +80 -0
  948. package/nodes/qwen-qwen3-6-max-preview/qwen-qwen3-6-max-preview.config.json +215 -0
  949. package/nodes/qwen-qwen3-6-max-preview/qwen-qwen3-6-max-preview.process.js +139 -0
  950. package/nodes/qwen-qwen3-6-max-preview/qwen-qwen3-6-max-preview.tests.json +80 -0
  951. package/nodes/qwen-qwen3-6-plus/qwen-qwen3-6-plus.config.json +193 -0
  952. package/nodes/qwen-qwen3-6-plus/qwen-qwen3-6-plus.process.js +138 -0
  953. package/nodes/qwen-qwen3-6-plus/qwen-qwen3-6-plus.tests.json +80 -0
  954. package/nodes/qwen-qwen3-7-max/qwen-qwen3-7-max.config.json +215 -0
  955. package/nodes/qwen-qwen3-7-max/qwen-qwen3-7-max.process.js +139 -0
  956. package/nodes/qwen-qwen3-7-max/qwen-qwen3-7-max.tests.json +80 -0
  957. package/nodes/qwen-qwen3-7-plus/qwen-qwen3-7-plus.config.json +215 -0
  958. package/nodes/qwen-qwen3-7-plus/qwen-qwen3-7-plus.process.js +139 -0
  959. package/nodes/qwen-qwen3-7-plus/qwen-qwen3-7-plus.tests.json +80 -0
  960. package/nodes/qwen-qwen3-8b/qwen-qwen3-8b.config.json +213 -0
  961. package/nodes/qwen-qwen3-8b/qwen-qwen3-8b.process.js +138 -0
  962. package/nodes/qwen-qwen3-8b/qwen-qwen3-8b.tests.json +80 -0
  963. package/nodes/qwen-qwen3-coder/qwen-qwen3-coder.config.json +191 -0
  964. package/nodes/qwen-qwen3-coder/qwen-qwen3-coder.process.js +136 -0
  965. package/nodes/qwen-qwen3-coder/qwen-qwen3-coder.tests.json +80 -0
  966. package/nodes/qwen-qwen3-coder-30b-a3b-instruct/qwen-qwen3-coder-30b-a3b-instruct.config.json +189 -0
  967. package/nodes/qwen-qwen3-coder-30b-a3b-instruct/qwen-qwen3-coder-30b-a3b-instruct.process.js +136 -0
  968. package/nodes/qwen-qwen3-coder-30b-a3b-instruct/qwen-qwen3-coder-30b-a3b-instruct.tests.json +80 -0
  969. package/nodes/qwen-qwen3-coder-flash/qwen-qwen3-coder-flash.config.json +170 -0
  970. package/nodes/qwen-qwen3-coder-flash/qwen-qwen3-coder-flash.process.js +136 -0
  971. package/nodes/qwen-qwen3-coder-flash/qwen-qwen3-coder-flash.tests.json +80 -0
  972. package/nodes/qwen-qwen3-coder-free/qwen-qwen3-coder-free.config.json +171 -0
  973. package/nodes/qwen-qwen3-coder-free/qwen-qwen3-coder-free.process.js +136 -0
  974. package/nodes/qwen-qwen3-coder-free/qwen-qwen3-coder-free.tests.json +80 -0
  975. package/nodes/qwen-qwen3-coder-next/qwen-qwen3-coder-next.config.json +191 -0
  976. package/nodes/qwen-qwen3-coder-next/qwen-qwen3-coder-next.process.js +136 -0
  977. package/nodes/qwen-qwen3-coder-next/qwen-qwen3-coder-next.tests.json +80 -0
  978. package/nodes/qwen-qwen3-coder-plus/qwen-qwen3-coder-plus.config.json +171 -0
  979. package/nodes/qwen-qwen3-coder-plus/qwen-qwen3-coder-plus.process.js +136 -0
  980. package/nodes/qwen-qwen3-coder-plus/qwen-qwen3-coder-plus.tests.json +80 -0
  981. package/nodes/qwen-qwen3-embedding-4b/qwen-qwen3-embedding-4b.config.json +90 -0
  982. package/nodes/qwen-qwen3-embedding-4b/qwen-qwen3-embedding-4b.process.js +36 -0
  983. package/nodes/qwen-qwen3-embedding-4b/qwen-qwen3-embedding-4b.tests.json +48 -0
  984. package/nodes/qwen-qwen3-embedding-8b/qwen-qwen3-embedding-8b.config.json +90 -0
  985. package/nodes/qwen-qwen3-embedding-8b/qwen-qwen3-embedding-8b.process.js +36 -0
  986. package/nodes/qwen-qwen3-embedding-8b/qwen-qwen3-embedding-8b.tests.json +48 -0
  987. package/nodes/qwen-qwen3-max/qwen-qwen3-max.config.json +170 -0
  988. package/nodes/qwen-qwen3-max/qwen-qwen3-max.process.js +136 -0
  989. package/nodes/qwen-qwen3-max/qwen-qwen3-max.tests.json +80 -0
  990. package/nodes/qwen-qwen3-max-thinking/qwen-qwen3-max-thinking.config.json +193 -0
  991. package/nodes/qwen-qwen3-max-thinking/qwen-qwen3-max-thinking.process.js +138 -0
  992. package/nodes/qwen-qwen3-max-thinking/qwen-qwen3-max-thinking.tests.json +80 -0
  993. package/nodes/qwen-qwen3-next-80b-a3b-instruct/qwen-qwen3-next-80b-a3b-instruct.config.json +191 -0
  994. package/nodes/qwen-qwen3-next-80b-a3b-instruct/qwen-qwen3-next-80b-a3b-instruct.process.js +136 -0
  995. package/nodes/qwen-qwen3-next-80b-a3b-instruct/qwen-qwen3-next-80b-a3b-instruct.tests.json +80 -0
  996. package/nodes/qwen-qwen3-next-80b-a3b-instruct-free/qwen-qwen3-next-80b-a3b-instruct-free.config.json +180 -0
  997. package/nodes/qwen-qwen3-next-80b-a3b-instruct-free/qwen-qwen3-next-80b-a3b-instruct-free.process.js +136 -0
  998. package/nodes/qwen-qwen3-next-80b-a3b-instruct-free/qwen-qwen3-next-80b-a3b-instruct-free.tests.json +80 -0
  999. package/nodes/qwen-qwen3-next-80b-a3b-thinking/qwen-qwen3-next-80b-a3b-thinking.config.json +213 -0
  1000. package/nodes/qwen-qwen3-next-80b-a3b-thinking/qwen-qwen3-next-80b-a3b-thinking.process.js +138 -0
  1001. package/nodes/qwen-qwen3-next-80b-a3b-thinking/qwen-qwen3-next-80b-a3b-thinking.tests.json +80 -0
  1002. package/nodes/qwen-qwen3-vl-235b-a22b-instruct/qwen-qwen3-vl-235b-a22b-instruct.config.json +191 -0
  1003. package/nodes/qwen-qwen3-vl-235b-a22b-instruct/qwen-qwen3-vl-235b-a22b-instruct.process.js +136 -0
  1004. package/nodes/qwen-qwen3-vl-235b-a22b-instruct/qwen-qwen3-vl-235b-a22b-instruct.tests.json +80 -0
  1005. package/nodes/qwen-qwen3-vl-235b-a22b-thinking/qwen-qwen3-vl-235b-a22b-thinking.config.json +210 -0
  1006. package/nodes/qwen-qwen3-vl-235b-a22b-thinking/qwen-qwen3-vl-235b-a22b-thinking.process.js +138 -0
  1007. package/nodes/qwen-qwen3-vl-235b-a22b-thinking/qwen-qwen3-vl-235b-a22b-thinking.tests.json +80 -0
  1008. package/nodes/qwen-qwen3-vl-30b-a3b-instruct/qwen-qwen3-vl-30b-a3b-instruct.config.json +191 -0
  1009. package/nodes/qwen-qwen3-vl-30b-a3b-instruct/qwen-qwen3-vl-30b-a3b-instruct.process.js +136 -0
  1010. package/nodes/qwen-qwen3-vl-30b-a3b-instruct/qwen-qwen3-vl-30b-a3b-instruct.tests.json +80 -0
  1011. package/nodes/qwen-qwen3-vl-30b-a3b-thinking/qwen-qwen3-vl-30b-a3b-thinking.config.json +211 -0
  1012. package/nodes/qwen-qwen3-vl-30b-a3b-thinking/qwen-qwen3-vl-30b-a3b-thinking.process.js +138 -0
  1013. package/nodes/qwen-qwen3-vl-30b-a3b-thinking/qwen-qwen3-vl-30b-a3b-thinking.tests.json +80 -0
  1014. package/nodes/qwen-qwen3-vl-32b-instruct/qwen-qwen3-vl-32b-instruct.config.json +170 -0
  1015. package/nodes/qwen-qwen3-vl-32b-instruct/qwen-qwen3-vl-32b-instruct.process.js +136 -0
  1016. package/nodes/qwen-qwen3-vl-32b-instruct/qwen-qwen3-vl-32b-instruct.tests.json +80 -0
  1017. package/nodes/qwen-qwen3-vl-8b-instruct/qwen-qwen3-vl-8b-instruct.config.json +191 -0
  1018. package/nodes/qwen-qwen3-vl-8b-instruct/qwen-qwen3-vl-8b-instruct.process.js +136 -0
  1019. package/nodes/qwen-qwen3-vl-8b-instruct/qwen-qwen3-vl-8b-instruct.tests.json +80 -0
  1020. package/nodes/qwen-qwen3-vl-8b-thinking/qwen-qwen3-vl-8b-thinking.config.json +193 -0
  1021. package/nodes/qwen-qwen3-vl-8b-thinking/qwen-qwen3-vl-8b-thinking.process.js +138 -0
  1022. package/nodes/qwen-qwen3-vl-8b-thinking/qwen-qwen3-vl-8b-thinking.tests.json +80 -0
  1023. package/nodes/random-error/random-error.config.json +50 -0
  1024. package/nodes/random-error/random-error.process.js +25 -0
  1025. package/nodes/random-error/random-error.tests.json +38 -0
  1026. package/nodes/random-number/random-number.config.json +66 -0
  1027. package/nodes/random-number/random-number.process.js +67 -0
  1028. package/nodes/random-number/random-number.tests.json +59 -0
  1029. package/nodes/range/range.config.json +35 -0
  1030. package/nodes/range/range.process.js +22 -0
  1031. package/nodes/range/range.tests.json +68 -0
  1032. package/nodes/read-website/read-website.config.json +71 -0
  1033. package/nodes/remote-mcp-tool/remote-mcp-tool.config.json +21 -0
  1034. package/nodes/resend-send-email/resend-send-email.config.json +74 -0
  1035. package/nodes/resend-send-email/resend-send-email.process.js +26 -0
  1036. package/nodes/resend-send-email/resend-send-email.tests.json +14 -0
  1037. package/nodes/response-format/response-format.config.json +62 -0
  1038. package/nodes/response-format/response-format.process.js +17 -0
  1039. package/nodes/response-format/response-format.tests.json +233 -0
  1040. package/nodes/round/round.config.json +23 -0
  1041. package/nodes/round/round.process.js +8 -0
  1042. package/nodes/round/round.tests.json +38 -0
  1043. package/nodes/route/route.config.json +38 -0
  1044. package/nodes/route/route.process.js +16 -0
  1045. package/nodes/route/route.tests.json +90 -0
  1046. package/nodes/search-internet/search-internet.config.json +71 -0
  1047. package/nodes/self-healing-error/self-healing-error.config.json +43 -0
  1048. package/nodes/self-healing-error/self-healing-error.process.js +31 -0
  1049. package/nodes/self-healing-error/self-healing-error.tests.json +34 -0
  1050. package/nodes/semantic-search/semantic-search.config.json +75 -0
  1051. package/nodes/semantic-search/semantic-search.process.js +70 -0
  1052. package/nodes/sendgrid-send-email/sendgrid-send-email.config.json +81 -0
  1053. package/nodes/sendgrid-send-email/sendgrid-send-email.process.js +43 -0
  1054. package/nodes/sendgrid-send-email/sendgrid-send-email.tests.json +41 -0
  1055. package/nodes/sentence-transformers-all-minilm-l12-v2/sentence-transformers-all-minilm-l12-v2.config.json +90 -0
  1056. package/nodes/sentence-transformers-all-minilm-l12-v2/sentence-transformers-all-minilm-l12-v2.process.js +36 -0
  1057. package/nodes/sentence-transformers-all-minilm-l12-v2/sentence-transformers-all-minilm-l12-v2.tests.json +48 -0
  1058. package/nodes/sentence-transformers-all-minilm-l6-v2/sentence-transformers-all-minilm-l6-v2.config.json +90 -0
  1059. package/nodes/sentence-transformers-all-minilm-l6-v2/sentence-transformers-all-minilm-l6-v2.process.js +36 -0
  1060. package/nodes/sentence-transformers-all-minilm-l6-v2/sentence-transformers-all-minilm-l6-v2.tests.json +48 -0
  1061. package/nodes/sentence-transformers-all-mpnet-base-v2/sentence-transformers-all-mpnet-base-v2.config.json +90 -0
  1062. package/nodes/sentence-transformers-all-mpnet-base-v2/sentence-transformers-all-mpnet-base-v2.process.js +36 -0
  1063. package/nodes/sentence-transformers-all-mpnet-base-v2/sentence-transformers-all-mpnet-base-v2.tests.json +48 -0
  1064. package/nodes/sentence-transformers-multi-qa-mpnet-base-dot-v1/sentence-transformers-multi-qa-mpnet-base-dot-v1.config.json +90 -0
  1065. package/nodes/sentence-transformers-multi-qa-mpnet-base-dot-v1/sentence-transformers-multi-qa-mpnet-base-dot-v1.process.js +36 -0
  1066. package/nodes/sentence-transformers-multi-qa-mpnet-base-dot-v1/sentence-transformers-multi-qa-mpnet-base-dot-v1.tests.json +48 -0
  1067. package/nodes/sentence-transformers-paraphrase-minilm-l6-v2/sentence-transformers-paraphrase-minilm-l6-v2.config.json +90 -0
  1068. package/nodes/sentence-transformers-paraphrase-minilm-l6-v2/sentence-transformers-paraphrase-minilm-l6-v2.process.js +36 -0
  1069. package/nodes/sentence-transformers-paraphrase-minilm-l6-v2/sentence-transformers-paraphrase-minilm-l6-v2.tests.json +48 -0
  1070. package/nodes/set-object-property/set-object-property.config.json +70 -0
  1071. package/nodes/set-object-property/set-object-property.process.js +55 -0
  1072. package/nodes/set-object-property/set-object-property.tests.json +173 -0
  1073. package/nodes/simple-agent/simple-agent.config.json +121 -0
  1074. package/nodes/simple-agent/simple-agent.process.js +121 -0
  1075. package/nodes/simple-agent/simple-agent.tests.json +28 -0
  1076. package/nodes/slack-send-message/slack-send-message.config.json +51 -0
  1077. package/nodes/slack-send-message/slack-send-message.process.js +19 -0
  1078. package/nodes/slack-send-message/slack-send-message.tests.json +13 -0
  1079. package/nodes/split-into-chunks/split-into-chunks.config.json +54 -0
  1080. package/nodes/split-into-chunks/split-into-chunks.process.js +42 -0
  1081. package/nodes/split-into-chunks/split-into-chunks.tests.json +74 -0
  1082. package/nodes/standard-deviation/standard-deviation.config.json +29 -0
  1083. package/nodes/standard-deviation/standard-deviation.process.js +35 -0
  1084. package/nodes/standard-deviation/standard-deviation.tests.json +49 -0
  1085. package/nodes/string/string.config.json +39 -0
  1086. package/nodes/string/string.process.js +14 -0
  1087. package/nodes/string/string.tests.json +56 -0
  1088. package/nodes/string-case/string-case.config.json +33 -0
  1089. package/nodes/string-case/string-case.process.js +46 -0
  1090. package/nodes/string-case/string-case.tests.json +44 -0
  1091. package/nodes/string-concat/string-concat.config.json +30 -0
  1092. package/nodes/string-concat/string-concat.process.js +9 -0
  1093. package/nodes/string-concat/string-concat.tests.json +12 -0
  1094. package/nodes/string-contains/string-contains.config.json +45 -0
  1095. package/nodes/string-contains/string-contains.process.js +19 -0
  1096. package/nodes/string-contains/string-contains.tests.json +58 -0
  1097. package/nodes/string-length/string-length.config.json +23 -0
  1098. package/nodes/string-length/string-length.process.js +5 -0
  1099. package/nodes/string-length/string-length.tests.json +22 -0
  1100. package/nodes/string-match/string-match.config.json +58 -0
  1101. package/nodes/string-match/string-match.process.js +32 -0
  1102. package/nodes/string-match/string-match.tests.json +82 -0
  1103. package/nodes/string-replace/string-replace.config.json +53 -0
  1104. package/nodes/string-replace/string-replace.process.js +19 -0
  1105. package/nodes/string-replace/string-replace.tests.json +62 -0
  1106. package/nodes/string-split/string-split.config.json +39 -0
  1107. package/nodes/string-split/string-split.process.js +12 -0
  1108. package/nodes/string-split/string-split.tests.json +26 -0
  1109. package/nodes/string-substring/string-substring.config.json +37 -0
  1110. package/nodes/string-substring/string-substring.process.js +10 -0
  1111. package/nodes/string-substring/string-substring.tests.json +44 -0
  1112. package/nodes/string-template/string-template.config.json +39 -0
  1113. package/nodes/string-template/string-template.process.js +15 -0
  1114. package/nodes/string-template/string-template.tests.json +63 -0
  1115. package/nodes/string-trim/string-trim.config.json +40 -0
  1116. package/nodes/string-trim/string-trim.process.js +22 -0
  1117. package/nodes/string-trim/string-trim.tests.json +38 -0
  1118. package/nodes/strip-html/strip-html.config.json +40 -0
  1119. package/nodes/strip-html/strip-html.process.js +61 -0
  1120. package/nodes/strip-html/strip-html.tests.json +94 -0
  1121. package/nodes/stripe-list-customers/stripe-list-customers.config.json +52 -0
  1122. package/nodes/stripe-list-customers/stripe-list-customers.process.js +18 -0
  1123. package/nodes/stripe-list-customers/stripe-list-customers.tests.json +24 -0
  1124. package/nodes/subtract/subtract.config.json +31 -0
  1125. package/nodes/subtract/subtract.process.js +15 -0
  1126. package/nodes/subtract/subtract.tests.json +41 -0
  1127. package/nodes/supabase-query/supabase-query.config.json +67 -0
  1128. package/nodes/supabase-query/supabase-query.process.js +21 -0
  1129. package/nodes/supabase-query/supabase-query.tests.json +14 -0
  1130. package/nodes/switch/switch.config.json +59 -0
  1131. package/nodes/switch/switch.process.js +40 -0
  1132. package/nodes/switch/switch.tests.json +85 -0
  1133. package/nodes/system-prompt/system-prompt.config.json +54 -0
  1134. package/nodes/system-prompt/system-prompt.process.js +62 -0
  1135. package/nodes/temperature/temperature.config.json +46 -0
  1136. package/nodes/temperature/temperature.process.js +16 -0
  1137. package/nodes/temperature/temperature.tests.json +47 -0
  1138. package/nodes/text-scratch-pad/text-scratch-pad.config.json +63 -0
  1139. package/nodes/text-scratch-pad/text-scratch-pad.process.js +40 -0
  1140. package/nodes/text-scratch-pad/text-scratch-pad.tests.json +131 -0
  1141. package/nodes/text-to-conversation/text-to-conversation.config.json +46 -0
  1142. package/nodes/text-to-conversation/text-to-conversation.process.js +50 -0
  1143. package/nodes/text-to-conversation/text-to-conversation.tests.json +132 -0
  1144. package/nodes/thenlper-gte-base/thenlper-gte-base.config.json +90 -0
  1145. package/nodes/thenlper-gte-base/thenlper-gte-base.process.js +36 -0
  1146. package/nodes/thenlper-gte-base/thenlper-gte-base.tests.json +48 -0
  1147. package/nodes/thenlper-gte-large/thenlper-gte-large.config.json +90 -0
  1148. package/nodes/thenlper-gte-large/thenlper-gte-large.process.js +36 -0
  1149. package/nodes/thenlper-gte-large/thenlper-gte-large.tests.json +48 -0
  1150. package/nodes/throw-error/throw-error.config.json +36 -0
  1151. package/nodes/throw-error/throw-error.process.js +24 -0
  1152. package/nodes/throw-error/throw-error.tests.json +44 -0
  1153. package/nodes/time-adder/time-adder.config.json +81 -0
  1154. package/nodes/time-adder/time-adder.process.js +111 -0
  1155. package/nodes/time-adder/time-adder.tests.json +158 -0
  1156. package/nodes/timestamp/timestamp.config.json +45 -0
  1157. package/nodes/timestamp/timestamp.process.js +71 -0
  1158. package/nodes/timestamp/timestamp.tests.json +90 -0
  1159. package/nodes/token-count/token-count.config.json +24 -0
  1160. package/nodes/token-count/token-count.process.js +74 -0
  1161. package/nodes/token-count/token-count.tests.json +163 -0
  1162. package/nodes/tokenizer/tokenizer.config.json +38 -0
  1163. package/nodes/tokenizer/tokenizer.process.js +74 -0
  1164. package/nodes/tokenizer/tokenizer.tests.json +129 -0
  1165. package/nodes/tool/tool.config.json +60 -0
  1166. package/nodes/tool/tool.process.js +26 -0
  1167. package/nodes/tool-cleanup/tool-cleanup.config.json +30 -0
  1168. package/nodes/tool-cleanup/tool-cleanup.process.js +99 -0
  1169. package/nodes/tool-cleanup/tool-cleanup.tests.json +160 -0
  1170. package/nodes/truncate-by-tokens/truncate-by-tokens.config.json +57 -0
  1171. package/nodes/truncate-by-tokens/truncate-by-tokens.process.js +72 -0
  1172. package/nodes/truncate-by-tokens/truncate-by-tokens.tests.json +84 -0
  1173. package/nodes/truncate-by-tokens-from-start/truncate-by-tokens-from-start.config.json +57 -0
  1174. package/nodes/truncate-by-tokens-from-start/truncate-by-tokens-from-start.process.js +72 -0
  1175. package/nodes/truncate-by-tokens-from-start/truncate-by-tokens-from-start.tests.json +84 -0
  1176. package/nodes/truncate-by-tokens-preserve-system/truncate-by-tokens-preserve-system.config.json +71 -0
  1177. package/nodes/truncate-by-tokens-preserve-system/truncate-by-tokens-preserve-system.process.js +130 -0
  1178. package/nodes/truncate-by-tokens-preserve-system/truncate-by-tokens-preserve-system.tests.json +144 -0
  1179. package/nodes/truncate-old-tool-responses/truncate-old-tool-responses.config.json +46 -0
  1180. package/nodes/truncate-old-tool-responses/truncate-old-tool-responses.process.js +40 -0
  1181. package/nodes/truncate-old-tool-responses/truncate-old-tool-responses.tests.json +207 -0
  1182. package/nodes/truncate-text/truncate-text.config.json +68 -0
  1183. package/nodes/truncate-text/truncate-text.process.js +77 -0
  1184. package/nodes/truncate-text/truncate-text.tests.json +115 -0
  1185. package/nodes/twilio-send-sms/twilio-send-sms.config.json +45 -0
  1186. package/nodes/twilio-send-sms/twilio-send-sms.process.js +17 -0
  1187. package/nodes/twilio-send-sms/twilio-send-sms.tests.json +14 -0
  1188. package/nodes/untokenizer/untokenizer.config.json +38 -0
  1189. package/nodes/untokenizer/untokenizer.process.js +46 -0
  1190. package/nodes/untokenizer/untokenizer.tests.json +83 -0
  1191. package/nodes/word-count/word-count.config.json +54 -0
  1192. package/nodes/word-count/word-count.process.js +40 -0
  1193. package/nodes/word-count/word-count.tests.json +100 -0
  1194. package/nodes/x-ai-grok-4-20/x-ai-grok-4-20.config.json +207 -0
  1195. package/nodes/x-ai-grok-4-20/x-ai-grok-4-20.process.js +139 -0
  1196. package/nodes/x-ai-grok-4-20/x-ai-grok-4-20.tests.json +80 -0
  1197. package/nodes/x-ai-grok-4-20-multi-agent/x-ai-grok-4-20-multi-agent.config.json +156 -0
  1198. package/nodes/x-ai-grok-4-20-multi-agent/x-ai-grok-4-20-multi-agent.process.js +54 -0
  1199. package/nodes/x-ai-grok-4-20-multi-agent/x-ai-grok-4-20-multi-agent.tests.json +44 -0
  1200. package/nodes/x-ai-grok-4-3/x-ai-grok-4-3.config.json +231 -0
  1201. package/nodes/x-ai-grok-4-3/x-ai-grok-4-3.process.js +139 -0
  1202. package/nodes/x-ai-grok-4-3/x-ai-grok-4-3.tests.json +80 -0
  1203. package/nodes/x-ai-grok-build-0-1/x-ai-grok-build-0-1.config.json +231 -0
  1204. package/nodes/x-ai-grok-build-0-1/x-ai-grok-build-0-1.process.js +139 -0
  1205. package/nodes/x-ai-grok-build-0-1/x-ai-grok-build-0-1.tests.json +80 -0
  1206. package/nodes/xml-to-json/xml-to-json.config.json +44 -0
  1207. package/nodes/xml-to-json/xml-to-json.process.js +163 -0
  1208. package/nodes/xml-to-json/xml-to-json.tests.json +113 -0
  1209. package/nodes/xnor-gate/xnor-gate.config.json +30 -0
  1210. package/nodes/xnor-gate/xnor-gate.process.js +10 -0
  1211. package/nodes/xnor-gate/xnor-gate.tests.json +22 -0
  1212. package/nodes/xor-gate/xor-gate.config.json +30 -0
  1213. package/nodes/xor-gate/xor-gate.process.js +10 -0
  1214. package/nodes/xor-gate/xor-gate.tests.json +22 -0
  1215. package/nodes/yaml-to-json/yaml-to-json.config.json +36 -0
  1216. package/nodes/yaml-to-json/yaml-to-json.process.js +178 -0
  1217. package/nodes/yaml-to-json/yaml-to-json.tests.json +112 -0
  1218. package/package.json +37 -0
  1219. package/src/classes/ErrorManager.js +234 -0
  1220. package/src/index.js +2998 -0
  1221. package/src/integrations/airtable.js +192 -0
  1222. package/src/integrations/confluence.js +87 -0
  1223. package/src/integrations/firecrawl.js +157 -0
  1224. package/src/integrations/github.js +80 -0
  1225. package/src/integrations/google_custom_search.js +136 -0
  1226. package/src/integrations/hubspot.js +282 -0
  1227. package/src/integrations/jira.js +115 -0
  1228. package/src/integrations/knowledge-base-interface.js +119 -0
  1229. package/src/integrations/linear.js +134 -0
  1230. package/src/integrations/newsdata_io.js +201 -0
  1231. package/src/integrations/notion.js +208 -0
  1232. package/src/integrations/openai.js +185 -0
  1233. package/src/integrations/openrouter.js +751 -0
  1234. package/src/integrations/pinecone.js +80 -0
  1235. package/src/integrations/resend.js +76 -0
  1236. package/src/integrations/sendgrid.js +118 -0
  1237. package/src/integrations/slack.js +70 -0
  1238. package/src/integrations/sqlite.js +477 -0
  1239. package/src/integrations/stripe.js +78 -0
  1240. package/src/integrations/supabase.js +104 -0
  1241. package/src/integrations/twilio.js +66 -0
  1242. package/src/types/README.md +181 -0
  1243. package/src/types/content.converters.js +21 -0
  1244. package/src/types/content.json +31 -0
  1245. package/src/types/conversation.converters.js +148 -0
  1246. package/src/types/conversation.json +51 -0
  1247. package/src/types/message.converters.js +45 -0
  1248. package/src/types/message.json +47 -0
  1249. package/src/types/plugin.converters.js +28 -0
  1250. package/src/types/plugin.json +10 -0
  1251. package/src/types/tool.converters.js +28 -0
  1252. package/src/types/tool.json +18 -0
  1253. package/src/utilities/cache.js +381 -0
  1254. package/src/utilities/helpers.js +87 -0
  1255. package/src/utilities/loaders.js +900 -0
  1256. package/src/utilities/mcp.js +165 -0
  1257. package/src/utilities/oauth.js +280 -0
  1258. package/src/utilities/sanitizeAPICall.js +63 -0
  1259. package/src/utilities/typeConverters.js +34 -0
  1260. package/src/utilities/typers.js +484 -0
  1261. package/src/utilities/validators.js +124 -0
  1262. package/types/content.json +31 -0
  1263. package/types/conversation.json +51 -0
  1264. package/types/message.json +47 -0
  1265. package/types/plugin.json +10 -0
  1266. package/types/tool.json +18 -0
package/src/index.js ADDED
@@ -0,0 +1,2998 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import Ajv from "ajv";
4
+ import axios from "axios";
5
+ import { v4 as uuidv4 } from "uuid";
6
+ import crypto from "crypto";
7
+
8
+ import ErrorManager from "./classes/ErrorManager.js";
9
+ import CacheManager from "./utilities/cache.js";
10
+
11
+ import { callMCPTool, fetchMCPTools } from "./utilities/mcp.js";
12
+ import { loadNodes, loadIntegrations, detectAndLoadFlow } from "./utilities/loaders.js";
13
+ import { validateKeys, validateFlow, validateInputs } from "./utilities/validators.js";
14
+ import { loadCustomTypes, typeCheck, convertType } from "./utilities/typers.js";
15
+ import { createSafeToolName, isRemoteMCPTool, isManualToolNode, mapTypeToJSONSchema, getDirname } from "./utilities/helpers.js";
16
+ import { sanitizeAPICallEvent } from "./utilities/sanitizeAPICall.js";
17
+
18
+
19
+ /**
20
+ * Workbench - Core class for executing node-based flows
21
+ * Handles node loading, input/output validation, and flow execution
22
+ */
23
+ export default class Workbench {
24
+
25
+
26
+
27
+ /**
28
+ * Create a new Workbench instance
29
+ * @param {Object} flow - The flow definition containing nodes and links
30
+ * @param {Object} config - Configuration options and context for the engine
31
+ */
32
+ constructor(flow, config = {}) {
33
+ this.flow = flow;
34
+ this.debug = config.debug || false;
35
+ this.keys = config.keys || {};
36
+ this.executionQueue = [];
37
+ this.runningNodes = new Set();
38
+ this.completedNodes = new Set();
39
+ this.maxConcurrency = config.maxConcurrency || 5;
40
+ this.lastError = null;
41
+
42
+ this.maxPluginCalls = config.maxPluginCalls || 10;
43
+
44
+ this.config = {
45
+ ...config
46
+ };
47
+
48
+ // The abort signal inherited from an ancestor engine (set once, at
49
+ // construction — sub-engines receive the parent's via {...config}). Captured
50
+ // here so a later run() can't mistake a *prior run's own* signal (which run()
51
+ // writes back into config.signal) for an ancestor and abort instantly.
52
+ this._inheritedSignal = this.config.signal || null;
53
+
54
+ // Track knowledge base files that need cleanup
55
+ this.knowledgeFilesToCleanup = new Set();
56
+
57
+ // Initialize timeout flag
58
+ this.hasTimedOut = false;
59
+
60
+ // Track executed node states to prevent duplicate processing
61
+ this.executedNodeStates = new Set();
62
+
63
+ // Track node hashes that are currently in the queue or running
64
+ // This prevents adding the same node multiple times to the queue
65
+ this.queuedNodeHashes = new Set();
66
+
67
+ // Resolvers for waitForNodeCompletion - event-based signaling
68
+ this.nodeCompletionResolvers = [];
69
+
70
+ }
71
+
72
+ async initialize() {
73
+
74
+ this.loadNodes = loadNodes.bind(this);
75
+ this.loadIntegrations = loadIntegrations.bind(this);
76
+ this.loadCustomTypes = loadCustomTypes.bind(this);
77
+
78
+ this.typeCheck = typeCheck.bind(this);
79
+ this.convertType = convertType.bind(this);
80
+ // MCP tools are fetched via fetchMCPTools utility (no binding needed)
81
+ this.validateKeys = validateKeys.bind(this);
82
+ this.validateFlow = validateFlow.bind(this);
83
+ this.validateInputs = validateInputs.bind(this);
84
+
85
+
86
+ const nodes = await this.loadNodes(this.flow);
87
+ const compiledCustomTypes = await this.loadCustomTypes();
88
+
89
+ if(!this.config.integrations) {
90
+ this.config.integrations = await this.loadIntegrations(this.config, this.flow);
91
+ }
92
+
93
+ // Bind API call emit helper for node process functions (e.g. http-request)
94
+ this.config._emitAPICall = async (rawEvent) => {
95
+ if (!this.config.onAPICall) return;
96
+ const event = sanitizeAPICallEvent(rawEvent);
97
+ try {
98
+ const result = this.config.onAPICall(event);
99
+ if (result && typeof result.then === 'function') await result;
100
+ } catch (e) {
101
+ // Don't let callback errors break node execution
102
+ }
103
+ };
104
+
105
+ this.nodes = nodes;
106
+ this.compiledCustomTypes = compiledCustomTypes;
107
+ this.nodesDir = path.join(getDirname(import.meta.url), "../nodes");
108
+
109
+
110
+ this.cache = new CacheManager();
111
+ this.flowTimeout = null;
112
+ this.timeline = [];
113
+
114
+ // Initialize ErrorManager for centralized error handling
115
+ this.errorManager = new ErrorManager({
116
+ onError: this.config.onError || null,
117
+ executionId: this.config.executionId || uuidv4(),
118
+ executionContext: {
119
+ timeline: this.timeline,
120
+ nodeCount: this.flow.nodes.length
121
+ }
122
+ });
123
+
124
+ // this.logDebug("zv1 initialized with config:", JSON.stringify(config, null, 2));
125
+ this.logDebug(`Loaded ${Object.keys(nodes).length} node types`);
126
+ this.logDebug(`Loaded ${Object.keys(compiledCustomTypes).length} custom types`);
127
+
128
+ // Clean up the flow
129
+ this.sanitizeFlow();
130
+
131
+ // Validate keys for nodes
132
+ this.validateKeys();
133
+
134
+ // Ensure the flow can even be run (use expanded flow)
135
+ this.validateFlow(this.flow);
136
+ this.initializePluginMappings();
137
+
138
+ // Track knowledge base files for cleanup
139
+ this._trackKnowledgeFiles();
140
+ }
141
+
142
+ /**
143
+ * Create a new zv1 instance
144
+ * Supports both legacy JSON files and new .zv1 files with hierarchical imports
145
+ *
146
+ * @param {string|Object} flow - File path (string) or flow definition object
147
+ * @param {Object} config - Configuration options and context for the engine
148
+ * @returns {Promise<zv1>} Fully initialized zv1 instance
149
+ * @throws {Error} If flow cannot be loaded or is invalid
150
+ *
151
+ * @example
152
+ * // Load from .zv1 file
153
+ * const engine = await Workbench.create('./myflow.zv1', { keys: { openrouter: 'sk-...' } });
154
+ *
155
+ * // Load from legacy JSON file
156
+ * const engine = await Workbench.create('./legacy.json', { keys: { openrouter: 'sk-...' } });
157
+ *
158
+ * // Load from flow object
159
+ * const engine = await Workbench.create(flowObject, { keys: { openrouter: 'sk-...' } });
160
+ */
161
+ static async create(flow, config = {}) {
162
+ try {
163
+ // Use the new unified loader to detect and load the flow
164
+ // This handles both legacy JSON files and new .zv1 files
165
+ const loadedFlow = await detectAndLoadFlow(flow);
166
+
167
+ // Create engine instance with the loaded flow
168
+ const engine = new Workbench(loadedFlow, config);
169
+
170
+ // Initialize the engine (loads nodes, validates flow, etc.)
171
+ await engine.initialize();
172
+
173
+ return engine;
174
+ } catch (error) {
175
+ // Provide more context for common errors
176
+ if (error.message.includes('not found')) {
177
+ throw new Error(`Flow file not found: ${error.message}`);
178
+ } else if (error.message.includes('Invalid JSON')) {
179
+ throw new Error(`Invalid JSON in flow file: ${error.message}`);
180
+ } else if (error.message.includes('Missing required')) {
181
+ throw new Error(`Invalid flow structure: ${error.message}`);
182
+ } else {
183
+ throw new Error(`Failed to create zv1 instance: ${error.message}`);
184
+ }
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Helper function to log debug information
190
+ * @param {...any} args - Arguments to log
191
+ */
192
+ logDebug(...args) {
193
+ if (this.debug) {
194
+ console.log("[DEBUG]", ...args);
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Helper to check if node has any refiring inputs
200
+ * @private
201
+ * @param {string} nodeId - The node ID to check
202
+ * @returns {boolean} True if the node has any refiring inputs
203
+ */
204
+ _hasRefiringInput(nodeId) {
205
+ const node = this.flow.nodes.find(n => n.id === nodeId);
206
+ if (!node) return false;
207
+
208
+ const nodeDefinition = this.nodes[node.type];
209
+ if (!nodeDefinition) return false;
210
+
211
+ return nodeDefinition.config.inputs.some(input =>
212
+ input.allow_multiple && input.refires
213
+ );
214
+ }
215
+
216
+ /**
217
+ * Create a hash of node execution state (id + inputs + settings)
218
+ * Used to prevent duplicate processing of nodes with identical contexts
219
+ * @private
220
+ * @param {string} nodeId - The node ID
221
+ * @param {Object} inputs - The inputs object
222
+ * @param {Object} settings - The settings object
223
+ * @returns {string} SHA-256 hash of the execution state
224
+ */
225
+ _createExecutionHash(nodeId, inputs, settings) {
226
+ const state = {
227
+ id: nodeId,
228
+ inputs: inputs || {},
229
+ settings: settings || {}
230
+ };
231
+
232
+ // For refiring nodes, add a timestamp based on the input values
233
+ if (this._hasRefiringInput(nodeId)) {
234
+ // Find the latest timestamp from any refiring input
235
+ let latestInputTimestamp = 0;
236
+ const node = this.flow.nodes.find(n => n.id === nodeId);
237
+ const nodeDefinition = this.nodes[node.type];
238
+
239
+ if (nodeDefinition?.config?.inputs) {
240
+ nodeDefinition.config.inputs.forEach(inputDef => {
241
+ if (inputDef.allow_multiple && inputDef.refires) {
242
+ const inputLinks = this.flow.links.filter(
243
+ link => link.to.node_id === nodeId && link.to.port_name === inputDef.name
244
+ );
245
+
246
+ inputLinks.forEach(link => {
247
+ const timestamp = this.cache.getLatestTimestamp({
248
+ node_id: link.from.node_id,
249
+ port_name: link.from.port_name
250
+ });
251
+ if (timestamp && timestamp > latestInputTimestamp) {
252
+ latestInputTimestamp = timestamp;
253
+ }
254
+ });
255
+ }
256
+ });
257
+ }
258
+
259
+ state.timestamp = latestInputTimestamp;
260
+ } else {
261
+ // For non-refiring nodes, include input values AND current timestamp
262
+ // This ensures nodes re-execute when inputs change OR when called again
263
+ state.inputHash = JSON.stringify(inputs || {});
264
+ // state.timestamp = Number(process.hrtime.bigint()); // Current execution timestamp
265
+ }
266
+
267
+ // Create deterministic JSON string (sort keys for consistency)
268
+ const stateString = JSON.stringify(state, Object.keys(state).sort());
269
+
270
+ // Use SHA-256 hash
271
+ return crypto.createHash('sha256')
272
+ .update(stateString)
273
+ .digest('hex')
274
+ .substring(0, 16); // Use first 16 chars for shorter hash
275
+ }
276
+
277
+ /**
278
+ * Update execution context for ErrorManager
279
+ * @param {Object} context - Additional execution context
280
+ */
281
+ updateErrorContext(context) {
282
+ if (this.errorManager) {
283
+ this.errorManager.updateExecutionContext(context);
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Core execution logic shared between processNode and processNodeWithArgs
289
+ * @private
290
+ */
291
+ /**
292
+ * Race a node's execution promise against the flow's abort signal so a hung
293
+ * node (e.g. a stalled LLM/HTTP call) can't park the execution loop past the
294
+ * flow timeout. The signal is also handed to integrations so the underlying
295
+ * request is actually cancelled. No-op when there's no abort controller, so
296
+ * callers outside run() behave exactly as before.
297
+ */
298
+ _raceAbort(promise) {
299
+ const signal = this.abortController?.signal;
300
+ if (!signal) return promise;
301
+ const reason = () => (signal.reason instanceof Error ? signal.reason : new Error('Flow aborted'));
302
+ if (signal.aborted) {
303
+ // Still attach a handler so the original promise's eventual rejection
304
+ // isn't unhandled.
305
+ Promise.resolve(promise).catch(() => {});
306
+ return Promise.reject(reason());
307
+ }
308
+ return new Promise((resolve, reject) => {
309
+ const onAbort = () => { cleanup(); reject(reason()); };
310
+ const cleanup = () => signal.removeEventListener('abort', onAbort);
311
+ signal.addEventListener('abort', onAbort, { once: true });
312
+ promise.then(
313
+ (v) => { cleanup(); resolve(v); },
314
+ (e) => { cleanup(); reject(e); }
315
+ );
316
+ });
317
+ }
318
+
319
+ async _executeNodeCore(node, inputs, settings, nodeDefinition) {
320
+ // Warn once per deprecated node type. These still execute (so existing flows
321
+ // keep working), but the underlying model may be gone or expiring — see
322
+ // config.deprecation. Tombstoned nodes are kept rather than deleted precisely
323
+ // so flows referencing them still load.
324
+ if (nodeDefinition?.config?.deprecated) {
325
+ this._deprecationWarned = this._deprecationWarned || new Set();
326
+ if (!this._deprecationWarned.has(node.type)) {
327
+ this._deprecationWarned.add(node.type);
328
+ const dep = nodeDefinition.config.deprecation || {};
329
+ const detail = [
330
+ dep.reason,
331
+ dep.expiration_date ? `expires ${dep.expiration_date}` : null,
332
+ dep.replacement ? `use ${dep.replacement} instead` : null
333
+ ].filter(Boolean).join('; ');
334
+ console.warn(`[zv1] Node type "${node.type}" is deprecated${detail ? ': ' + detail : ''}.`);
335
+ }
336
+ }
337
+
338
+ const timelineEntry = {
339
+ nodeId: node.id,
340
+ nodeType: node.type,
341
+ inputs: JSON.parse(JSON.stringify(inputs)),
342
+ settings: JSON.parse(JSON.stringify(settings || {})),
343
+ startTime: new Date().toISOString()
344
+ };
345
+ const startDate = new Date();
346
+
347
+ try {
348
+ if(this.config.onNodeStart) {
349
+ await this.config.onNodeStart({
350
+ nodeId: node.id,
351
+ nodeType: node.type,
352
+ timestamp: Date.now(),
353
+ inputs: inputs,
354
+ settings: settings || {}
355
+ });
356
+ }
357
+
358
+ // Create a copy of nodeConfig with instance-specific type and id
359
+ // This avoids mutating the shared nodeDefinition.config which would cause
360
+ // issues when multiple nodes of the same type run concurrently
361
+ const nodeConfig = {
362
+ ...nodeDefinition.config,
363
+ type: node.type,
364
+ id: node.id
365
+ };
366
+
367
+ const outputs = await this._raceAbort(
368
+ nodeDefinition.process({inputs, settings, config: this.config, nodeConfig})
369
+ );
370
+ const endDate = new Date();
371
+ timelineEntry.outputs = JSON.parse(JSON.stringify(outputs));
372
+ timelineEntry.endTime = endDate.toISOString();
373
+ timelineEntry.durationMs = endDate - startDate;
374
+ timelineEntry.status = 'success';
375
+ this.timeline.push(timelineEntry);
376
+
377
+ if(this.config.onNodeComplete) {
378
+ await this.config.onNodeComplete({
379
+ nodeId: node.id,
380
+ nodeType: node.type,
381
+ timestamp: Date.now(),
382
+ inputs: inputs,
383
+ outputs: outputs,
384
+ settings: settings || {}
385
+ });
386
+ }
387
+
388
+ return outputs;
389
+ } catch (error) {
390
+ const endDate = new Date();
391
+ timelineEntry.endTime = endDate.toISOString();
392
+ timelineEntry.durationMs = endDate - startDate;
393
+ timelineEntry.status = 'error';
394
+ timelineEntry.errorMessage = error.message;
395
+ this.timeline.push(timelineEntry);
396
+
397
+ // Update execution context for ErrorManager
398
+ this.errorManager.updateExecutionContext({
399
+ timeline: this.timeline,
400
+ nodeCount: this.flow.nodes.length,
401
+ nodesExecuted: this.timeline.length,
402
+ cost_summary: this._getCostSummaryFromTimeline()
403
+ });
404
+
405
+ // Use ErrorManager to handle the error
406
+ this.errorManager.throwNodeError(
407
+ node.id,
408
+ node.type,
409
+ `Node execution failed: ${error.message}`,
410
+ error
411
+ );
412
+ }
413
+ }
414
+
415
+ /**
416
+ * Process a single node
417
+ * @param {Object} node - The node to process
418
+ * @returns {Object} The outputs from the node
419
+ */
420
+ async processNode(node) {
421
+
422
+ this.logDebug(`Processing node [${node.id}] of type [${node.type}]`);
423
+
424
+ const nodeDefinition = this.nodes[node.type];
425
+ if (!nodeDefinition) {
426
+ this.logDebug(`Error: Node type "${node.type}" not found`);
427
+ throw new Error(`Node type "${node.type}" not found. It may have been removed or renamed (e.g. a model retired from the provider). Check for an updated node type or regenerate the node catalog.`);
428
+ }
429
+
430
+ // Apply default settings from node configuration
431
+ if (!node.settings) {
432
+ node.settings = {};
433
+ }
434
+
435
+ // Inherit default values for settings
436
+ if (nodeDefinition.config.settings) {
437
+ nodeDefinition.config.settings.forEach(settingDef => {
438
+ if (settingDef.default !== undefined && (node.settings[settingDef.name] === undefined || node.settings[settingDef.name] === null || node.settings[settingDef.name] === '')) {
439
+ this.logDebug(`Applying default value for setting [${settingDef.name}]: ${JSON.stringify(settingDef.default)}`);
440
+ node.settings[settingDef.name] = settingDef.default;
441
+ }
442
+ });
443
+ }
444
+
445
+ // Collect inputs from the cache
446
+ const inputs = {};
447
+ this.logDebug(`Collecting inputs for node [${node.id}]`);
448
+
449
+ // Get all links that connect to this node
450
+ const nodeInputLinks = this.flow.links.filter((link) => link.to.node_id === node.id);
451
+
452
+ // First, identify which input ports have connections
453
+ const connectedInputs = new Set();
454
+ nodeInputLinks.forEach(link => {
455
+ connectedInputs.add(link.to.port_name);
456
+ });
457
+
458
+ // Process all connected inputs
459
+ nodeInputLinks.forEach((link) => {
460
+ this.logDebug(`Processing link from [${link.from.node_id}:${link.from.port_name}] to [${node.id}:${link.to.port_name}]`);
461
+
462
+ const key = `${link.from.node_id}:${link.from.port_name}`;
463
+ const inputName = link.to.port_name;
464
+
465
+ const inputDef = nodeDefinition.config.inputs.find((i) => i.name === inputName);
466
+ if (!inputDef) {
467
+ this.logDebug(`Warning: No input definition found for port ${inputName}`);
468
+ return;
469
+ }
470
+
471
+ this.logDebug(`Input definition for ${inputName}:`, inputDef);
472
+
473
+ if (inputDef.allow_multiple && inputDef.refires) {
474
+ // Refiring input: Only collect NEW values (not yet consumed)
475
+ const lastConsumed = CacheManager.getLastConsumed(node.settings, inputName);
476
+
477
+ // If never consumed before, get the latest value (for initial execution)
478
+ if (lastConsumed === 0) {
479
+ const latestValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
480
+ if (latestValue !== undefined) {
481
+ inputs[inputName] = latestValue;
482
+ this.logDebug(`Refiring input [${inputName}]: using initial value (never consumed):`, latestValue);
483
+ }
484
+ } else {
485
+ // Get only new values since last consumption
486
+ const newValues = this.cache.getNew({
487
+ node_id: link.from.node_id,
488
+ port_name: link.from.port_name,
489
+ afterTimestamp: lastConsumed
490
+ });
491
+
492
+ this.logDebug(`Refiring input [${inputName}]: found ${newValues.length} new values since ${lastConsumed}`);
493
+
494
+ if (newValues.length > 0) {
495
+ // For refiring, use the first new value that triggered this execution
496
+ inputs[inputName] = newValues[0];
497
+ this.logDebug(`Setting refiring input [${inputName}] to new value:`, newValues[0]);
498
+ }
499
+ }
500
+ } else if (inputDef.allow_multiple && !inputDef.refires) {
501
+ // Non-refiring multiple input: Collect all current values
502
+ const cachedValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
503
+
504
+ if (this.cache.has({ node_id: link.from.node_id, port_name: link.from.port_name })) {
505
+ if(!inputs[inputName]) {
506
+ inputs[inputName] = [];
507
+ }
508
+
509
+ // Get the value and validate its type
510
+ const value = cachedValue;
511
+ const itemType = inputDef.type || "any";
512
+
513
+ // If the value is an array and doesn't pass type check as-is,
514
+ // check if it's an array of the expected type and spread it in
515
+ if (Array.isArray(value) && !this.typeCheck(value, itemType)) {
516
+ let allValid = value.length > 0;
517
+ for (const item of value) {
518
+ if (!this.typeCheck(item, itemType)) {
519
+ allValid = false;
520
+ break;
521
+ }
522
+ }
523
+ if (allValid) {
524
+ inputs[inputName].push(...value);
525
+ this.logDebug(`Spread array of ${value.length} items into multiple-input [${inputName}]`);
526
+ } else {
527
+ this.logDebug(`Type mismatch for item in multiple-input [${inputName}]: Expected ${itemType}`);
528
+ }
529
+ } else if (this.typeCheck(value, itemType)) {
530
+ inputs[inputName].push(value);
531
+ } else {
532
+ this.logDebug(`Type mismatch for item in multiple-input [${inputName}]: Expected ${itemType}`);
533
+ }
534
+ }
535
+ } else {
536
+ // Single value input: Always use latest value
537
+ const cachedValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
538
+
539
+ if (this.cache.has({ node_id: link.from.node_id, port_name: link.from.port_name })) {
540
+ this.logDebug(`Setting value for input [${inputName}]`);
541
+ inputs[inputName] = cachedValue;
542
+ } else if (!inputDef.required && inputDef.default !== undefined) {
543
+ // If optional and no link value, use the default
544
+ inputs[inputName] = inputDef.default;
545
+ this.logDebug(`Input [${inputName}] for node [${node.id}] using default value:`, inputDef.default);
546
+ } else {
547
+ this.logDebug(`Warning: No value found for input [${inputName}] and no default available`);
548
+ }
549
+ }
550
+ });
551
+
552
+ // Now handle unconnected inputs with default values
553
+ nodeDefinition.config.inputs.forEach((inputDef) => {
554
+ const inputName = inputDef.name;
555
+
556
+ // Skip inputs that already have values from connections
557
+ if (connectedInputs.has(inputName) || inputs[inputName] !== undefined) {
558
+ return;
559
+ }
560
+
561
+ // Apply default value for unconnected inputs if available
562
+ if (inputDef.default !== undefined) {
563
+ inputs[inputName] = inputDef.default;
564
+ this.logDebug(`Applied default value for unconnected input [${inputName}]:`, inputDef.default);
565
+ } else if (inputDef.required) {
566
+ this.logDebug(`Warning: Required input [${inputName}] has no connection and no default value`);
567
+ }
568
+ });
569
+
570
+
571
+
572
+ // Debug: Log inputs before processing
573
+ this.logDebug(`Node [${node.id}] of type [${node.type}] inputs:`, JSON.stringify(inputs, null, 2));
574
+
575
+ // Validate inputs against node configuration
576
+ this.validateInputs(nodeDefinition.config, inputs);
577
+
578
+ // If this node is a macro, execute its internal flow (check this FIRST before accepts_plugins)
579
+ if (nodeDefinition.config.is_macro) {
580
+ this.logDebug(`Node [${node.id}] is a macro. Executing internal flow.`);
581
+ return await this.processMacroNode(node);
582
+ }
583
+
584
+ // If this node is an LLM that accepts plugins, use the special handler
585
+ if (nodeDefinition.config.accepts_plugins) {
586
+ this.logDebug(`Node [${node.id}] is an LLM with plugin support. Using processLLMNode.`);
587
+ return await this.processLLMNode(node);
588
+ }
589
+
590
+ // Execute the process function using the shared core logic
591
+ this.logDebug(`Executing process function for node [${node.id}]`);
592
+
593
+ const outputs = await this._executeNodeCore(node, inputs, node.settings || {}, nodeDefinition);
594
+
595
+ // Track this execution state to prevent future duplicates
596
+ const executionHash = this._createExecutionHash(node.id, inputs, node.settings || {});
597
+ this.executedNodeStates.add(executionHash);
598
+
599
+ // Debug: Log outputs after processing
600
+ this.logDebug(`Node [${node.id}] outputs:`, JSON.stringify(outputs, null, 2));
601
+
602
+ // Update consumption tracking for refiring inputs
603
+ const consumptionUpdates = {};
604
+ const refiringInputs = nodeDefinition.config.inputs.filter(i => i.allow_multiple && i.refires);
605
+
606
+ if (refiringInputs.length > 0) {
607
+ this.logDebug(`Updating consumption tracking for ${refiringInputs.length} refiring inputs`);
608
+
609
+ refiringInputs.forEach(inputDef => {
610
+ const inputName = inputDef.name;
611
+
612
+ // Find all connected links for this refiring input
613
+ const inputLinks = this.flow.links.filter(
614
+ link => link.to.node_id === node.id && link.to.port_name === inputName
615
+ );
616
+
617
+ // Get the latest timestamp from all connected sources
618
+ let maxTimestamp = 0;
619
+ inputLinks.forEach(link => {
620
+ const timestamp = this.cache.getLatestTimestamp({
621
+ node_id: link.from.node_id,
622
+ port_name: link.from.port_name
623
+ });
624
+ if (timestamp && timestamp > maxTimestamp) {
625
+ maxTimestamp = timestamp;
626
+ }
627
+ });
628
+
629
+ if (maxTimestamp > 0) {
630
+ consumptionUpdates[inputName] = maxTimestamp;
631
+ this.logDebug(`Marking input [${inputName}] as consumed up to timestamp ${maxTimestamp}`);
632
+ }
633
+ });
634
+ }
635
+
636
+ // Handle updated settings
637
+ if (outputs.__updated_settings) {
638
+ node.settings = {
639
+ ...node.settings,
640
+ ...outputs.__updated_settings,
641
+ };
642
+ this.logDebug(`Node [${node.id}] updated settings:`, JSON.stringify(node.settings, null, 2));
643
+ delete outputs.__updated_settings; // Remove from regular outputs
644
+ }
645
+
646
+ // Apply consumption tracking updates if any
647
+ if (Object.keys(consumptionUpdates).length > 0) {
648
+ node.settings = {
649
+ ...node.settings,
650
+ _consumption_tracking: {
651
+ ...node.settings._consumption_tracking,
652
+ ...consumptionUpdates
653
+ }
654
+ };
655
+ this.logDebug(`Node [${node.id}] updated consumption tracking:`, node.settings._consumption_tracking);
656
+ }
657
+
658
+ // Store outputs in the cache
659
+ for (const [key, value] of Object.entries(outputs)) {
660
+ this.logDebug(`Storing output in cache: ${node.id}:${key}`, value);
661
+ this.cache.set({ node_id: node.id, port_name: key, value });
662
+ }
663
+
664
+ this.logDebug(`Node [${node.id}] processing completed successfully`);
665
+ return outputs;
666
+ }
667
+
668
+ /**
669
+ * Propagate values through the graph
670
+ * @param {string} nodeId - The ID of the node to start propagation from
671
+ */
672
+ async propagate(currentNodeId) {
673
+ this.logDebug(`Starting propagation from node [${currentNodeId}]`);
674
+ const visitedNodes = new Map(); // Map to track visit count per node
675
+
676
+ // Check for timeout
677
+ if (this.hasTimedOut) {
678
+ this.errorManager.throwTimeoutError("Flow execution timed out.");
679
+ }
680
+
681
+ // Get current node definition
682
+ const currentNode = this.flow.nodes.find((node) => node.id === currentNodeId);
683
+ if (!currentNode) {
684
+ this.logDebug(`Error: Node with ID "${currentNodeId}" not found`);
685
+ throw new Error(`Node with ID "${currentNodeId}" not found.`);
686
+ }
687
+
688
+ const nodeDefinition = this.nodes[currentNode.type];
689
+
690
+ // Get current visit count (for debugging only)
691
+ const visitCount = visitedNodes.get(currentNodeId) || 0;
692
+
693
+ // Increment visit count
694
+ visitedNodes.set(currentNodeId, visitCount + 1);
695
+
696
+ this.logDebug(`Processing node [${currentNodeId}] (visit #${visitCount + 1})`);
697
+
698
+ this.logDebug(`Processing downstream propagation for node [${currentNodeId}]`);
699
+
700
+ // Find all downstream nodes connected to this node
701
+ const downstreamLinks = this.flow.links.filter((link) => {
702
+
703
+ if(link.from.node_id === currentNodeId) {
704
+
705
+ // did this output from nodeId actually send a value that wasn't undefined or null?
706
+ const value = this.cache.get({ node_id: currentNodeId, port_name: link.from.port_name });
707
+ if(value === undefined || value === null) {
708
+ return false;
709
+ }
710
+
711
+ return true;
712
+ }
713
+
714
+ return false;
715
+ });
716
+ const downstreamNodes = downstreamLinks.map((link) => {
717
+ const node = this.flow.nodes.find((node) => node.id === link.to.node_id);
718
+
719
+
720
+ return node;
721
+ });
722
+
723
+ this.logDebug(`Downstream nodes: ${downstreamNodes.map(n => n?.id).join(', ')}`);
724
+
725
+ for (const downstreamNode of downstreamNodes) {
726
+ if (!downstreamNode) continue;
727
+
728
+ // Skip plugin nodes that are actually connected as plugins - they should only run when called by LLM
729
+ const isPluginNode = this.nodes[downstreamNode.type]?.config?.is_plugin;
730
+ const isConnectedAsPlugin = this.flow.links.some(link =>
731
+ link.from.node_id === downstreamNode.id && link.type === "plugin"
732
+ );
733
+
734
+ if (isPluginNode && isConnectedAsPlugin) {
735
+ this.logDebug(`Skipping plugin node [${downstreamNode.id}] during propagation - will only run when called by LLM`);
736
+ continue;
737
+ }
738
+
739
+ // Check if the downstream node is ready
740
+ const nodeDefinition = this.nodes[downstreamNode.type];
741
+ const nodeLinks = this.flow.links.filter((link) => link.to.node_id === downstreamNode.id);
742
+
743
+ // Group links by input name
744
+ const linksByInput = {};
745
+ nodeLinks.forEach(link => {
746
+ if (!linksByInput[link.to.port_name]) {
747
+ linksByInput[link.to.port_name] = [];
748
+ }
749
+ linksByInput[link.to.port_name].push(link);
750
+ });
751
+
752
+ // Check if all required inputs are ready
753
+ const isReady = Object.entries(linksByInput).every(([inputName, links]) => {
754
+ const inputDef = nodeDefinition.config.inputs.find(i => i.name === inputName);
755
+ if (!inputDef) return true; // defensive; shouldn't normally happen
756
+
757
+ // --- 1) Refiring multiple inputs (loop-start etc.) ---
758
+ if (inputDef.allow_multiple && inputDef.refires) {
759
+ const lastConsumed = CacheManager.getLastConsumed(downstreamNode.settings, inputName);
760
+
761
+ return links.some(link => {
762
+ if (link.type === "plugin") return false; // shouldn't be refiring anyway
763
+
764
+ const { node_id, port_name } = link.from;
765
+
766
+ if (lastConsumed === 0) {
767
+ // First time: we only consider this "ready"
768
+ // if upstream has actually produced a *non-null* value.
769
+ const hasEntry = this.cache.has({ node_id, port_name });
770
+ const value = this.cache.get({ node_id, port_name });
771
+
772
+ const ready = hasEntry && value !== null && value !== undefined;
773
+ this.logDebug(
774
+ `Refiring input [${inputName}] first run from ${node_id}:${port_name}, ready: ${ready}`
775
+ );
776
+ return ready;
777
+ } else {
778
+ // Subsequent runs: only fire when there is something newer
779
+ const hasNew = this.cache.hasNew({
780
+ node_id,
781
+ port_name,
782
+ afterTimestamp: lastConsumed
783
+ });
784
+ this.logDebug(
785
+ `Refiring input [${inputName}] from ${node_id}:${port_name}, hasNew: ${hasNew} (after ${lastConsumed})`
786
+ );
787
+ return hasNew;
788
+ }
789
+ });
790
+ }
791
+
792
+ // --- 2) Non-refiring inputs (your existing behavior, just made explicit) ---
793
+
794
+ return links.every(link => {
795
+ if (link.type === "plugin") return true;
796
+
797
+ const { node_id, port_name } = link.from;
798
+ const hasEntry = this.cache.has({ node_id, port_name });
799
+ const value = this.cache.get({ node_id, port_name });
800
+
801
+ this.logDebug(
802
+ `Checking regular input [${inputName}] from ${node_id}:${port_name}, hasEntry: ${hasEntry}, value:`,
803
+ value
804
+ );
805
+
806
+ if (inputDef.required) {
807
+ // Required: as soon as the upstream has *any* value in history
808
+ // (including null), we consider this satisfied.
809
+ // This matches your previous behavior.
810
+ return hasEntry;
811
+ }
812
+
813
+ // Optional:
814
+ // - If no link existed in history -> false (upstream hasn't run yet).
815
+ // - If upstream ran with either a real value or null -> true.
816
+ // (null meaning "ran, but intentionally no data", which is your convention.)
817
+ return hasEntry || value === null;
818
+ });
819
+ });
820
+
821
+
822
+ if (isReady) {
823
+ this.logDebug(`Node [${downstreamNode.id}] is ready. Processing...`);
824
+
825
+ // Use centralized method to add to queue with duplicate checking
826
+ this.addToExecutionQueue(downstreamNode);
827
+ } else {
828
+ this.logDebug(`Node [${downstreamNode.id}] is not ready.`);
829
+ }
830
+ }
831
+ this.logDebug(`Propagation from node [${currentNodeId}] completed`);
832
+ }
833
+
834
+ /**
835
+ * Add a node to the execution queue with duplicate checking
836
+ * This centralizes all execution queue additions and ensures nodes
837
+ * with identical execution states (same inputs/settings) are not executed twice
838
+ * @param {Object} node - The node to add to the execution queue
839
+ * @returns {boolean} True if the node was added to the queue, false if it was skipped (duplicate)
840
+ * @private
841
+ */
842
+ addToExecutionQueue(node) {
843
+ try {
844
+ const nodeDefinition = this.nodes[node.type];
845
+ if (!nodeDefinition) {
846
+ this.logDebug(`Warning: Cannot add node [${node.id}] to queue - node type "${node.type}" not found`);
847
+ return false;
848
+ }
849
+
850
+ // Collect inputs for this node to create execution hash
851
+ const inputs = this._collectNodeInputs(node, nodeDefinition);
852
+ const executionHash = this._createExecutionHash(node.id, inputs, node.settings || {});
853
+
854
+ // Check if this exact execution state has already been processed
855
+ if (this.executedNodeStates.has(executionHash)) {
856
+ this.logDebug(`Node [${node.id}] already executed with identical inputs/settings (hash: ${executionHash}), skipping queue addition`);
857
+ return false;
858
+ }
859
+
860
+ // Check if this node is already in the queue or currently running
861
+ if (this.queuedNodeHashes.has(executionHash)) {
862
+ this.logDebug(`Node [${node.id}] already in queue or running with hash ${executionHash}, skipping duplicate queue addition`);
863
+ return false;
864
+ }
865
+
866
+ // Mark as queued and add to execution queue
867
+ this.queuedNodeHashes.add(executionHash);
868
+ this.executionQueue.push(node);
869
+ this.logDebug(`Added node [${node.id}] of type [${node.type}] to execution queue (hash: ${executionHash})`);
870
+ return true;
871
+ } catch (error) {
872
+ this.logDebug(`Error adding node [${node.id}] to execution queue:`, error.message);
873
+ throw error;
874
+ }
875
+ }
876
+
877
+ /**
878
+ * Collect inputs for a node (helper method for duplicate checking)
879
+ * @param {Object} node - The node to collect inputs for
880
+ * @param {Object} nodeDefinition - The node definition
881
+ * @returns {Object} The collected inputs
882
+ * @private
883
+ */
884
+ _collectNodeInputs(node, nodeDefinition) {
885
+ const inputs = {};
886
+
887
+ // Get all links that connect to this node
888
+ const nodeInputLinks = this.flow.links.filter((link) => link.to.node_id === node.id);
889
+
890
+ // First, identify which input ports have connections
891
+ const connectedInputs = new Set();
892
+ nodeInputLinks.forEach(link => {
893
+ connectedInputs.add(link.to.port_name);
894
+ });
895
+
896
+ // Process all connected inputs
897
+ nodeInputLinks.forEach((link) => {
898
+ const inputName = link.to.port_name;
899
+ const inputDef = nodeDefinition.config.inputs.find((i) => i.name === inputName);
900
+ if (!inputDef) return;
901
+
902
+ if (inputDef.allow_multiple && inputDef.refires) {
903
+ // Refiring input: Only collect NEW values (not yet consumed)
904
+ const lastConsumed = CacheManager.getLastConsumed(node.settings, inputName);
905
+
906
+ if (lastConsumed === 0) {
907
+ const latestValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
908
+ if (latestValue !== undefined) {
909
+ inputs[inputName] = latestValue;
910
+ }
911
+ } else {
912
+ const newValues = this.cache.getNew({
913
+ node_id: link.from.node_id,
914
+ port_name: link.from.port_name,
915
+ afterTimestamp: lastConsumed
916
+ });
917
+
918
+ if (newValues.length > 0) {
919
+ inputs[inputName] = newValues[0];
920
+ }
921
+ }
922
+ } else if (inputDef.allow_multiple && !inputDef.refires) {
923
+ // Non-refiring multiple input: Collect all current values
924
+ const cachedValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
925
+
926
+ if (this.cache.has({ node_id: link.from.node_id, port_name: link.from.port_name })) {
927
+ if(!inputs[inputName]) {
928
+ inputs[inputName] = [];
929
+ }
930
+
931
+ const value = cachedValue;
932
+ const itemType = inputDef.type || "any";
933
+
934
+ // If the value is an array and doesn't pass type check as-is,
935
+ // check if it's an array of the expected type and spread it in
936
+ if (Array.isArray(value) && !this.typeCheck(value, itemType)) {
937
+ let allValid = value.length > 0;
938
+ for (const item of value) {
939
+ if (!this.typeCheck(item, itemType)) {
940
+ allValid = false;
941
+ break;
942
+ }
943
+ }
944
+ if (allValid) {
945
+ inputs[inputName].push(...value);
946
+ }
947
+ } else if (this.typeCheck(value, itemType)) {
948
+ inputs[inputName].push(value);
949
+ }
950
+ }
951
+ } else {
952
+ // Single value input: Always use latest value
953
+ const cachedValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
954
+
955
+ if (this.cache.has({ node_id: link.from.node_id, port_name: link.from.port_name })) {
956
+ inputs[inputName] = cachedValue;
957
+ } else if (!inputDef.required && inputDef.default !== undefined) {
958
+ inputs[inputName] = inputDef.default;
959
+ }
960
+ }
961
+ });
962
+
963
+ // Handle unconnected inputs with default values
964
+ nodeDefinition.config.inputs.forEach((inputDef) => {
965
+ const inputName = inputDef.name;
966
+
967
+ if (connectedInputs.has(inputName) || inputs[inputName] !== undefined) {
968
+ return;
969
+ }
970
+
971
+ if (inputDef.default !== undefined) {
972
+ inputs[inputName] = inputDef.default;
973
+ }
974
+ });
975
+
976
+ return inputs;
977
+ }
978
+
979
+ /**
980
+ * Launch a node for parallel execution
981
+ * @param {Object} node - The node to launch
982
+ */
983
+ launchNode(node) {
984
+ this.runningNodes.add(node.id);
985
+ this.logDebug(`Node [${node.id}] added to running set`);
986
+
987
+ // Remove from queued hashes since it's now running
988
+ // Recalculate hash to remove it from tracking
989
+ try {
990
+ const nodeDefinition = this.nodes[node.type];
991
+ if (nodeDefinition) {
992
+ const inputs = this._collectNodeInputs(node, nodeDefinition);
993
+ const executionHash = this._createExecutionHash(node.id, inputs, node.settings || {});
994
+ this.queuedNodeHashes.delete(executionHash);
995
+ this.logDebug(`Removed node [${node.id}] hash ${executionHash} from queued tracking (now running)`);
996
+ }
997
+ } catch (error) {
998
+ // If hash calculation fails, continue anyway - not critical
999
+ this.logDebug(`Warning: Could not remove hash for node [${node.id}] from queued tracking:`, error.message);
1000
+ }
1001
+
1002
+ // Execute node asynchronously
1003
+ this.processNode(node)
1004
+ .then(() => {
1005
+ // Node completed successfully
1006
+ this.runningNodes.delete(node.id);
1007
+ this.completedNodes.add(node.id);
1008
+ this.logDebug(`Node [${node.id}] completed, removed from running set`);
1009
+
1010
+ // Propagate downstream nodes to queue
1011
+ this.propagate(node.id);
1012
+
1013
+ // Signal completion for any waiting promises
1014
+ this._signalNodeCompletion();
1015
+ })
1016
+ .catch((error) => {
1017
+ // Node failed
1018
+ this.runningNodes.delete(node.id);
1019
+ this.logDebug(`Node [${node.id}] failed, removed from running set:`, error.message);
1020
+
1021
+ // Store the error for the main execution loop to handle
1022
+ this.lastError = error;
1023
+
1024
+ // Signal completion for any waiting promises (even on failure)
1025
+ this._signalNodeCompletion();
1026
+ });
1027
+ }
1028
+
1029
+ /**
1030
+ * Wait for at least one node to complete
1031
+ * Uses event-based signaling instead of busy-waiting
1032
+ * @returns {Promise<void>}
1033
+ */
1034
+ async waitForNodeCompletion() {
1035
+ // If no nodes are running, return immediately
1036
+ if (this.runningNodes.size === 0) {
1037
+ return;
1038
+ }
1039
+
1040
+ // Wait for a node to complete via event-based signaling
1041
+ return new Promise((resolve) => {
1042
+ this.nodeCompletionResolvers.push(resolve);
1043
+ });
1044
+ }
1045
+
1046
+ /**
1047
+ * Signal that a node has completed (success or failure)
1048
+ * This triggers any waiting promises from waitForNodeCompletion
1049
+ */
1050
+ _signalNodeCompletion() {
1051
+ if (this.nodeCompletionResolvers.length > 0) {
1052
+ const resolver = this.nodeCompletionResolvers.shift();
1053
+ resolver();
1054
+ }
1055
+ }
1056
+
1057
+
1058
+ /**
1059
+ * Clean up resources including knowledge databases and temporary files
1060
+ * This should be called when the engine is no longer needed to free up memory
1061
+ * @returns {Promise<void>}
1062
+ */
1063
+ async cleanup() {
1064
+ this.logDebug('Starting cleanup process...');
1065
+
1066
+ try {
1067
+ // Clean up main orchestration knowledge base integration
1068
+ if (this.config.integrations?.knowledgeBase) {
1069
+ this.logDebug('Cleaning up main knowledge base integration...');
1070
+ await this.config.integrations.knowledgeBase.disconnect();
1071
+ delete this.config.integrations.knowledgeBase;
1072
+ }
1073
+
1074
+ // Also clean up legacy sqlite integration for backward compatibility
1075
+ if (this.config.integrations?.sqlite) {
1076
+ this.logDebug('Cleaning up legacy SQLite integration...');
1077
+ await this.config.integrations.sqlite.disconnect();
1078
+ delete this.config.integrations.sqlite;
1079
+ }
1080
+
1081
+ // Clean up any imported engines that were created
1082
+ // These are stored in the cache when import nodes are processed
1083
+ const rawStore = this.cache.getRawStore();
1084
+ const importEngines = Object.values(rawStore).filter(item =>
1085
+ item && typeof item === 'object' && item.constructor?.name === 'zv1'
1086
+ );
1087
+
1088
+ for (const importEngine of importEngines) {
1089
+ if (importEngine.cleanup) {
1090
+ this.logDebug('Cleaning up imported engine...');
1091
+ await importEngine.cleanup();
1092
+ }
1093
+ }
1094
+
1095
+ // Clear the cache
1096
+ this.cache.clear();
1097
+
1098
+ // Clear timeline
1099
+ this.timeline = [];
1100
+
1101
+ // Clean up any remaining temporary knowledge base files
1102
+ await this._cleanupTempKnowledgeFiles();
1103
+
1104
+ this.logDebug('Cleanup completed successfully');
1105
+
1106
+ } catch (error) {
1107
+ console.warn('[WARN] Error during cleanup:', error.message);
1108
+ // Don't throw - cleanup should be best effort
1109
+ }
1110
+ }
1111
+
1112
+ /**
1113
+ * Run the flow and return the final output of the flow
1114
+ * @param {Object} inputData - Data to inject into input nodes
1115
+ * @param {number} timeout - Maximum execution time in milliseconds
1116
+ * @returns {Object} The final output from output nodes
1117
+ */
1118
+ async run(inputData, timeout = 60000) {
1119
+ this.logDebug(`Starting flow execution with timeout: ${timeout}ms`);
1120
+ this.logDebug(`Input data:`, JSON.stringify(inputData, null, 2));
1121
+
1122
+ // Clear executed states for this run
1123
+ this.executedNodeStates.clear();
1124
+ this.queuedNodeHashes.clear();
1125
+
1126
+ // clear the execution queue
1127
+ this.executionQueue = [];
1128
+ this.runningNodes = new Set();
1129
+ this.completedNodes = new Set();
1130
+
1131
+ // Update error manager with timeout context
1132
+ this.errorManager.updateExecutionContext({
1133
+ timeout: timeout,
1134
+ startTime: Date.now()
1135
+ });
1136
+
1137
+ // Set a timeout flag to prevent infinite execution
1138
+ this.hasTimedOut = false;
1139
+
1140
+ // Preemptive cancellation: an AbortController whose signal is exposed to
1141
+ // node processes/integrations (via config.signal) and aborted when the flow
1142
+ // times out. This lets in-flight LLM/HTTP calls and sub-engines actually
1143
+ // stop, instead of the loop parking on a hung node until it resolves.
1144
+ this.abortController = new AbortController();
1145
+ this._abortTeardown = () => {};
1146
+ // Cascade: if an ancestor engine passed its signal in (macro / imported-flow
1147
+ // sub-engine), abort when it aborts — so a parent timeout reaches descendants.
1148
+ // Use the construction-time inherited signal, NOT this.config.signal (which a
1149
+ // prior run() of a reused engine may have overwritten with its own signal).
1150
+ const inheritedSignal = this._inheritedSignal;
1151
+ if (inheritedSignal) {
1152
+ if (inheritedSignal.aborted) {
1153
+ this.abortController.abort(inheritedSignal.reason);
1154
+ } else {
1155
+ const onAncestorAbort = () => this.abortController.abort(inheritedSignal.reason);
1156
+ inheritedSignal.addEventListener('abort', onAncestorAbort, { once: true });
1157
+ this._abortTeardown = () => inheritedSignal.removeEventListener('abort', onAncestorAbort);
1158
+ }
1159
+ }
1160
+ // Expose our signal to node processes and integrations.
1161
+ this.config.signal = this.abortController.signal;
1162
+
1163
+ this.flowTimeout = setTimeout(() => {
1164
+ this.logDebug("Flow execution timed out");
1165
+ this.hasTimedOut = true;
1166
+ if (!this.abortController.signal.aborted) {
1167
+ this.abortController.abort(new Error(`Flow execution timed out after ${timeout}ms`));
1168
+ }
1169
+ }, timeout);
1170
+
1171
+ let inputsMissingValues = [];
1172
+
1173
+ try {
1174
+ this.logDebug("Starting flow execution...");
1175
+
1176
+ // Step 1: Add all entry nodes (constant nodes without inputs) to the execution queue
1177
+ for (const node of this.entryNodes) {
1178
+ this.logDebug(`Processing entry node [${node.id}] of type [${node.type}]`);
1179
+ // await this.processNode(node);
1180
+ // await this.propagate(node.id);
1181
+
1182
+ this.addToExecutionQueue(node);
1183
+ }
1184
+
1185
+
1186
+ // Step 2: Setup and then add input nodes (if any) to the execution queue
1187
+ if (this.inputNodes.length > 0) {
1188
+
1189
+ const numberOfInputDataNodes = this.inputNodes.filter(node => node.type === "input-data").length;
1190
+ const numberOfInputChatNodes = this.inputNodes.filter(node => node.type === "input-chat").length;
1191
+ const numberOfInputPromptNodes = this.inputNodes.filter(node => node.type === "input-prompt").length;
1192
+
1193
+ this.logDebug(`Found ${numberOfInputDataNodes} input-data nodes, ${numberOfInputChatNodes} input-chat nodes, ${numberOfInputPromptNodes} input-prompt nodes`);
1194
+
1195
+ for(const inputNode of this.inputNodes) {
1196
+ this.logDebug(`Injecting inputData into input node [${inputNode.id}]:`, inputData);
1197
+ if(!inputNode.settings) {
1198
+ inputNode.settings = {};
1199
+ }
1200
+
1201
+ if(inputNode.type === "input-data") {
1202
+ const inputKey = inputNode.settings?.key || 'data';
1203
+ let variable_value = inputData[inputKey];
1204
+
1205
+ // If the specific key is not found, try to map from the main 'data' key
1206
+ // This handles cases where .zv1 files have input-data nodes with specific keys
1207
+ // but the main flow is passing input with key 'data'
1208
+ if(variable_value === undefined && inputData.data !== undefined && inputKey !== 'data') {
1209
+ this.logDebug(`Mapping input 'data' to input-data node key '${inputKey}'`);
1210
+ variable_value = inputData.data;
1211
+ }
1212
+
1213
+ if(variable_value === undefined) {
1214
+ variable_value = inputNode.settings.default_value;
1215
+ }
1216
+
1217
+ if(variable_value !== undefined) {
1218
+ // await this.processNode({ ...inputNode, settings: { ...inputNode.settings, ...{value: variable_value} } });
1219
+ // await this.propagate(inputNode.id);
1220
+ this.addToExecutionQueue({ ...inputNode, settings: { ...inputNode.settings, ...{value: variable_value} } });
1221
+ } else {
1222
+ inputsMissingValues.push({
1223
+ id: inputNode.id,
1224
+ type: inputNode.type,
1225
+ key: inputNode.settings?.key || 'data'
1226
+ });
1227
+ }
1228
+ } else if(inputNode.type === "input-chat") {
1229
+
1230
+ let variable_value = inputData[inputNode.settings?.key || 'chat'];
1231
+
1232
+ if(variable_value !== undefined) {
1233
+ // Normalize string/object to message array
1234
+ if (typeof variable_value === 'string') {
1235
+ variable_value = [{ role: 'user', content: variable_value }];
1236
+ } else if (variable_value && typeof variable_value === 'object' && !Array.isArray(variable_value)) {
1237
+ variable_value = [variable_value];
1238
+ }
1239
+ this.addToExecutionQueue({ ...inputNode, settings: { ...inputNode.settings, ...{messages: variable_value} } });
1240
+ } else {
1241
+ inputsMissingValues.push({
1242
+ id: inputNode.id,
1243
+ type: inputNode.type,
1244
+ key: inputNode.settings?.key || 'chat'
1245
+ });
1246
+ }
1247
+
1248
+ } else if(inputNode.type === "input-prompt") {
1249
+ let variable_value = inputData[inputNode.settings?.key || 'prompt'];
1250
+
1251
+ if(variable_value !== undefined) {
1252
+ // await this.processNode({ ...inputNode, settings: { ...inputNode.settings, ...{prompt: variable_value} } });
1253
+ // await this.propagate(inputNode.id);
1254
+ this.addToExecutionQueue({ ...inputNode, settings: { ...inputNode.settings, ...{prompt: variable_value} } });
1255
+ } else {
1256
+ inputsMissingValues.push({
1257
+ id: inputNode.id,
1258
+ type: inputNode.type,
1259
+ key: inputNode.settings?.key || 'prompt'
1260
+ });
1261
+ }
1262
+ }
1263
+ }
1264
+
1265
+ } else {
1266
+ this.logDebug("No input nodes found - flow will start from constant nodes");
1267
+ }
1268
+
1269
+ // Step 3: Process the execution queue with parallel execution
1270
+ while(this.executionQueue.length > 0 || this.runningNodes.size > 0) {
1271
+
1272
+ // timeout check
1273
+ if(this.hasTimedOut) {
1274
+ this.errorManager.throwTimeoutError("Flow execution timed out.");
1275
+ break;
1276
+ }
1277
+
1278
+ // Launch nodes up to concurrency limit
1279
+ while(this.executionQueue.length > 0 && this.runningNodes.size < this.maxConcurrency) {
1280
+ const node = this.executionQueue.shift();
1281
+ this.logDebug('Launching node:', node.id, 'type:', node.type);
1282
+ // Launch node asynchronously
1283
+ this.launchNode(node);
1284
+ }
1285
+
1286
+ // Wait for at least one node to complete
1287
+ if(this.runningNodes.size > 0) {
1288
+ await this.waitForNodeCompletion();
1289
+
1290
+ // Check for errors from parallel execution
1291
+ if(this.lastError) {
1292
+ const error = this.lastError;
1293
+ this.lastError = null; // Clear the error
1294
+ throw error; // Re-throw to be handled by the main try/catch
1295
+ }
1296
+ }
1297
+ }
1298
+
1299
+ this.logDebug("Execution queue is empty and no nodes running");
1300
+
1301
+ // Step 4: Capture and return all output nodes' results
1302
+ const outputNodes = this.flow.nodes.filter(
1303
+ (node) => this.nodes[node.type]?.config?.is_output
1304
+ );
1305
+
1306
+ this.logDebug("outputNodes", outputNodes);
1307
+
1308
+ // If no output nodes found, return partial completion from terminal nodes
1309
+ if (outputNodes.length === 0) {
1310
+ this.logDebug("No output nodes found, returning partial completion from terminal nodes");
1311
+
1312
+ // Find all nodes that don't have outgoing connections
1313
+ const nodesWithOutgoingLinks = new Set(
1314
+ this.flow.links.map(link => link.from.node_id)
1315
+ );
1316
+
1317
+ const terminalNodes = this.flow.nodes.filter(
1318
+ node => !nodesWithOutgoingLinks.has(node.id)
1319
+ );
1320
+
1321
+ this.logDebug(`Found ${terminalNodes.length} terminal nodes`);
1322
+
1323
+ // Collect outputs from all terminal nodes
1324
+ const terminalOutputs = terminalNodes
1325
+ .map(node => {
1326
+ const nodeConfig = this.nodes[node.type]?.config;
1327
+ if (!nodeConfig) return null;
1328
+ const outputs = {};
1329
+ // For all outputs defined in config
1330
+ for (const output of nodeConfig.outputs) {
1331
+ const value = this.cache.get({ node_id: node.id, port_name: output.name });
1332
+ if (value === null || value === undefined) continue;
1333
+ outputs[output.name] = value;
1334
+ }
1335
+
1336
+ // --- NEW: For import nodes, also include all cache keys that start with node.id + ':'
1337
+ if (node.type.startsWith('imported-')) {
1338
+ const nodeOutputs = this.cache.getNodeOutputs(node.id);
1339
+ for (const [outputName, value] of Object.entries(nodeOutputs)) {
1340
+ if (!(outputName in outputs)) {
1341
+ outputs[outputName] = value;
1342
+ }
1343
+ }
1344
+ }
1345
+ if (Object.keys(outputs).length === 0) return null;
1346
+ return {
1347
+ node_id: node.id,
1348
+ type: node.type,
1349
+ outputs
1350
+ };
1351
+ })
1352
+ .filter(Boolean); // Remove null entries
1353
+
1354
+ return {
1355
+ partial: true,
1356
+ message: inputsMissingValues.length > 0 ? "Completed with missing input values and output nodes, results may be partial." : "Completed without output nodes.",
1357
+ terminalNodes: terminalOutputs,
1358
+ timeline: this.timeline,
1359
+ inputsMissingValues: inputsMissingValues,
1360
+ cost_summary: this._getCostSummaryFromTimeline()
1361
+ };
1362
+ }
1363
+
1364
+ // Collect all outputs
1365
+ const finalOutputs = {};
1366
+
1367
+ for (const node of outputNodes) {
1368
+ // Get the node's output values
1369
+ const nodeConfig = this.nodes[node.type].config;
1370
+ const nodeOutputs = nodeConfig.outputs;
1371
+
1372
+ // check cache to see if this node returned a value for output_key
1373
+
1374
+ const outputKey = node.settings?.key;
1375
+
1376
+ let doesThisNodeHaveAKey = false;
1377
+
1378
+ if(outputKey !== undefined && outputKey !== null && outputKey !== '') {
1379
+ doesThisNodeHaveAKey = true;
1380
+ } else {
1381
+ doesThisNodeHaveAKey = false;
1382
+ }
1383
+
1384
+ this.logDebug("nodeOutputs", nodeOutputs);
1385
+
1386
+ const numberOfOutputDataNodes = nodeOutputs.filter(output => output.name === "value").length;
1387
+ const numberOfOutputChatNodes = nodeOutputs.filter(output => output.name === "chat").length;
1388
+ const numberOfOutputPromptNodes = nodeOutputs.filter(output => output.name === "prompt").length;
1389
+
1390
+ this.logDebug(`Found ${numberOfOutputDataNodes} output-data nodes, ${numberOfOutputChatNodes} output-chat nodes, ${numberOfOutputPromptNodes} output-prompt nodes`);
1391
+
1392
+ let dataIndex = 0;
1393
+ let chatIndex = 0;
1394
+
1395
+ for (const output of nodeOutputs) {
1396
+
1397
+ if (this.cache.has({ node_id: node.id, port_name: output.name })) {
1398
+ const value = this.cache.get({ node_id: node.id, port_name: output.name });
1399
+
1400
+ // Skip null/undefined values
1401
+ if (value === null || value === undefined) continue;
1402
+
1403
+ // if this node.type is a data output, add the value to .data if no key is provided - otherwise add the value to the outputs with the key
1404
+ if(node.type === "output-data") {
1405
+ if(doesThisNodeHaveAKey ) {
1406
+ // if the key is provided, add the value to the outputs with the key
1407
+ finalOutputs[outputKey] = value;
1408
+ } else if(numberOfOutputDataNodes > 1) {
1409
+ finalOutputs['data_' + dataIndex] = value;
1410
+ dataIndex++;
1411
+ } else {
1412
+ finalOutputs['data'] = value;
1413
+ }
1414
+ } else if(node.type === "output-chat") {
1415
+ // if this node.type is a chat output, add the value to the conversation object
1416
+
1417
+ if(doesThisNodeHaveAKey) {
1418
+ finalOutputs[outputKey] = value;
1419
+ } else if(numberOfOutputChatNodes > 1) {
1420
+ finalOutputs['chat_' + chatIndex] = value;
1421
+ chatIndex++;
1422
+ } else {
1423
+ finalOutputs['chat'] = value;
1424
+ }
1425
+ }
1426
+ }
1427
+ }
1428
+ }
1429
+
1430
+ this.logDebug("Flow execution complete. Final outputs:", finalOutputs);
1431
+ clearTimeout(this.flowTimeout);
1432
+
1433
+ return {
1434
+ outputs: finalOutputs,
1435
+ timeline: this.timeline,
1436
+ cost_summary: this._getCostSummaryFromTimeline(),
1437
+ inputsMissingValues: inputsMissingValues,
1438
+ message: inputsMissingValues.length > 0 ? "Completed with missing input values, results may be partial." : "Completed."
1439
+ };
1440
+ } catch (error) {
1441
+ // Clear the timeout on error
1442
+ clearTimeout(this.flowTimeout);
1443
+
1444
+ // If this is a timeout error, add it to the timeline
1445
+ if (error.errorType === 'timeout') {
1446
+ // Create a timeline entry for the timeout
1447
+ const timeoutEntry = {
1448
+ nodeId: 'system',
1449
+ nodeType: 'timeout',
1450
+ inputs: {},
1451
+ settings: {},
1452
+ startTime: new Date(this.errorManager.executionContext.startTime).toISOString(),
1453
+ endTime: new Date().toISOString(),
1454
+ durationMs: Date.now() - this.errorManager.executionContext.startTime,
1455
+ status: 'error',
1456
+ errorMessage: error.message
1457
+ };
1458
+ this.timeline.push(timeoutEntry);
1459
+
1460
+ // Update error manager context with final timeline
1461
+ this.errorManager.updateExecutionContext({
1462
+ timeline: this.timeline,
1463
+ nodesExecuted: this.timeline.length
1464
+ });
1465
+ }
1466
+
1467
+ // Re-throw to propagate to the caller
1468
+ throw error;
1469
+ } finally {
1470
+ clearTimeout(this.flowTimeout);
1471
+ this._abortTeardown?.();
1472
+ // Restore the inherited signal so a reused engine doesn't treat this run's
1473
+ // (possibly aborted) signal as an ancestor on the next run().
1474
+ this.config.signal = this._inheritedSignal;
1475
+ }
1476
+ }
1477
+
1478
+ /**
1479
+ * Process a macro node by executing its internal flow
1480
+ * @param {Object} node - The macro node to process
1481
+ * @returns {Object} The outputs from the macro
1482
+ */
1483
+ async processMacroNode(node) {
1484
+ this.logDebug(`Processing macro node [${node.id}] of type [${node.type}]`);
1485
+
1486
+ const nodeDefinition = this.nodes[node.type];
1487
+ const macroConfig = nodeDefinition.config;
1488
+
1489
+ // Create timeline entry for macro execution
1490
+ const startDate = new Date();
1491
+ const timelineEntry = {
1492
+ node_id: node.id,
1493
+ type: node.type,
1494
+ startTime: startDate.toISOString(),
1495
+ status: 'running'
1496
+ };
1497
+ this.timeline.push(timelineEntry);
1498
+
1499
+
1500
+ try {
1501
+ // Create internal flow from macro_flow
1502
+ const internalFlow = {
1503
+ nodes: [...macroConfig.macro_flow.nodes],
1504
+ links: [...macroConfig.macro_flow.links]
1505
+ };
1506
+
1507
+ // Prepare tools object for internal engine if this macro accepts plugins
1508
+ let tools = {};
1509
+
1510
+ if (macroConfig.accepts_plugins && macroConfig.plugins && macroConfig.plugins.length > 0) {
1511
+ this.logDebug(`Macro [${node.id}] accepts plugins, creating tool runners for parent context execution`);
1512
+
1513
+ // Find all plugin nodes connected to this macro in the parent flow
1514
+ const externalPluginNodeIds = this.flow.links
1515
+ .filter(link => link.type === "plugin" && link.to.node_id === node.id)
1516
+ .map(link => link.from.node_id);
1517
+
1518
+ this.logDebug(`Found ${externalPluginNodeIds.length} external plugins connected to macro [${node.id}]`);
1519
+
1520
+ // Create tool definitions for each external plugin
1521
+ for (const externalPluginNodeId of externalPluginNodeIds) {
1522
+ const externalPluginNode = this.flow.nodes.find(n => n.id === externalPluginNodeId);
1523
+ if (externalPluginNode) {
1524
+ // Generate the tool schema from the parent context
1525
+ const schema = this.generateToolSchema(externalPluginNode);
1526
+
1527
+ // Create a tool runner that executes in PARENT context
1528
+ const toolRunner = async (args) => {
1529
+ this.logDebug(`Tool runner called for [${schema.name}] from internal macro engine`);
1530
+ return await this.executePluginInParentContext(externalPluginNode, args);
1531
+ };
1532
+
1533
+ tools[schema.name] = {
1534
+ schema: schema,
1535
+ process: toolRunner
1536
+ };
1537
+
1538
+ this.logDebug(`Created tool runner for plugin [${externalPluginNode.id}] -> [${schema.name}]`);
1539
+ }
1540
+ }
1541
+ }
1542
+
1543
+ // Create internal zv1 instance
1544
+ const internalEngine = new Workbench(internalFlow, {
1545
+ ...this.config,
1546
+ // Pass through integrations and other config
1547
+ integrations: this.config.integrations,
1548
+ keys: this.config.keys,
1549
+ debug: this.config.debug,
1550
+ // Pass tools from parent context
1551
+ tools: Object.keys(tools).length > 0 ? tools : undefined,
1552
+ // Conditionally disable event firing for internal engine
1553
+ // If includeInternalEvents is true, pass through the event handlers
1554
+ onNodeStart: this.config.includeInternalEvents ? this.config.onNodeStart : null,
1555
+ onNodeComplete: this.config.includeInternalEvents ? this.config.onNodeComplete : null,
1556
+ onNodeError: this.config.includeInternalEvents ? this.config.onNodeError : null
1557
+ });
1558
+
1559
+ // Share executed states with internal engine to prevent duplicate execution
1560
+ internalEngine.executedNodeStates = this.executedNodeStates;
1561
+
1562
+ // Initialize the internal engine
1563
+ await internalEngine.initialize();
1564
+
1565
+ // Map macro inputs to internal flow inputs
1566
+ const internalInputs = {};
1567
+ macroConfig.inputs.forEach(inputDef => {
1568
+ const inputValue = this.getNodeInputValue(node, inputDef.name);
1569
+ if (inputValue !== undefined) {
1570
+ internalInputs[inputDef.name] = inputValue;
1571
+ }
1572
+ });
1573
+
1574
+
1575
+ // Fire onNodeStart event (matching the convention used in _executeNodeCore)
1576
+ if (this.config.onNodeStart) {
1577
+ await this.config.onNodeStart({
1578
+ nodeId: node.id,
1579
+ nodeType: node.type,
1580
+ timestamp: Date.now(),
1581
+ inputs: internalInputs,
1582
+ settings: node.settings || {}
1583
+ });
1584
+ }
1585
+
1586
+ this.logDebug(`Macro [${node.id}] internal inputs:`, JSON.stringify(internalInputs, null, 2));
1587
+
1588
+ // Create execution hash to check for duplicate processing
1589
+ const executionHash = this._createExecutionHash(node.id, internalInputs, node.settings || {});
1590
+
1591
+ // Skip if this exact execution state has already been processed
1592
+ if (this.executedNodeStates.has(executionHash)) {
1593
+
1594
+ this.logDebug(`Macro [${node.id}] already executed with identical inputs/settings (hash: ${executionHash}), skipping`);
1595
+
1596
+ // Complete timeline entry for skipped execution
1597
+ const endDate = new Date();
1598
+ timelineEntry.endTime = endDate.toISOString();
1599
+ timelineEntry.durationMs = endDate - startDate;
1600
+ timelineEntry.status = 'skipped';
1601
+ timelineEntry.outputs = {};
1602
+
1603
+ // Fire onNodeComplete event for skipped execution
1604
+ if (this.config.onNodeComplete) {
1605
+ await this.config.onNodeComplete({
1606
+ nodeId: node.id,
1607
+ nodeType: node.type,
1608
+ timestamp: Date.now(),
1609
+ outputs: {},
1610
+ durationMs: endDate - startDate
1611
+ });
1612
+ }
1613
+
1614
+ return {}; // Return empty outputs to avoid downstream processing
1615
+ }
1616
+
1617
+ // Execute the internal flow
1618
+ const internalResult = await internalEngine.run(internalInputs);
1619
+
1620
+ // Map internal outputs back to macro outputs
1621
+ const macroOutputs = {};
1622
+ macroConfig.outputs.forEach(outputDef => {
1623
+ // Look for the output in the internal engine's cache
1624
+ // The output-data node stores its value in the cache with key "output_node_id:value"
1625
+ const outputNodeId = `output_${outputDef.name}`;
1626
+ const outputValue = internalEngine.cache.get({ node_id: outputNodeId, port_name: 'value' });
1627
+
1628
+ if (outputValue !== undefined) {
1629
+ macroOutputs[outputDef.name] = outputValue;
1630
+ }
1631
+ });
1632
+
1633
+ this.logDebug(`Macro [${node.id}] outputs:`, JSON.stringify(macroOutputs, null, 2));
1634
+
1635
+ // Store macro outputs in the main engine's cache for downstream propagation
1636
+ for (const [outputName, outputValue] of Object.entries(macroOutputs)) {
1637
+ this.cache.set({ node_id: node.id, port_name: outputName, value: outputValue });
1638
+ this.logDebug(`Stored macro output in cache: ${node.id}:${outputName}`, outputValue);
1639
+ }
1640
+
1641
+ // Complete timeline entry
1642
+ const endDate = new Date();
1643
+ timelineEntry.endTime = endDate.toISOString();
1644
+ timelineEntry.durationMs = endDate - startDate;
1645
+ timelineEntry.status = 'completed';
1646
+ timelineEntry.outputs = JSON.parse(JSON.stringify(macroOutputs));
1647
+
1648
+ // Fire onNodeComplete event (matching the convention used in _executeNodeCore)
1649
+ if (this.config.onNodeComplete) {
1650
+ await this.config.onNodeComplete({
1651
+ nodeId: node.id,
1652
+ nodeType: node.type,
1653
+ timestamp: Date.now(),
1654
+ outputs: JSON.parse(JSON.stringify(macroOutputs)),
1655
+ durationMs: endDate - startDate
1656
+ });
1657
+ }
1658
+
1659
+ // Track this execution state
1660
+ this.executedNodeStates.add(executionHash);
1661
+
1662
+ return macroOutputs;
1663
+ } catch (error) {
1664
+ // Update timeline entry with error
1665
+ const endDate = new Date();
1666
+ timelineEntry.endTime = endDate.toISOString();
1667
+ timelineEntry.durationMs = endDate - startDate;
1668
+ timelineEntry.status = 'error';
1669
+ timelineEntry.errorMessage = error.message;
1670
+
1671
+ // Fire onNodeError event (matching the convention used in _executeNodeCore)
1672
+ if (this.config.onNodeError) {
1673
+ await this.config.onNodeError({
1674
+ nodeId: node.id,
1675
+ nodeType: node.type,
1676
+ timestamp: Date.now(),
1677
+ error: error.message,
1678
+ durationMs: endDate - startDate
1679
+ });
1680
+ }
1681
+
1682
+ throw error;
1683
+ }
1684
+ }
1685
+
1686
+ /**
1687
+ * Get input value for a specific node and input name
1688
+ * @param {Object} node - The node to get input for
1689
+ * @param {string} inputName - The name of the input
1690
+ * @returns {any} The input value or undefined
1691
+ */
1692
+ getNodeInputValue(node, inputName) {
1693
+ // Find the link that connects to this input
1694
+ const link = this.flow.links.find(link =>
1695
+ link.to.node_id === node.id && link.to.port_name === inputName
1696
+ );
1697
+
1698
+ if (!link) {
1699
+ this.logDebug(`No link found for input ${inputName} on node ${node.id}`);
1700
+ return undefined;
1701
+ }
1702
+
1703
+ const value = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
1704
+
1705
+ this.logDebug(`Getting input value for ${node.id}.${inputName} from ${link.from.node_id}:${link.from.port_name}:`, value);
1706
+
1707
+ return value;
1708
+ }
1709
+
1710
+ /**
1711
+ * Clean up the flow by removing invalid links
1712
+ * @private
1713
+ */
1714
+ sanitizeFlow() {
1715
+ const nodeIds = new Set(this.flow.nodes.map(node => node.id));
1716
+
1717
+ // Filter out links that reference non-existent nodes
1718
+ const originalLength = this.flow.links.length;
1719
+ this.flow.links = this.flow.links.filter(link =>
1720
+ nodeIds.has(link.from.node_id) && nodeIds.has(link.to.node_id)
1721
+ );
1722
+
1723
+ const removedCount = originalLength - this.flow.links.length;
1724
+ if (removedCount > 0) {
1725
+ this.logDebug(`Removed ${removedCount} invalid link(s) referencing non-existent nodes`);
1726
+ }
1727
+ }
1728
+
1729
+ /**
1730
+ * Scan for plugin links and map LLM nodes to their plugin/tool nodes
1731
+ */
1732
+ initializePluginMappings() {
1733
+ this.llmPlugins = {};
1734
+ for (const node of this.flow.nodes) {
1735
+ if (this.nodes[node.type]?.config?.accepts_plugins) {
1736
+ this.llmPlugins[node.id] = this.flow.links
1737
+ .filter(link => link.type === "plugin" && link.to.node_id === node.id)
1738
+ .map(link => link.from.node_id);
1739
+ }
1740
+ }
1741
+ }
1742
+
1743
+ /**
1744
+ * Main entry for processing LLM nodes with plugins/tools
1745
+ */
1746
+ async processLLMNode(node) {
1747
+ this.logDebug(`Processing LLM node [${node.id}] of type [${node.type}]`);
1748
+
1749
+ const nodeDefinition = this.nodes[node.type];
1750
+ if (!nodeDefinition) {
1751
+ this.logDebug(`Error: Node type "${node.type}" not found`);
1752
+ throw new Error(`Node type "${node.type}" not found. It may have been removed or renamed (e.g. a model retired from the provider). Check for an updated node type or regenerate the node catalog.`);
1753
+ }
1754
+
1755
+ // Apply default settings from node configuration (same as processNode)
1756
+ if (!node.settings) {
1757
+ node.settings = {};
1758
+ }
1759
+
1760
+ // Inherit default values for settings
1761
+ if (nodeDefinition.config.settings) {
1762
+ nodeDefinition.config.settings.forEach(settingDef => {
1763
+ if (settingDef.default !== undefined && (node.settings[settingDef.name] === undefined || node.settings[settingDef.name] === null || node.settings[settingDef.name] === '')) {
1764
+ this.logDebug(`Applying default value for setting [${settingDef.name}]: ${JSON.stringify(settingDef.default)}`);
1765
+ node.settings[settingDef.name] = settingDef.default;
1766
+ }
1767
+ });
1768
+ }
1769
+
1770
+ // Collect inputs using the same logic as processNode (same as processNode)
1771
+ const inputs = {};
1772
+ this.logDebug(`Collecting inputs for LLM node [${node.id}]`);
1773
+
1774
+ // Get all links that connect to this node
1775
+ const nodeInputLinks = this.flow.links.filter((link) => link.to.node_id === node.id);
1776
+
1777
+ // First, identify which input ports have connections
1778
+ const connectedInputs = new Set();
1779
+ nodeInputLinks.forEach(link => {
1780
+ connectedInputs.add(link.to.port_name);
1781
+ });
1782
+
1783
+ // Process all connected inputs
1784
+ nodeInputLinks.forEach((link) => {
1785
+ this.logDebug(`Processing link from [${link.from.node_id}:${link.from.port_name}] to [${node.id}:${link.to.port_name}]`);
1786
+
1787
+ const key = `${link.from.node_id}:${link.from.port_name}`;
1788
+ const inputName = link.to.port_name;
1789
+
1790
+ const inputDef = nodeDefinition.config.inputs.find((i) => i.name === inputName);
1791
+ if (!inputDef) {
1792
+ this.logDebug(`Warning: No input definition found for port ${inputName}`);
1793
+ return;
1794
+ }
1795
+
1796
+ this.logDebug(`Input definition for ${inputName}:`, inputDef);
1797
+
1798
+ if (inputDef.allow_multiple && inputDef.refires) {
1799
+ // Refiring input: Only collect NEW values (not yet consumed)
1800
+ const lastConsumed = CacheManager.getLastConsumed(node.settings, inputName);
1801
+
1802
+ // If never consumed before, get the latest value (for initial execution)
1803
+ if (lastConsumed === 0) {
1804
+ const latestValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
1805
+ if (latestValue !== undefined) {
1806
+ inputs[inputName] = latestValue;
1807
+ this.logDebug(`LLM refiring input [${inputName}]: using initial value (never consumed):`, latestValue);
1808
+ }
1809
+ } else {
1810
+ // Get only new values since last consumption
1811
+ const newValues = this.cache.getNew({
1812
+ node_id: link.from.node_id,
1813
+ port_name: link.from.port_name,
1814
+ afterTimestamp: lastConsumed
1815
+ });
1816
+
1817
+ this.logDebug(`LLM refiring input [${inputName}]: found ${newValues.length} new values since ${lastConsumed}`);
1818
+
1819
+ if (newValues.length > 0) {
1820
+ // For refiring, use the first new value that triggered this execution
1821
+ inputs[inputName] = newValues[0];
1822
+ this.logDebug(`Setting LLM refiring input [${inputName}] to new value:`, newValues[0]);
1823
+ }
1824
+ }
1825
+ } else if (inputDef.allow_multiple && !inputDef.refires) {
1826
+ // Non-refiring multiple input: Collect all current values
1827
+ const cachedValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
1828
+
1829
+ if (this.cache.has({ node_id: link.from.node_id, port_name: link.from.port_name })) {
1830
+ if(!inputs[inputName]) {
1831
+ inputs[inputName] = [];
1832
+ }
1833
+
1834
+ // Get the value and validate its type
1835
+ const value = cachedValue;
1836
+ const itemType = inputDef.type || "any";
1837
+
1838
+ // If the value is an array and doesn't pass type check as-is,
1839
+ // check if it's an array of the expected type and spread it in
1840
+ if (Array.isArray(value) && !this.typeCheck(value, itemType)) {
1841
+ let allValid = value.length > 0;
1842
+ for (const item of value) {
1843
+ if (!this.typeCheck(item, itemType)) {
1844
+ allValid = false;
1845
+ break;
1846
+ }
1847
+ }
1848
+ if (allValid) {
1849
+ inputs[inputName].push(...value);
1850
+ this.logDebug(`Spread array of ${value.length} items into multiple-input [${inputName}]`);
1851
+ } else {
1852
+ this.logDebug(`Type mismatch for item in multiple-input [${inputName}]: Expected ${itemType}`);
1853
+ }
1854
+ } else if (this.typeCheck(value, itemType)) {
1855
+ inputs[inputName].push(value);
1856
+ } else {
1857
+ this.logDebug(`Type mismatch for item in multiple-input [${inputName}]: Expected ${itemType}`);
1858
+ }
1859
+ }
1860
+ } else {
1861
+ // Single value input: Always use latest value
1862
+ const cachedValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
1863
+
1864
+ if (this.cache.has({ node_id: link.from.node_id, port_name: link.from.port_name })) {
1865
+ this.logDebug(`Setting value for LLM input [${inputName}]`);
1866
+ inputs[inputName] = cachedValue;
1867
+ } else if (!inputDef.required && inputDef.default !== undefined) {
1868
+ // If optional and no link value, use the default
1869
+ inputs[inputName] = inputDef.default;
1870
+ this.logDebug(`Input [${inputName}] for LLM node [${node.id}] using default value:`, inputDef.default);
1871
+ } else {
1872
+ this.logDebug(`Warning: No value found for LLM input [${inputName}] and no default available`);
1873
+ }
1874
+ }
1875
+ });
1876
+
1877
+ // Now handle unconnected inputs with default values
1878
+ nodeDefinition.config.inputs.forEach((inputDef) => {
1879
+ const inputName = inputDef.name;
1880
+
1881
+ // Skip inputs that already have values from connections
1882
+ if (connectedInputs.has(inputName) || inputs[inputName] !== undefined) {
1883
+ return;
1884
+ }
1885
+
1886
+ // Apply default value for unconnected inputs if available
1887
+ if (inputDef.default !== undefined) {
1888
+ inputs[inputName] = inputDef.default;
1889
+ this.logDebug(`Applied default value for unconnected input [${inputName}]:`, inputDef.default);
1890
+ } else if (inputDef.required) {
1891
+ this.logDebug(`Warning: Required input [${inputName}] has no connection and no default value`);
1892
+ }
1893
+ });
1894
+
1895
+ // Debug: Log inputs before processing
1896
+ this.logDebug(`LLM Node [${node.id}] of type [${node.type}] inputs:`, JSON.stringify(inputs, null, 2));
1897
+
1898
+ // Validate inputs against node configuration (same as processNode)
1899
+ this.validateInputs(nodeDefinition.config, inputs);
1900
+
1901
+ // Create execution hash to check for duplicate processing
1902
+ const executionHash = this._createExecutionHash(node.id, inputs, node.settings || {});
1903
+
1904
+ // Skip if this exact execution state has already been processed
1905
+ if (this.executedNodeStates.has(executionHash)) {
1906
+ this.logDebug(`LLM Node [${node.id}] already executed with identical inputs/settings (hash: ${executionHash}), skipping`);
1907
+ return {}; // Return empty outputs to avoid downstream processing
1908
+ }
1909
+
1910
+ // 1. Gather plugin/tool schemas and runners
1911
+ const toolSchemas = [];
1912
+ const toolRunners = {}; // toolName -> handler
1913
+
1914
+ // First, check if there are tools provided from config (parent context or developer-provided)
1915
+ if (this.config.tools && typeof this.config.tools === 'object' && !Array.isArray(this.config.tools)) {
1916
+ this.logDebug(`Found ${Object.keys(this.config.tools).length} tools from config`);
1917
+ for (const [toolName, toolDef] of Object.entries(this.config.tools)) {
1918
+ // If schema is provided, add it to the list
1919
+ if (toolDef.schema) {
1920
+ toolSchemas.push(toolDef.schema);
1921
+ this.logDebug(`Loaded tool schema from config: ${toolName}`);
1922
+ }
1923
+ // Always register the process function if provided
1924
+ if (toolDef.process) {
1925
+ toolRunners[toolName] = toolDef.process;
1926
+ this.logDebug(`Loaded tool runner from config: ${toolName}`);
1927
+ }
1928
+ }
1929
+ }
1930
+
1931
+ // Then, discover local plugins (these can override or supplement parent tools)
1932
+ const pluginNodeIds = this.llmPlugins[node.id] || [];
1933
+ const toolNodeMap = {}; // Map tool name -> plugin node info for timeline/error tracking
1934
+ const manualToolNames = new Set(); // Track manual tools that have no runner
1935
+
1936
+ for (const pluginNodeId of pluginNodeIds) {
1937
+ const pluginNode = this.flow.nodes.find(n => n.id === pluginNodeId);
1938
+ if (!pluginNode) continue;
1939
+
1940
+ if (this.isLocalNodePlugin(pluginNode)) {
1941
+ const schema = this.generateToolSchema(pluginNode);
1942
+ toolSchemas.push(schema);
1943
+
1944
+ // Track the mapping from tool name to plugin node
1945
+ toolNodeMap[schema.name] = { node: pluginNode, type: 'plugin' };
1946
+
1947
+ const nodeDefinition = this.nodes[pluginNode.type];
1948
+
1949
+ // Special handling for macro nodes
1950
+ if (nodeDefinition?.config?.is_macro) {
1951
+ this.logDebug(`Node [${pluginNode.id}] is a macro plugin. Using processMacroNode.`);
1952
+ toolRunners[schema.name] = async (args) => {
1953
+ // Merge LLM args with static inputs
1954
+ const staticInputs = this.collectStaticInputs(pluginNode);
1955
+ const mergedInputs = { ...staticInputs, ...args };
1956
+ this.logDebug(`Merged inputs for macro plugin [${pluginNode.id}]:`, mergedInputs);
1957
+
1958
+ // For macros, we need to temporarily add the args to the cache
1959
+ const tempCacheKeys = [];
1960
+
1961
+ // Store args in cache as if they came from upstream nodes
1962
+ for (const [inputName, value] of Object.entries(mergedInputs)) {
1963
+ const tempNodeId = `__plugin_arg_${pluginNode.id}`;
1964
+ this.cache.set({ node_id: tempNodeId, port_name: inputName, value });
1965
+ tempCacheKeys.push({ node_id: tempNodeId, port_name: inputName });
1966
+
1967
+ // Add a temporary link so getNodeInputValue can find it
1968
+ const tempLink = {
1969
+ from: { node_id: tempNodeId, port_name: inputName },
1970
+ to: { node_id: pluginNode.id, port_name: inputName }
1971
+ };
1972
+ this.flow.links.push(tempLink);
1973
+ }
1974
+
1975
+ try {
1976
+ // Execute the macro
1977
+ const outputs = await this.processMacroNode(pluginNode);
1978
+
1979
+ // Store outputs in cache for downstream propagation
1980
+ this.logDebug(`Storing macro plugin outputs in cache:`, outputs);
1981
+ for (const [key, value] of Object.entries(outputs)) {
1982
+ this.cache.set({ node_id: pluginNode.id, port_name: key, value });
1983
+ }
1984
+
1985
+ // Propagate outputs downstream
1986
+ await this.propagate(pluginNode.id);
1987
+
1988
+ return outputs;
1989
+ } finally {
1990
+ // Clean up temporary cache entries and links
1991
+ tempCacheKeys.forEach(({ node_id, port_name }) =>
1992
+ this.cache.delete({ node_id, port_name })
1993
+ );
1994
+ this.flow.links = this.flow.links.filter(link =>
1995
+ !link.from.node_id.startsWith('__plugin_arg_')
1996
+ );
1997
+ }
1998
+ };
1999
+ }
2000
+ // Check if this is an imported node with chat inputs
2001
+ else if (nodeDefinition?.config?.is_import &&
2002
+ nodeDefinition.config.inputs?.some(input => input.is_chat_input)) {
2003
+ toolRunners[schema.name] = async (args) => {
2004
+ // Merge LLM args with static inputs
2005
+ const staticInputs = this.collectStaticInputs(pluginNode);
2006
+ const mergedInputs = { ...staticInputs, ...args };
2007
+ this.logDebug(`Merged inputs for chat plugin [${pluginNode.id}]:`, mergedInputs);
2008
+
2009
+ // Execute the plugin node
2010
+ const outputs = await this.processImportedChatNode(pluginNode, mergedInputs);
2011
+
2012
+ // Store outputs in cache for downstream propagation
2013
+ this.logDebug(`Storing plugin outputs in cache for downstream propagation:`, outputs);
2014
+ for (const [key, value] of Object.entries(outputs)) {
2015
+ this.cache.set({ node_id: pluginNode.id, port_name: key, value });
2016
+ }
2017
+
2018
+ // Propagate outputs downstream (if any downstream connections exist)
2019
+ await this.propagate(pluginNode.id);
2020
+
2021
+ return outputs;
2022
+ };
2023
+ } else {
2024
+ toolRunners[schema.name] = async (args) => {
2025
+ // Merge LLM args with static inputs
2026
+ const staticInputs = this.collectStaticInputs(pluginNode);
2027
+ const mergedInputs = { ...staticInputs, ...args };
2028
+ this.logDebug(`Merged inputs for plugin [${pluginNode.id}]:`, mergedInputs);
2029
+
2030
+ // Execute the plugin node
2031
+ const outputs = await this.processNodeWithArgs(pluginNode, mergedInputs);
2032
+
2033
+ // Store outputs in cache for downstream propagation
2034
+ this.logDebug(`Storing plugin outputs in cache for downstream propagation:`, outputs);
2035
+ for (const [key, value] of Object.entries(outputs)) {
2036
+ this.cache.set({ node_id: pluginNode.id, port_name: key, value });
2037
+ }
2038
+
2039
+ // Propagate outputs downstream (if any downstream connections exist)
2040
+ await this.propagate(pluginNode.id);
2041
+
2042
+ return outputs;
2043
+ };
2044
+ }
2045
+ } else if (isRemoteMCPTool(pluginNode)) {
2046
+ // Resolve named MCP integration from config.keys.mcp
2047
+ const integrationName = pluginNode.settings?.mcp_integration;
2048
+ if (!integrationName) {
2049
+ this.logDebug(`MCP plugin node [${pluginNode.id}] has no mcp_integration setting`);
2050
+ continue;
2051
+ }
2052
+ const mcpConfig = this.config.keys?.mcp?.[integrationName];
2053
+ if (!mcpConfig?.url) {
2054
+ this.logDebug(`MCP integration "${integrationName}" not found in config.keys.mcp`);
2055
+ continue;
2056
+ }
2057
+ const { url, token } = mcpConfig;
2058
+ try {
2059
+ const tools = await fetchMCPTools(url, token);
2060
+ for (const tool of tools) {
2061
+ toolSchemas.push(tool);
2062
+ toolNodeMap[tool.name] = { node: pluginNode, type: 'mcp', mcpToolName: tool.name };
2063
+ toolRunners[tool.name] = async (args) => {
2064
+ return await callMCPTool({ ...args, name: tool.name }, { url, token });
2065
+ };
2066
+ }
2067
+ this.logDebug(`Loaded ${tools.length} MCP tools from "${integrationName}"`);
2068
+ } catch (err) {
2069
+ this.logDebug(`Failed to fetch MCP tools from "${integrationName}" (${url}): ${err.message}`);
2070
+ }
2071
+ } else if (isManualToolNode(pluginNode)) {
2072
+ const schema = this.generateToolSchema(pluginNode);
2073
+ toolSchemas.push(schema);
2074
+ // Manual tools: no runner, just pass through - track them so we don't error
2075
+ manualToolNames.add(schema.name);
2076
+ toolNodeMap[schema.name] = { node: pluginNode, type: 'manual' };
2077
+ }
2078
+ }
2079
+
2080
+ // --- Also gather tool schemas connected to the LLM's 'tools' input port ---
2081
+ const toolInputLinks = this.flow.links.filter(
2082
+ link => link.to.node_id === node.id && link.to.port_name === "tools"
2083
+ );
2084
+ for (const link of toolInputLinks) {
2085
+ const cachedValue = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
2086
+ if (!cachedValue) continue;
2087
+
2088
+ // Normalize to an array — could be a single tool or an array of tools
2089
+ const schemas = Array.isArray(cachedValue) ? cachedValue : [cachedValue];
2090
+ for (const toolSchema of schemas) {
2091
+ if (!toolSchema || typeof toolSchema !== 'object' || !toolSchema.name) continue;
2092
+ // Only add schema if developer hasn't already provided one via config
2093
+ const configTool = this.config.tools?.[toolSchema.name];
2094
+ if (!configTool || !configTool.schema) {
2095
+ toolSchemas.push(toolSchema);
2096
+ this.logDebug(`Loaded tool schema from flow: ${toolSchema.name}`);
2097
+ // If there's no runner for this tool, track it as a manual tool
2098
+ if (!configTool?.process && !toolRunners[toolSchema.name]) {
2099
+ manualToolNames.add(toolSchema.name);
2100
+ const sourceNode = this.flow.nodes.find(n => n.id === link.from.node_id);
2101
+ toolNodeMap[toolSchema.name] = { node: sourceNode, type: 'manual' };
2102
+ }
2103
+ } else {
2104
+ this.logDebug(`Skipping flow schema for ${toolSchema.name} - using config schema instead`);
2105
+ }
2106
+ }
2107
+ }
2108
+
2109
+ // Extract internal tool names (tools handled by engine plugins, not manual/external tools)
2110
+ // This is used by LLM nodes to filter the conversation output to only include
2111
+ // messages related to internally-handled tools
2112
+ const internalToolNames = Object.entries(toolNodeMap)
2113
+ .filter(([_, info]) => info.type === 'plugin' || info.type === 'mcp')
2114
+ .map(([name, _]) => name);
2115
+ this.config.internal_tool_names = internalToolNames;
2116
+ this.logDebug(`Internal tool names for LLM node [${node.id}]:`, internalToolNames);
2117
+
2118
+ // 2. Inject toolSchemas into LLM call
2119
+ let llmResult;
2120
+ let tool_results = [];
2121
+ let toolCallMessage = null;
2122
+ let toolCallCount = 0;
2123
+
2124
+ // Normalize string/object messages to array format before accumulation
2125
+ if (typeof inputs.messages === 'string') {
2126
+ inputs.messages = [{ role: 'user', content: inputs.messages }];
2127
+ } else if (inputs.messages && typeof inputs.messages === 'object' && !Array.isArray(inputs.messages)) {
2128
+ inputs.messages = [inputs.messages];
2129
+ }
2130
+
2131
+ // Track accumulated messages across tool call rounds
2132
+ // Start with the original messages, then append tool calls/results each round
2133
+ let accumulatedMessages = Array.isArray(inputs.messages) ? [...inputs.messages] : [];
2134
+
2135
+ do {
2136
+ // Create inputs with accumulated messages for this round
2137
+ const roundInputs = { ...inputs, messages: accumulatedMessages };
2138
+ llmResult = await this.callLLMWithTools(node, roundInputs, toolSchemas, toolCallMessage, tool_results);
2139
+
2140
+ // After the LLM call, if there were tool calls/results, they've been appended
2141
+ // Update accumulatedMessages to include what was sent this round
2142
+ if (toolCallMessage && tool_results.length > 0) {
2143
+ accumulatedMessages = [...accumulatedMessages, toolCallMessage];
2144
+ for (const toolResult of tool_results) {
2145
+ accumulatedMessages.push({
2146
+ role: "tool",
2147
+ tool_call_id: toolResult.tool_call_id,
2148
+ name: toolResult.name,
2149
+ content: typeof toolResult.result === "string"
2150
+ ? toolResult.result
2151
+ : JSON.stringify(toolResult.result)
2152
+ });
2153
+ }
2154
+ }
2155
+
2156
+ tool_results = [];
2157
+ toolCallMessage = null;
2158
+
2159
+ if (llmResult.tool_calls && llmResult.tool_calls.length > 0) {
2160
+
2161
+ for (const tool_call of llmResult.tool_calls) {
2162
+
2163
+ if (tool_call.type === 'function' && tool_call.function) {
2164
+ const toolName = tool_call.function.name;
2165
+
2166
+ // Check if this is a manual tool (no runner, pass through)
2167
+ if (manualToolNames.has(toolName)) {
2168
+ this.logDebug(`Manual tool called: ${toolName} - passing through without execution`);
2169
+ // Don't push any result - the tool_calls will pass through on the assistant message
2170
+ continue;
2171
+ }
2172
+
2173
+ if (toolRunners[toolName]) {
2174
+ const toolNodeInfo = toolNodeMap[toolName];
2175
+ const startDate = new Date();
2176
+
2177
+ // Try to parse the arguments as a JSON object
2178
+ let toolArguments = tool_call.function.arguments;
2179
+ try {
2180
+ toolArguments = JSON.parse(tool_call.function.arguments);
2181
+ } catch (parseError) {
2182
+ // JSON parse failed - create error result
2183
+ this.logDebug(`Failed to parse tool arguments for ${toolName}:`, parseError.message);
2184
+
2185
+ // Create timeline entry for the parse error
2186
+ if (toolNodeInfo?.node) {
2187
+ const endDate = new Date();
2188
+ const timelineEntry = {
2189
+ nodeId: toolNodeInfo.node.id,
2190
+ nodeType: toolNodeInfo.node.type,
2191
+ inputs: { raw_arguments: tool_call.function.arguments },
2192
+ startTime: startDate.toISOString(),
2193
+ endTime: endDate.toISOString(),
2194
+ durationMs: endDate - startDate,
2195
+ status: 'error',
2196
+ errorMessage: `Invalid JSON arguments: ${parseError.message}`
2197
+ };
2198
+ this.timeline.push(timelineEntry);
2199
+
2200
+ // Fire onNodeError event
2201
+ if (this.config.onNodeError) {
2202
+ await this.config.onNodeError({
2203
+ nodeId: toolNodeInfo.node.id,
2204
+ nodeType: toolNodeInfo.node.type,
2205
+ error: parseError
2206
+ });
2207
+ }
2208
+ }
2209
+
2210
+ // Push error result to LLM
2211
+ tool_results.push({
2212
+ original_tool_call: tool_call,
2213
+ tool_call_id: tool_call.id,
2214
+ name: toolName,
2215
+ result: {
2216
+ error: true,
2217
+ message: `Invalid arguments provided: ${parseError.message}`
2218
+ }
2219
+ });
2220
+ continue;
2221
+ }
2222
+
2223
+ // Fire onNodeStart so the consumer's live-trace sink
2224
+ // sees this tool call as it dispatches. Without this,
2225
+ // multi-round tool-calling LLMs look like a single
2226
+ // long "LLM node running…" entry — the waterfall has
2227
+ // no visibility into the tool-call cycles inside.
2228
+ if (toolNodeInfo?.node && this.config.onNodeStart) {
2229
+ try {
2230
+ await this.config.onNodeStart({
2231
+ nodeId: toolNodeInfo.node.id,
2232
+ nodeType: toolNodeInfo.node.type,
2233
+ inputs: toolArguments,
2234
+ });
2235
+ } catch (_hookErr) {
2236
+ // Hook failures must not break the flow — swallow.
2237
+ }
2238
+ }
2239
+
2240
+ // Execute the tool runner with error handling
2241
+ try {
2242
+ const toolResult = await toolRunners[toolName](toolArguments);
2243
+
2244
+ // Success - push result
2245
+ tool_results.push({
2246
+ original_tool_call: tool_call,
2247
+ tool_call_id: tool_call.id,
2248
+ name: toolName,
2249
+ result: toolResult
2250
+ });
2251
+
2252
+ // Mirror the error-path instrumentation: record a
2253
+ // timeline entry + onNodeComplete event for the
2254
+ // successful tool call. Consumers' post-run timeline
2255
+ // walk + live trace stream both see the call now.
2256
+ if (toolNodeInfo?.node) {
2257
+ const endDate = new Date();
2258
+ const timelineEntry = {
2259
+ nodeId: toolNodeInfo.node.id,
2260
+ nodeType: toolNodeInfo.node.type,
2261
+ inputs: toolArguments,
2262
+ outputs: { result: toolResult },
2263
+ startTime: startDate.toISOString(),
2264
+ endTime: endDate.toISOString(),
2265
+ durationMs: endDate - startDate,
2266
+ status: 'success',
2267
+ };
2268
+ this.timeline.push(timelineEntry);
2269
+
2270
+ if (this.config.onNodeComplete) {
2271
+ try {
2272
+ await this.config.onNodeComplete({
2273
+ nodeId: toolNodeInfo.node.id,
2274
+ nodeType: toolNodeInfo.node.type,
2275
+ inputs: toolArguments,
2276
+ outputs: { result: toolResult },
2277
+ });
2278
+ } catch (_hookErr) {
2279
+ // Swallow — hook errors must not break the flow.
2280
+ }
2281
+ }
2282
+ }
2283
+
2284
+ } catch (executionError) {
2285
+ // Tool execution failed - create error result
2286
+ this.logDebug(`Tool execution failed for ${toolName}:`, executionError.message);
2287
+
2288
+ // Create timeline entry for the execution error
2289
+ if (toolNodeInfo?.node) {
2290
+ const endDate = new Date();
2291
+ const timelineEntry = {
2292
+ nodeId: toolNodeInfo.node.id,
2293
+ nodeType: toolNodeInfo.node.type,
2294
+ inputs: toolArguments,
2295
+ startTime: startDate.toISOString(),
2296
+ endTime: endDate.toISOString(),
2297
+ durationMs: endDate - startDate,
2298
+ status: 'error',
2299
+ errorMessage: executionError.message
2300
+ };
2301
+ this.timeline.push(timelineEntry);
2302
+
2303
+ // Fire onNodeError event
2304
+ if (this.config.onNodeError) {
2305
+ await this.config.onNodeError({
2306
+ nodeId: toolNodeInfo.node.id,
2307
+ nodeType: toolNodeInfo.node.type,
2308
+ error: executionError
2309
+ });
2310
+ }
2311
+ }
2312
+
2313
+ // Push error result to LLM
2314
+ tool_results.push({
2315
+ original_tool_call: tool_call,
2316
+ tool_call_id: tool_call.id,
2317
+ name: toolName,
2318
+ result: {
2319
+ error: true,
2320
+ message: executionError.message
2321
+ }
2322
+ });
2323
+ }
2324
+ } else {
2325
+ // No runner found and not a manual tool - this is an error
2326
+ this.logDebug(`No runner found for tool: ${toolName}`);
2327
+
2328
+ tool_results.push({
2329
+ original_tool_call: tool_call,
2330
+ tool_call_id: tool_call.id,
2331
+ name: toolName,
2332
+ result: {
2333
+ error: true,
2334
+ message: `Tool "${toolName}" is not available`
2335
+ }
2336
+ });
2337
+ }
2338
+ }
2339
+ }
2340
+ // Prepare the tool call message for the next LLM call
2341
+ toolCallMessage = {
2342
+ role: "assistant",
2343
+ content: null,
2344
+ tool_calls: llmResult.tool_calls
2345
+ };
2346
+ toolCallCount++;
2347
+ } else {
2348
+ break; // No more tool calls, exit loop
2349
+ }
2350
+ } while (tool_results.length > 0 && toolCallCount < this.maxPluginCalls);
2351
+
2352
+ // 4. Continue with normal LLM output processing
2353
+ // Store outputs in the cache and propagate downstream
2354
+ for (const output of nodeDefinition.config.outputs || []) {
2355
+ if (llmResult[output.name] !== undefined) {
2356
+ this.cache.set({ node_id: node.id, port_name: output.name, value: llmResult[output.name] });
2357
+ }
2358
+ }
2359
+
2360
+ // Debug: Log outputs after processing
2361
+ this.logDebug(`LLM Node [${node.id}] outputs:`, JSON.stringify(llmResult, null, 2));
2362
+
2363
+ // Handle updated settings
2364
+ if (llmResult.__updated_settings) {
2365
+ node.settings = {
2366
+ ...node.settings,
2367
+ ...llmResult.__updated_settings,
2368
+ };
2369
+ this.logDebug(`LLM Node [${node.id}] updated settings:`, JSON.stringify(node.settings, null, 2));
2370
+ delete llmResult.__updated_settings; // Remove from regular outputs
2371
+ }
2372
+
2373
+ // Track this execution state
2374
+ this.executedNodeStates.add(executionHash);
2375
+
2376
+ // Clean up internal_tool_names from config
2377
+ delete this.config.internal_tool_names;
2378
+
2379
+ this.logDebug(`LLM Node [${node.id}] processing completed successfully`);
2380
+ return llmResult;
2381
+ }
2382
+
2383
+ /**
2384
+ * Execute a plugin node in the parent context with all its dependencies
2385
+ * This allows plugins to be called from internal engines (macros/imports)
2386
+ * while maintaining their connections in the parent flow
2387
+ * @param {Object} pluginNode - The plugin node to execute
2388
+ * @param {Object} args - Arguments from the tool call
2389
+ * @returns {Object} The outputs from the plugin
2390
+ */
2391
+ async executePluginInParentContext(pluginNode, args) {
2392
+ this.logDebug(`Executing plugin [${pluginNode.id}] in parent context with args:`, args);
2393
+
2394
+ const nodeDefinition = this.nodes[pluginNode.type];
2395
+ if (!nodeDefinition) {
2396
+ this.logDebug(`Error: Node type "${pluginNode.type}" not found`);
2397
+ throw new Error(`Node type "${pluginNode.type}" not found.`);
2398
+ }
2399
+
2400
+ // Check if this is a macro or import node (needs special handling)
2401
+ if (nodeDefinition.config.is_macro) {
2402
+ this.logDebug(`Plugin [${pluginNode.id}] is a macro, using processMacroNode`);
2403
+
2404
+ // For macros called as plugins, we need to temporarily add the args to the cache
2405
+ // so the macro can access them via getNodeInputValue
2406
+ const tempCacheKeys = [];
2407
+
2408
+ // Store args in cache as if they came from upstream nodes
2409
+ for (const [inputName, value] of Object.entries(args)) {
2410
+ const tempNodeId = `__plugin_arg_${pluginNode.id}`;
2411
+ this.cache.set({ node_id: tempNodeId, port_name: inputName, value });
2412
+ tempCacheKeys.push({ node_id: tempNodeId, port_name: inputName });
2413
+
2414
+ // Also add a temporary link so getNodeInputValue can find it
2415
+ const tempLink = {
2416
+ from: { node_id: tempNodeId, port_name: inputName },
2417
+ to: { node_id: pluginNode.id, port_name: inputName }
2418
+ };
2419
+ this.flow.links.push(tempLink);
2420
+ }
2421
+
2422
+ try {
2423
+ // Execute the macro
2424
+ const outputs = await this.processMacroNode(pluginNode);
2425
+ return outputs;
2426
+ } finally {
2427
+ // Always clean up temporary cache entries and links, even on error
2428
+ tempCacheKeys.forEach(({ node_id, port_name }) =>
2429
+ this.cache.delete({ node_id, port_name })
2430
+ );
2431
+ this.flow.links = this.flow.links.filter(link =>
2432
+ !link.from.node_id.startsWith('__plugin_arg_')
2433
+ );
2434
+ }
2435
+ }
2436
+
2437
+ // For regular nodes and imports
2438
+ // Collect static inputs (from parent flow connections)
2439
+ const staticInputs = this.collectStaticInputs(pluginNode);
2440
+
2441
+ // Merge static inputs with LLM-provided args
2442
+ const mergedInputs = { ...staticInputs, ...args };
2443
+
2444
+ this.logDebug(`Merged inputs for plugin [${pluginNode.id}]:`, mergedInputs);
2445
+
2446
+ // Execute the plugin node with merged inputs
2447
+ const outputs = await this.processNodeWithArgs(pluginNode, mergedInputs);
2448
+
2449
+ // Store outputs in parent cache for downstream propagation
2450
+ this.logDebug(`Storing plugin outputs in parent cache:`, outputs);
2451
+ for (const [key, value] of Object.entries(outputs)) {
2452
+ this.cache.set({ node_id: pluginNode.id, port_name: key, value });
2453
+ }
2454
+
2455
+ // Propagate outputs downstream in parent context
2456
+ await this.propagate(pluginNode.id);
2457
+
2458
+ return outputs;
2459
+ }
2460
+
2461
+ /**
2462
+ * Check if a node is a local plugin (is_plugin or is_macro)
2463
+ * @param {Object} node - The node to check
2464
+ * @returns {boolean} True if the node is a plugin or macro
2465
+ */
2466
+ isLocalNodePlugin(node) {
2467
+ const thisNodeConfig = this.nodes[node.type]?.config || {};
2468
+ return thisNodeConfig.is_plugin || thisNodeConfig.is_macro;
2469
+ }
2470
+
2471
+ /**
2472
+ * Check if a node has all required inputs available in the cache
2473
+ * @param {Object} node - The node to check
2474
+ * @returns {boolean} True if all required inputs are available
2475
+ * @throws {Error} If the node type is not found
2476
+ */
2477
+ isNodeReady(node) {
2478
+ const nodeDefinition = this.nodes[node.type];
2479
+ if (!nodeDefinition) {
2480
+ this.logDebug(`Error: Node type "${node.type}" not found`);
2481
+ throw new Error(`Node type "${node.type}" not found. It may have been removed or renamed (e.g. a model retired from the provider). Check for an updated node type or regenerate the node catalog.`);
2482
+ }
2483
+ for (const inputDef of nodeDefinition.config.inputs || []) {
2484
+ if (!inputDef.required) continue;
2485
+ const inputName = inputDef.name;
2486
+ const incomingLink = this.flow.links.find(
2487
+ link => link.to.node_id === node.id && link.to.port_name === inputName
2488
+ );
2489
+ if (incomingLink) {
2490
+ if (!this.cache.has({ node_id: incomingLink.from.node_id, port_name: incomingLink.from.port_name })) {
2491
+ return false;
2492
+ }
2493
+ }
2494
+ }
2495
+ return true;
2496
+ }
2497
+
2498
+ /**
2499
+ * Collect statically connected inputs for a plugin node
2500
+ * @param {Object} node - The plugin node to collect inputs for
2501
+ * @returns {Object} The statically connected inputs
2502
+ */
2503
+ collectStaticInputs(node) {
2504
+ const config = this.nodes[node.type]?.config || {};
2505
+ const staticInputs = {};
2506
+
2507
+ // Find all statically connected inputs (non-plugin links)
2508
+ const staticLinks = this.flow.links.filter(link =>
2509
+ link.to.node_id === node.id && link.type !== "plugin"
2510
+ );
2511
+
2512
+ this.logDebug(`Collecting static inputs for plugin node [${node.id}]:`, staticLinks.map(l => `${l.from.node_id}:${l.from.port_name} -> ${l.to.port_name}`));
2513
+
2514
+ staticLinks.forEach(link => {
2515
+ const inputName = link.to.port_name;
2516
+ const inputDef = config.inputs?.find(input => input.name === inputName);
2517
+
2518
+ if (!inputDef) {
2519
+ this.logDebug(`Warning: No input definition found for static input ${inputName}`);
2520
+ return;
2521
+ }
2522
+
2523
+ const value = this.cache.get({ node_id: link.from.node_id, port_name: link.from.port_name });
2524
+
2525
+ if (value !== undefined) {
2526
+ if (inputDef.allow_multiple) {
2527
+ // Initialize or reset the array for this run
2528
+ if (!staticInputs[inputName]) {
2529
+ staticInputs[inputName] = [];
2530
+ }
2531
+
2532
+ // Get the value and validate its type
2533
+ const itemType = inputDef.type || "any";
2534
+
2535
+ // Validate individual item type
2536
+ if (this.typeCheck(value, itemType)) {
2537
+ staticInputs[inputName].push(value);
2538
+ } else {
2539
+ this.logDebug(`Type mismatch for static input [${inputName}]: Expected ${itemType}`);
2540
+ }
2541
+ } else {
2542
+ staticInputs[inputName] = value;
2543
+ }
2544
+
2545
+ this.logDebug(`Collected static input [${inputName}]:`, value);
2546
+ } else {
2547
+ this.logDebug(`Warning: No value found for static input [${inputName}]`);
2548
+ }
2549
+ });
2550
+
2551
+ // Handle unconnected inputs with default values
2552
+ config.inputs?.forEach(inputDef => {
2553
+ const inputName = inputDef.name;
2554
+
2555
+ // Skip if already has a value from static connections
2556
+ if (staticInputs[inputName] !== undefined) {
2557
+ return;
2558
+ }
2559
+
2560
+ // Apply default value for unconnected inputs if available
2561
+ if (inputDef.default !== undefined) {
2562
+ staticInputs[inputName] = inputDef.default;
2563
+ this.logDebug(`Applied default value for static input [${inputName}]:`, inputDef.default);
2564
+ }
2565
+ });
2566
+
2567
+ return staticInputs;
2568
+ }
2569
+
2570
+ generateToolSchema(node) {
2571
+ // Get the node config and settings
2572
+ const config = this.nodes[node.type]?.config || {};
2573
+ const settings = node.settings || {};
2574
+
2575
+ // Use the node's custom display name name/description if present, otherwise fall back to config
2576
+ let name = node.display_name || config.display_name || node.type;
2577
+ name = createSafeToolName(name);
2578
+
2579
+ const description = node.description || config.description || "";
2580
+
2581
+ // Find which inputs are statically connected (not available to LLM)
2582
+ const staticallyConnectedInputs = new Set();
2583
+ this.flow.links.forEach(link => {
2584
+ if (link.to.node_id === node.id && link.type !== "plugin") {
2585
+ staticallyConnectedInputs.add(link.to.port_name);
2586
+ }
2587
+ });
2588
+
2589
+ // Build JSON Schema properties for each input that is NOT statically connected
2590
+ const properties = {};
2591
+ const required = [];
2592
+ (config.inputs || []).forEach(input => {
2593
+ // Skip inputs that are statically connected - LLM shouldn't see these
2594
+ if (staticallyConnectedInputs.has(input.name)) {
2595
+ return;
2596
+ }
2597
+
2598
+ if(config.is_import){
2599
+ if(input.is_data_input){
2600
+ properties[input.name] = {
2601
+ type: input.type || "object",
2602
+ description: input.description || "",
2603
+ };
2604
+ } else if(input.is_chat_input){
2605
+ properties[input.name] = {
2606
+ type: "string",
2607
+ description: "A conversational chat message to send to this agent."
2608
+ };
2609
+ } else if(input.is_prompt_input){
2610
+ properties[input.name] = {
2611
+ type: "string",
2612
+ description: input.description || "",
2613
+ };
2614
+ }
2615
+ if (input.required) {
2616
+ required.push(input.name);
2617
+ }
2618
+ } else {
2619
+ properties[input.name] = {
2620
+ type: mapTypeToJSONSchema(input.type),
2621
+ description: input.description || "",
2622
+ };
2623
+ if (input.default !== undefined) {
2624
+ properties[input.name].default = input.default;
2625
+ }
2626
+ if (input.required) {
2627
+ required.push(input.name);
2628
+ }
2629
+ }
2630
+ });
2631
+
2632
+
2633
+ // Return the tool schema object
2634
+ return {
2635
+ name,
2636
+ description,
2637
+ parameters: {
2638
+ type: "object",
2639
+ properties,
2640
+ required,
2641
+ }
2642
+ };
2643
+ }
2644
+
2645
+
2646
+ /**
2647
+ * Process imported nodes with chat inputs, maintaining conversation state per chat key
2648
+ * @param {Object} node - The imported node to process
2649
+ * @param {Object} args - Arguments from the LLM tool call
2650
+ * @returns {Object} The outputs from the node
2651
+ */
2652
+ async processImportedChatNode(node, args) {
2653
+ this.logDebug('processImportedChatNode', node.id, args);
2654
+
2655
+ const nodeDefinition = this.nodes[node.type];
2656
+ if (!nodeDefinition || !nodeDefinition.config.is_import) {
2657
+ this.logDebug(`Error: Node ${node.id} is not an imported node`);
2658
+ throw new Error(`Node ${node.id} is not an imported node`);
2659
+ }
2660
+
2661
+ // Initialize conversation state if it doesn't exist
2662
+ if (!this._conversationState) {
2663
+ this._conversationState = {};
2664
+ }
2665
+
2666
+ // Find the imported flow definition to get the actual input-chat nodes
2667
+ const importDef = nodeDefinition.config.importDefinition;
2668
+
2669
+ // Transform string arguments to message arrays for each chat input
2670
+ const transformedArgs = { ...args };
2671
+
2672
+ for (const chatInput of nodeDefinition.config.inputs.filter(input => input.is_chat_input)) {
2673
+ const inputValue = args[chatInput.name];
2674
+ if (typeof inputValue === 'string') {
2675
+
2676
+ // The input's name IS the input-chat node's settings.key (set in
2677
+ // convertImportToNodeType), so use it directly. The previous lookup
2678
+ // compared the key against node ids, always missed, and fell back to
2679
+ // 'chat' — collapsing every chat stream into one and ignoring custom keys.
2680
+ const chatKey = chatInput.name;
2681
+
2682
+ // Create conversation key: nodeId + chatKey for this specific chat stream
2683
+ const conversationKey = `${node.id}_${chatKey}`;
2684
+
2685
+ if (!this._conversationState[conversationKey]) {
2686
+ this._conversationState[conversationKey] = [];
2687
+ }
2688
+
2689
+ // Add the new user message to this chat stream's history
2690
+ this._conversationState[conversationKey].push({
2691
+ role: 'user',
2692
+ content: inputValue
2693
+ });
2694
+
2695
+ // Pass the full conversation history for this chat stream
2696
+ transformedArgs[chatInput.name] = [...this._conversationState[conversationKey]];
2697
+
2698
+ this.logDebug(`Updated conversation for ${conversationKey}:`, this._conversationState[conversationKey]);
2699
+ }
2700
+ }
2701
+
2702
+ // Process the imported node with the full conversation context
2703
+ const outputs = await this.processNodeWithArgs(node, transformedArgs);
2704
+
2705
+ // Handle responses from output-chat nodes and append to appropriate conversation streams
2706
+ const outputChatNodes = importDef.nodes.filter(n => n.type === 'output-chat');
2707
+
2708
+ for (const outputChatNode of outputChatNodes) {
2709
+ const chatKey = outputChatNode.settings?.key || 'chat';
2710
+ const conversationKey = `${node.id}_${chatKey}`;
2711
+
2712
+ // The import's process emits the response under the output-chat's key,
2713
+ // not the bare node id. Read by key (fall back to node id for safety) so
2714
+ // the assistant's replies actually get appended to the conversation.
2715
+ const chatOutput = outputs[chatKey] ?? outputs[outputChatNode.id];
2716
+
2717
+ this.logDebug(`Looking for output from node ${outputChatNode.id} with chat key ${chatKey}:`, chatOutput);
2718
+
2719
+ if (chatOutput && Array.isArray(chatOutput)) {
2720
+ if (!this._conversationState[conversationKey]) {
2721
+ this._conversationState[conversationKey] = [];
2722
+ }
2723
+
2724
+ // The chat output contains new response messages to append
2725
+ // (not the full conversation history)
2726
+ if (chatOutput.length > 0) {
2727
+ this._conversationState[conversationKey].push(...chatOutput);
2728
+ this.logDebug(`Appended ${chatOutput.length} new messages to conversation ${conversationKey}:`, chatOutput);
2729
+ }
2730
+ } else {
2731
+ this.logDebug(`No chat output found for node ${outputChatNode.id}, available outputs:`, Object.keys(outputs));
2732
+ }
2733
+ }
2734
+
2735
+ return outputs;
2736
+ }
2737
+
2738
+ /**
2739
+ * Reset conversation state for specific chat streams or all conversations
2740
+ * @param {string} nodeId - Optional node ID to reset conversations for
2741
+ * @param {string} chatKey - Optional chat key to reset specific chat stream
2742
+ */
2743
+ resetConversationState(nodeId = null, chatKey = null) {
2744
+ if (!this._conversationState) return;
2745
+
2746
+ if (nodeId && chatKey) {
2747
+ const conversationKey = `${nodeId}_${chatKey}`;
2748
+ delete this._conversationState[conversationKey];
2749
+ this.logDebug(`Reset conversation state for node ${nodeId}, chat key ${chatKey}`);
2750
+ } else if (nodeId) {
2751
+ // Reset all chat streams for this node
2752
+ const keysToDelete = Object.keys(this._conversationState).filter(key =>
2753
+ key.startsWith(`${nodeId}_`)
2754
+ );
2755
+ keysToDelete.forEach(key => delete this._conversationState[key]);
2756
+ this.logDebug(`Reset all conversation states for node ${nodeId}`);
2757
+ } else {
2758
+ this._conversationState = {};
2759
+ this.logDebug('Reset all conversation states');
2760
+ }
2761
+ }
2762
+
2763
+ async processNodeWithArgs(node, args) {
2764
+ this.logDebug('processNodeWithArgs', node, args);
2765
+
2766
+ const nodeDefinition = this.nodes[node.type];
2767
+ if (!nodeDefinition) {
2768
+ this.logDebug(`Error: Node type "${node.type}" not found`);
2769
+ throw new Error(`Node type "${node.type}" not found. It may have been removed or renamed (e.g. a model retired from the provider). Check for an updated node type or regenerate the node catalog.`);
2770
+ }
2771
+
2772
+ // For import nodes, their process function expects (inputs, settings, config)
2773
+ // For regular nodes, same signature
2774
+ // We'll use node.settings or an empty object
2775
+ const settings = node.settings || {};
2776
+
2777
+ // Validate the merged inputs against the node configuration
2778
+ this.validateInputs(nodeDefinition.config, args);
2779
+
2780
+ // Execute using the shared core logic
2781
+ const outputs = await this._executeNodeCore(node, args, settings, nodeDefinition);
2782
+
2783
+ this.logDebug('processNodeWithArgs outputs', outputs);
2784
+ return outputs;
2785
+ }
2786
+
2787
+
2788
+ async callLLMWithTools(node, inputs, toolSchemas, toolCallMessage, toolResults) {
2789
+ const nodeDefinition = this.nodes[node.type];
2790
+ if (!nodeDefinition) {
2791
+ this.logDebug(`Error: Node type "${node.type}" not found`);
2792
+ throw new Error(`Node type "${node.type}" not found. It may have been removed or renamed (e.g. a model retired from the provider). Check for an updated node type or regenerate the node catalog.`);
2793
+ }
2794
+
2795
+ // Use the inputs passed from processLLMNode instead of re-collecting them
2796
+ const llmInputs = { ...inputs };
2797
+
2798
+ // Merge plugin/manual toolSchemas with any tools already in inputs (e.g. from filter-tools)
2799
+ // Deduplicate by tool name since tools from the "tools" input port are collected
2800
+ // both during normal input collection and during tool schema gathering
2801
+ const inputTools = Array.isArray(llmInputs.tools) ? llmInputs.tools : [];
2802
+ const seenToolNames = new Set();
2803
+ llmInputs.tools = [];
2804
+ for (const tool of [...toolSchemas, ...inputTools]) {
2805
+ if (tool?.name && !seenToolNames.has(tool.name)) {
2806
+ seenToolNames.add(tool.name);
2807
+ llmInputs.tools.push(tool);
2808
+ }
2809
+ }
2810
+
2811
+ // If this is a tool call response, append it to the messages array (OpenAI style)
2812
+ if (toolCallMessage && toolResults && Array.isArray(llmInputs.messages)) {
2813
+
2814
+ //
2815
+ llmInputs.messages = [
2816
+ ...llmInputs.messages,
2817
+ toolCallMessage
2818
+ ];
2819
+
2820
+ for(const toolResult of toolResults) {
2821
+ // You may need to adapt this for other LLMs
2822
+ llmInputs.messages = [
2823
+ ...llmInputs.messages,
2824
+ {
2825
+ role: "tool",
2826
+ tool_call_id: toolResult.tool_call_id,
2827
+ name: toolResult.name,
2828
+ content: typeof toolResult.result === "string"
2829
+ ? toolResult.result
2830
+ : JSON.stringify(toolResult.result)
2831
+ }
2832
+ ];
2833
+ }
2834
+ }
2835
+
2836
+ // Execute using the shared core logic
2837
+ const outputs = await this._executeNodeCore(node, llmInputs, node.settings || {}, nodeDefinition);
2838
+
2839
+ return outputs;
2840
+ }
2841
+
2842
+ _getCostSummaryFromTimeline() {
2843
+ let total = 0;
2844
+ let itemized = [];
2845
+ for (const entry of this.timeline) {
2846
+ const outputs = entry.outputs || {};
2847
+ if (typeof outputs.cost_total === 'number') {
2848
+ total += outputs.cost_total;
2849
+
2850
+ let item = {
2851
+ node_id: entry.nodeId,
2852
+ node_type: entry.nodeType,
2853
+ total: outputs.cost_total,
2854
+ itemized: outputs.cost_itemized
2855
+ }
2856
+ itemized.push(item);
2857
+ }
2858
+ }
2859
+
2860
+ return {
2861
+ total,
2862
+ itemized
2863
+ };
2864
+ }
2865
+
2866
+ /**
2867
+ * Track a knowledge base file for cleanup
2868
+ * @param {string} filePath - Path to the knowledge base file
2869
+ */
2870
+ trackKnowledgeFile(filePath) {
2871
+ if (filePath && filePath.includes('knowledge_')) {
2872
+ this.knowledgeFilesToCleanup.add(filePath);
2873
+ this.logDebug(`Tracking knowledge file for cleanup: ${filePath}`);
2874
+ }
2875
+ }
2876
+
2877
+ /**
2878
+ * Track all knowledge base files that need cleanup
2879
+ * @private
2880
+ */
2881
+ _trackKnowledgeFiles() {
2882
+ // Track main flow's knowledge base file
2883
+ if (this.flow.knowledgeDbPath) {
2884
+ this.trackKnowledgeFile(this.flow.knowledgeDbPath);
2885
+ }
2886
+
2887
+ // Track import knowledge base files
2888
+ if (this.flow.imports && Array.isArray(this.flow.imports)) {
2889
+ for (const importDef of this.flow.imports) {
2890
+ if (importDef.knowledgeDbPath) {
2891
+ this.trackKnowledgeFile(importDef.knowledgeDbPath);
2892
+ }
2893
+ }
2894
+ }
2895
+ }
2896
+
2897
+ /**
2898
+ * Clean up temporary knowledge base files for this specific engine instance
2899
+ * @private
2900
+ */
2901
+ async _cleanupTempKnowledgeFiles() {
2902
+
2903
+ try {
2904
+ const fs = await import('fs');
2905
+ const path = await import('path');
2906
+
2907
+ const tempDir = path.join(process.cwd(), '.temp');
2908
+ if (!fs.existsSync(tempDir)) {
2909
+ return; // No temp directory exists
2910
+ }
2911
+
2912
+ // Clean up tracked knowledge files first
2913
+ for (const filePath of this.knowledgeFilesToCleanup) {
2914
+ try {
2915
+ if (fs.existsSync(filePath)) {
2916
+ fs.unlinkSync(filePath);
2917
+ this.logDebug(`Cleaned up tracked knowledge file: ${filePath}`);
2918
+ }
2919
+
2920
+ // Also clean up any lock files
2921
+ const lockPath = `${filePath}.lock`;
2922
+ if (fs.existsSync(lockPath)) {
2923
+ fs.unlinkSync(lockPath);
2924
+ this.logDebug(`Cleaned up lock file: ${lockPath}`);
2925
+ }
2926
+ } catch (error) {
2927
+ console.warn(`[WARN] Failed to cleanup tracked knowledge file ${filePath}:`, error.message);
2928
+ }
2929
+ }
2930
+
2931
+ // Get the flow ID to identify our specific temporary files
2932
+ const flowId = this.flow.id || this.flow.metadata?.id;
2933
+ if (!flowId) {
2934
+ this.logDebug('No flow ID found, skipping additional knowledge file cleanup');
2935
+ return;
2936
+ }
2937
+
2938
+ // Look for any remaining knowledge files that match our flow ID pattern
2939
+ const files = fs.readdirSync(tempDir);
2940
+ const knowledgeFiles = files.filter(file => file === `knowledge_${flowId}.db`);
2941
+
2942
+
2943
+ for (const file of knowledgeFiles) {
2944
+ const filePath = path.join(tempDir, file);
2945
+ const lockPath = `${filePath}.lock`;
2946
+
2947
+ try {
2948
+ fs.unlinkSync(filePath);
2949
+ this.logDebug(`Cleaned up additional temporary knowledge file: ${filePath}`);
2950
+ } catch (error) {
2951
+ console.warn(`[WARN] Failed to cleanup additional temporary knowledge file ${filePath}:`, error.message);
2952
+ }
2953
+
2954
+ // Also clean up any lock files
2955
+ try {
2956
+ if (fs.existsSync(lockPath)) {
2957
+ fs.unlinkSync(lockPath);
2958
+ this.logDebug(`Cleaned up lock file: ${lockPath}`);
2959
+ }
2960
+ } catch (error) {
2961
+ // Ignore lock file cleanup errors
2962
+ }
2963
+ }
2964
+
2965
+ // Also clean up any knowledge files that are older than 1 hour (safety cleanup)
2966
+ const oneHourAgo = Date.now() - (60 * 60 * 1000);
2967
+ const allKnowledgeFiles = files.filter(file => file.startsWith('knowledge_') && file.endsWith('.db'));
2968
+
2969
+ for (const file of allKnowledgeFiles) {
2970
+ const filePath = path.join(tempDir, file);
2971
+ try {
2972
+ const stats = fs.statSync(filePath);
2973
+ if (stats.mtime.getTime() < oneHourAgo) {
2974
+ fs.unlinkSync(filePath);
2975
+ this.logDebug(`Cleaned up old temporary knowledge file: ${filePath}`);
2976
+ }
2977
+ } catch (error) {
2978
+ // Ignore errors for old file cleanup
2979
+ }
2980
+ }
2981
+
2982
+ // If temp directory is now empty, remove it
2983
+ try {
2984
+ const remainingFiles = fs.readdirSync(tempDir);
2985
+ if (remainingFiles.length === 0) {
2986
+ fs.rmdirSync(tempDir);
2987
+ this.logDebug('Removed empty temporary directory');
2988
+ }
2989
+ } catch (error) {
2990
+ // Ignore errors for directory cleanup
2991
+ }
2992
+
2993
+ } catch (error) {
2994
+ console.warn('[WARN] Error during temporary knowledge file cleanup:', error.message);
2995
+ // Don't throw - cleanup should be best effort
2996
+ }
2997
+ }
2998
+ }