@nodetool-ai/base-nodes 0.7.0-rc.11

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 (353) hide show
  1. package/dist/index.d.ts +72 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +270 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/lib/audio-wav.d.ts +66 -0
  6. package/dist/lib/audio-wav.d.ts.map +1 -0
  7. package/dist/lib/audio-wav.js +215 -0
  8. package/dist/lib/audio-wav.js.map +1 -0
  9. package/dist/nodes/agents.d.ts +253 -0
  10. package/dist/nodes/agents.d.ts.map +1 -0
  11. package/dist/nodes/agents.js +2784 -0
  12. package/dist/nodes/agents.js.map +1 -0
  13. package/dist/nodes/anthropic.d.ts +26 -0
  14. package/dist/nodes/anthropic.d.ts.map +1 -0
  15. package/dist/nodes/anthropic.js +133 -0
  16. package/dist/nodes/anthropic.js.map +1 -0
  17. package/dist/nodes/apify.d.ts +120 -0
  18. package/dist/nodes/apify.d.ts.map +1 -0
  19. package/dist/nodes/apify.js +618 -0
  20. package/dist/nodes/apify.js.map +1 -0
  21. package/dist/nodes/audio.d.ts +301 -0
  22. package/dist/nodes/audio.d.ts.map +1 -0
  23. package/dist/nodes/audio.js +1274 -0
  24. package/dist/nodes/audio.js.map +1 -0
  25. package/dist/nodes/code-node.d.ts +34 -0
  26. package/dist/nodes/code-node.d.ts.map +1 -0
  27. package/dist/nodes/code-node.js +343 -0
  28. package/dist/nodes/code-node.js.map +1 -0
  29. package/dist/nodes/code.d.ts +301 -0
  30. package/dist/nodes/code.d.ts.map +1 -0
  31. package/dist/nodes/code.js +850 -0
  32. package/dist/nodes/code.js.map +1 -0
  33. package/dist/nodes/compare.d.ts +18 -0
  34. package/dist/nodes/compare.d.ts.map +1 -0
  35. package/dist/nodes/compare.js +122 -0
  36. package/dist/nodes/compare.js.map +1 -0
  37. package/dist/nodes/constant.d.ts +315 -0
  38. package/dist/nodes/constant.d.ts.map +1 -0
  39. package/dist/nodes/constant.js +715 -0
  40. package/dist/nodes/constant.js.map +1 -0
  41. package/dist/nodes/control.d.ts +83 -0
  42. package/dist/nodes/control.d.ts.map +1 -0
  43. package/dist/nodes/control.js +212 -0
  44. package/dist/nodes/control.js.map +1 -0
  45. package/dist/nodes/data.d.ts +369 -0
  46. package/dist/nodes/data.d.ts.map +1 -0
  47. package/dist/nodes/data.js +1590 -0
  48. package/dist/nodes/data.js.map +1 -0
  49. package/dist/nodes/document.d.ts +134 -0
  50. package/dist/nodes/document.d.ts.map +1 -0
  51. package/dist/nodes/document.js +984 -0
  52. package/dist/nodes/document.js.map +1 -0
  53. package/dist/nodes/extended-placeholders.d.ts +6 -0
  54. package/dist/nodes/extended-placeholders.d.ts.map +1 -0
  55. package/dist/nodes/extended-placeholders.js +19 -0
  56. package/dist/nodes/extended-placeholders.js.map +1 -0
  57. package/dist/nodes/gemini.d.ts +109 -0
  58. package/dist/nodes/gemini.d.ts.map +1 -0
  59. package/dist/nodes/gemini.js +861 -0
  60. package/dist/nodes/gemini.js.map +1 -0
  61. package/dist/nodes/generators.d.ts +84 -0
  62. package/dist/nodes/generators.d.ts.map +1 -0
  63. package/dist/nodes/generators.js +937 -0
  64. package/dist/nodes/generators.js.map +1 -0
  65. package/dist/nodes/image.d.ts +232 -0
  66. package/dist/nodes/image.d.ts.map +1 -0
  67. package/dist/nodes/image.js +1395 -0
  68. package/dist/nodes/image.js.map +1 -0
  69. package/dist/nodes/input.d.ts +445 -0
  70. package/dist/nodes/input.d.ts.map +1 -0
  71. package/dist/nodes/input.js +1459 -0
  72. package/dist/nodes/input.js.map +1 -0
  73. package/dist/nodes/kie-dynamic.d.ts +31 -0
  74. package/dist/nodes/kie-dynamic.d.ts.map +1 -0
  75. package/dist/nodes/kie-dynamic.js +270 -0
  76. package/dist/nodes/kie-dynamic.js.map +1 -0
  77. package/dist/nodes/lib-audio-dsp.d.ts +85 -0
  78. package/dist/nodes/lib-audio-dsp.d.ts.map +1 -0
  79. package/dist/nodes/lib-audio-dsp.js +450 -0
  80. package/dist/nodes/lib-audio-dsp.js.map +1 -0
  81. package/dist/nodes/lib-audio-effects.d.ts +116 -0
  82. package/dist/nodes/lib-audio-effects.d.ts.map +1 -0
  83. package/dist/nodes/lib-audio-effects.js +905 -0
  84. package/dist/nodes/lib-audio-effects.js.map +1 -0
  85. package/dist/nodes/lib-browser.d.ts +99 -0
  86. package/dist/nodes/lib-browser.d.ts.map +1 -0
  87. package/dist/nodes/lib-browser.js +692 -0
  88. package/dist/nodes/lib-browser.js.map +1 -0
  89. package/dist/nodes/lib-charts.d.ts +20 -0
  90. package/dist/nodes/lib-charts.d.ts.map +1 -0
  91. package/dist/nodes/lib-charts.js +245 -0
  92. package/dist/nodes/lib-charts.js.map +1 -0
  93. package/dist/nodes/lib-datetime.d.ts +108 -0
  94. package/dist/nodes/lib-datetime.d.ts.map +1 -0
  95. package/dist/nodes/lib-datetime.js +421 -0
  96. package/dist/nodes/lib-datetime.js.map +1 -0
  97. package/dist/nodes/lib-doc-convert.d.ts +19 -0
  98. package/dist/nodes/lib-doc-convert.d.ts.map +1 -0
  99. package/dist/nodes/lib-doc-convert.js +257 -0
  100. package/dist/nodes/lib-doc-convert.js.map +1 -0
  101. package/dist/nodes/lib-doc-transform.d.ts +39 -0
  102. package/dist/nodes/lib-doc-transform.d.ts.map +1 -0
  103. package/dist/nodes/lib-doc-transform.js +335 -0
  104. package/dist/nodes/lib-doc-transform.js.map +1 -0
  105. package/dist/nodes/lib-docx.d.ts +106 -0
  106. package/dist/nodes/lib-docx.d.ts.map +1 -0
  107. package/dist/nodes/lib-docx.js +595 -0
  108. package/dist/nodes/lib-docx.js.map +1 -0
  109. package/dist/nodes/lib-epub.d.ts +55 -0
  110. package/dist/nodes/lib-epub.d.ts.map +1 -0
  111. package/dist/nodes/lib-epub.js +220 -0
  112. package/dist/nodes/lib-epub.js.map +1 -0
  113. package/dist/nodes/lib-excel.d.ts +74 -0
  114. package/dist/nodes/lib-excel.d.ts.map +1 -0
  115. package/dist/nodes/lib-excel.js +448 -0
  116. package/dist/nodes/lib-excel.js.map +1 -0
  117. package/dist/nodes/lib-graphql.d.ts +66 -0
  118. package/dist/nodes/lib-graphql.d.ts.map +1 -0
  119. package/dist/nodes/lib-graphql.js +333 -0
  120. package/dist/nodes/lib-graphql.js.map +1 -0
  121. package/dist/nodes/lib-grid.d.ts +27 -0
  122. package/dist/nodes/lib-grid.d.ts.map +1 -0
  123. package/dist/nodes/lib-grid.js +172 -0
  124. package/dist/nodes/lib-grid.js.map +1 -0
  125. package/dist/nodes/lib-html-parse.d.ts +108 -0
  126. package/dist/nodes/lib-html-parse.d.ts.map +1 -0
  127. package/dist/nodes/lib-html-parse.js +365 -0
  128. package/dist/nodes/lib-html-parse.js.map +1 -0
  129. package/dist/nodes/lib-http.d.ts +98 -0
  130. package/dist/nodes/lib-http.d.ts.map +1 -0
  131. package/dist/nodes/lib-http.js +267 -0
  132. package/dist/nodes/lib-http.js.map +1 -0
  133. package/dist/nodes/lib-image-color-grading.d.ts +3 -0
  134. package/dist/nodes/lib-image-color-grading.d.ts.map +1 -0
  135. package/dist/nodes/lib-image-color-grading.js +1386 -0
  136. package/dist/nodes/lib-image-color-grading.js.map +1 -0
  137. package/dist/nodes/lib-image-draw.d.ts +3 -0
  138. package/dist/nodes/lib-image-draw.d.ts.map +1 -0
  139. package/dist/nodes/lib-image-draw.js +470 -0
  140. package/dist/nodes/lib-image-draw.js.map +1 -0
  141. package/dist/nodes/lib-image-enhance.d.ts +3 -0
  142. package/dist/nodes/lib-image-enhance.d.ts.map +1 -0
  143. package/dist/nodes/lib-image-enhance.js +731 -0
  144. package/dist/nodes/lib-image-enhance.js.map +1 -0
  145. package/dist/nodes/lib-image-filter.d.ts +3 -0
  146. package/dist/nodes/lib-image-filter.d.ts.map +1 -0
  147. package/dist/nodes/lib-image-filter.js +684 -0
  148. package/dist/nodes/lib-image-filter.js.map +1 -0
  149. package/dist/nodes/lib-image-utils.d.ts +24 -0
  150. package/dist/nodes/lib-image-utils.d.ts.map +1 -0
  151. package/dist/nodes/lib-image-utils.js +150 -0
  152. package/dist/nodes/lib-image-utils.js.map +1 -0
  153. package/dist/nodes/lib-mail.d.ts +74 -0
  154. package/dist/nodes/lib-mail.d.ts.map +1 -0
  155. package/dist/nodes/lib-mail.js +452 -0
  156. package/dist/nodes/lib-mail.js.map +1 -0
  157. package/dist/nodes/lib-markdown.d.ts +70 -0
  158. package/dist/nodes/lib-markdown.d.ts.map +1 -0
  159. package/dist/nodes/lib-markdown.js +236 -0
  160. package/dist/nodes/lib-markdown.js.map +1 -0
  161. package/dist/nodes/lib-nlp.d.ts +98 -0
  162. package/dist/nodes/lib-nlp.d.ts.map +1 -0
  163. package/dist/nodes/lib-nlp.js +374 -0
  164. package/dist/nodes/lib-nlp.js.map +1 -0
  165. package/dist/nodes/lib-notion.d.ts +99 -0
  166. package/dist/nodes/lib-notion.d.ts.map +1 -0
  167. package/dist/nodes/lib-notion.js +601 -0
  168. package/dist/nodes/lib-notion.js.map +1 -0
  169. package/dist/nodes/lib-ocr.d.ts +31 -0
  170. package/dist/nodes/lib-ocr.d.ts.map +1 -0
  171. package/dist/nodes/lib-ocr.js +143 -0
  172. package/dist/nodes/lib-ocr.js.map +1 -0
  173. package/dist/nodes/lib-os.d.ts +312 -0
  174. package/dist/nodes/lib-os.d.ts.map +1 -0
  175. package/dist/nodes/lib-os.js +909 -0
  176. package/dist/nodes/lib-os.js.map +1 -0
  177. package/dist/nodes/lib-pdf.d.ts +142 -0
  178. package/dist/nodes/lib-pdf.d.ts.map +1 -0
  179. package/dist/nodes/lib-pdf.js +871 -0
  180. package/dist/nodes/lib-pdf.js.map +1 -0
  181. package/dist/nodes/lib-pptx.d.ts +29 -0
  182. package/dist/nodes/lib-pptx.d.ts.map +1 -0
  183. package/dist/nodes/lib-pptx.js +122 -0
  184. package/dist/nodes/lib-pptx.js.map +1 -0
  185. package/dist/nodes/lib-rss.d.ts +31 -0
  186. package/dist/nodes/lib-rss.d.ts.map +1 -0
  187. package/dist/nodes/lib-rss.js +163 -0
  188. package/dist/nodes/lib-rss.js.map +1 -0
  189. package/dist/nodes/lib-s3.d.ts +112 -0
  190. package/dist/nodes/lib-s3.d.ts.map +1 -0
  191. package/dist/nodes/lib-s3.js +521 -0
  192. package/dist/nodes/lib-s3.js.map +1 -0
  193. package/dist/nodes/lib-secret.d.ts +15 -0
  194. package/dist/nodes/lib-secret.d.ts.map +1 -0
  195. package/dist/nodes/lib-secret.js +42 -0
  196. package/dist/nodes/lib-secret.js.map +1 -0
  197. package/dist/nodes/lib-sqlite.d.ts +102 -0
  198. package/dist/nodes/lib-sqlite.d.ts.map +1 -0
  199. package/dist/nodes/lib-sqlite.js +532 -0
  200. package/dist/nodes/lib-sqlite.js.map +1 -0
  201. package/dist/nodes/lib-supabase.d.ts +86 -0
  202. package/dist/nodes/lib-supabase.d.ts.map +1 -0
  203. package/dist/nodes/lib-supabase.js +425 -0
  204. package/dist/nodes/lib-supabase.js.map +1 -0
  205. package/dist/nodes/lib-svg.d.ts +199 -0
  206. package/dist/nodes/lib-svg.d.ts.map +1 -0
  207. package/dist/nodes/lib-svg.js +1027 -0
  208. package/dist/nodes/lib-svg.js.map +1 -0
  209. package/dist/nodes/lib-tensorflow.d.ts +71 -0
  210. package/dist/nodes/lib-tensorflow.d.ts.map +1 -0
  211. package/dist/nodes/lib-tensorflow.js +351 -0
  212. package/dist/nodes/lib-tensorflow.js.map +1 -0
  213. package/dist/nodes/lib-twilio.d.ts +71 -0
  214. package/dist/nodes/lib-twilio.d.ts.map +1 -0
  215. package/dist/nodes/lib-twilio.js +469 -0
  216. package/dist/nodes/lib-twilio.js.map +1 -0
  217. package/dist/nodes/lib-validate.d.ts +84 -0
  218. package/dist/nodes/lib-validate.d.ts.map +1 -0
  219. package/dist/nodes/lib-validate.js +215 -0
  220. package/dist/nodes/lib-validate.js.map +1 -0
  221. package/dist/nodes/lib-video-download.d.ts +26 -0
  222. package/dist/nodes/lib-video-download.d.ts.map +1 -0
  223. package/dist/nodes/lib-video-download.js +299 -0
  224. package/dist/nodes/lib-video-download.js.map +1 -0
  225. package/dist/nodes/messaging.d.ts +88 -0
  226. package/dist/nodes/messaging.d.ts.map +1 -0
  227. package/dist/nodes/messaging.js +418 -0
  228. package/dist/nodes/messaging.js.map +1 -0
  229. package/dist/nodes/mistral.d.ts +83 -0
  230. package/dist/nodes/mistral.d.ts.map +1 -0
  231. package/dist/nodes/mistral.js +450 -0
  232. package/dist/nodes/mistral.js.map +1 -0
  233. package/dist/nodes/model3d/analysis.d.ts +13 -0
  234. package/dist/nodes/model3d/analysis.d.ts.map +1 -0
  235. package/dist/nodes/model3d/analysis.js +36 -0
  236. package/dist/nodes/model3d/analysis.js.map +1 -0
  237. package/dist/nodes/model3d/base.d.ts +11 -0
  238. package/dist/nodes/model3d/base.d.ts.map +1 -0
  239. package/dist/nodes/model3d/base.js +20 -0
  240. package/dist/nodes/model3d/base.js.map +1 -0
  241. package/dist/nodes/model3d/boolean-ops.d.ts +2 -0
  242. package/dist/nodes/model3d/boolean-ops.d.ts.map +1 -0
  243. package/dist/nodes/model3d/boolean-ops.js +172 -0
  244. package/dist/nodes/model3d/boolean-ops.js.map +1 -0
  245. package/dist/nodes/model3d/cleanup.d.ts +47 -0
  246. package/dist/nodes/model3d/cleanup.d.ts.map +1 -0
  247. package/dist/nodes/model3d/cleanup.js +136 -0
  248. package/dist/nodes/model3d/cleanup.js.map +1 -0
  249. package/dist/nodes/model3d/convert.d.ts +14 -0
  250. package/dist/nodes/model3d/convert.d.ts.map +1 -0
  251. package/dist/nodes/model3d/convert.js +64 -0
  252. package/dist/nodes/model3d/convert.js.map +1 -0
  253. package/dist/nodes/model3d/defaults.d.ts +34 -0
  254. package/dist/nodes/model3d/defaults.d.ts.map +1 -0
  255. package/dist/nodes/model3d/defaults.js +28 -0
  256. package/dist/nodes/model3d/defaults.js.map +1 -0
  257. package/dist/nodes/model3d/document-ops.d.ts +4 -0
  258. package/dist/nodes/model3d/document-ops.d.ts.map +1 -0
  259. package/dist/nodes/model3d/document-ops.js +40 -0
  260. package/dist/nodes/model3d/document-ops.js.map +1 -0
  261. package/dist/nodes/model3d/generation.d.ts +39 -0
  262. package/dist/nodes/model3d/generation.d.ts.map +1 -0
  263. package/dist/nodes/model3d/generation.js +179 -0
  264. package/dist/nodes/model3d/generation.js.map +1 -0
  265. package/dist/nodes/model3d/glb.d.ts +29 -0
  266. package/dist/nodes/model3d/glb.d.ts.map +1 -0
  267. package/dist/nodes/model3d/glb.js +255 -0
  268. package/dist/nodes/model3d/glb.js.map +1 -0
  269. package/dist/nodes/model3d/index.d.ts +20 -0
  270. package/dist/nodes/model3d/index.d.ts.map +1 -0
  271. package/dist/nodes/model3d/index.js +38 -0
  272. package/dist/nodes/model3d/index.js.map +1 -0
  273. package/dist/nodes/model3d/io.d.ts +38 -0
  274. package/dist/nodes/model3d/io.d.ts.map +1 -0
  275. package/dist/nodes/model3d/io.js +162 -0
  276. package/dist/nodes/model3d/io.js.map +1 -0
  277. package/dist/nodes/model3d/mesh-ops.d.ts +40 -0
  278. package/dist/nodes/model3d/mesh-ops.d.ts.map +1 -0
  279. package/dist/nodes/model3d/mesh-ops.js +775 -0
  280. package/dist/nodes/model3d/mesh-ops.js.map +1 -0
  281. package/dist/nodes/model3d/nodes.d.ts +2 -0
  282. package/dist/nodes/model3d/nodes.d.ts.map +1 -0
  283. package/dist/nodes/model3d/nodes.js +2 -0
  284. package/dist/nodes/model3d/nodes.js.map +1 -0
  285. package/dist/nodes/model3d/ops.d.ts +37 -0
  286. package/dist/nodes/model3d/ops.d.ts.map +1 -0
  287. package/dist/nodes/model3d/ops.js +137 -0
  288. package/dist/nodes/model3d/ops.js.map +1 -0
  289. package/dist/nodes/model3d/transforms.d.ts +49 -0
  290. package/dist/nodes/model3d/transforms.d.ts.map +1 -0
  291. package/dist/nodes/model3d/transforms.js +142 -0
  292. package/dist/nodes/model3d/transforms.js.map +1 -0
  293. package/dist/nodes/model3d/types.d.ts +81 -0
  294. package/dist/nodes/model3d/types.d.ts.map +1 -0
  295. package/dist/nodes/model3d/types.js +2 -0
  296. package/dist/nodes/model3d/types.js.map +1 -0
  297. package/dist/nodes/model3d/utils.d.ts +34 -0
  298. package/dist/nodes/model3d/utils.d.ts.map +1 -0
  299. package/dist/nodes/model3d/utils.js +185 -0
  300. package/dist/nodes/model3d/utils.js.map +1 -0
  301. package/dist/nodes/model3d.d.ts +2 -0
  302. package/dist/nodes/model3d.d.ts.map +1 -0
  303. package/dist/nodes/model3d.js +2 -0
  304. package/dist/nodes/model3d.js.map +1 -0
  305. package/dist/nodes/openai.d.ts +172 -0
  306. package/dist/nodes/openai.d.ts.map +1 -0
  307. package/dist/nodes/openai.js +1294 -0
  308. package/dist/nodes/openai.js.map +1 -0
  309. package/dist/nodes/output.d.ts +29 -0
  310. package/dist/nodes/output.d.ts.map +1 -0
  311. package/dist/nodes/output.js +87 -0
  312. package/dist/nodes/output.js.map +1 -0
  313. package/dist/nodes/sandbox.d.ts +67 -0
  314. package/dist/nodes/sandbox.d.ts.map +1 -0
  315. package/dist/nodes/sandbox.js +681 -0
  316. package/dist/nodes/sandbox.js.map +1 -0
  317. package/dist/nodes/search.d.ts +117 -0
  318. package/dist/nodes/search.d.ts.map +1 -0
  319. package/dist/nodes/search.js +607 -0
  320. package/dist/nodes/search.js.map +1 -0
  321. package/dist/nodes/team.d.ts +74 -0
  322. package/dist/nodes/team.d.ts.map +1 -0
  323. package/dist/nodes/team.js +304 -0
  324. package/dist/nodes/team.js.map +1 -0
  325. package/dist/nodes/text-extra.d.ts +633 -0
  326. package/dist/nodes/text-extra.d.ts.map +1 -0
  327. package/dist/nodes/text-extra.js +2226 -0
  328. package/dist/nodes/text-extra.js.map +1 -0
  329. package/dist/nodes/tool-agents.d.ts +334 -0
  330. package/dist/nodes/tool-agents.d.ts.map +1 -0
  331. package/dist/nodes/tool-agents.js +1861 -0
  332. package/dist/nodes/tool-agents.js.map +1 -0
  333. package/dist/nodes/triggers.d.ts +107 -0
  334. package/dist/nodes/triggers.d.ts.map +1 -0
  335. package/dist/nodes/triggers.js +706 -0
  336. package/dist/nodes/triggers.js.map +1 -0
  337. package/dist/nodes/vector.d.ts +168 -0
  338. package/dist/nodes/vector.d.ts.map +1 -0
  339. package/dist/nodes/vector.js +1121 -0
  340. package/dist/nodes/vector.js.map +1 -0
  341. package/dist/nodes/video.d.ts +387 -0
  342. package/dist/nodes/video.d.ts.map +1 -0
  343. package/dist/nodes/video.js +2361 -0
  344. package/dist/nodes/video.js.map +1 -0
  345. package/dist/nodes/workflow.d.ts +24 -0
  346. package/dist/nodes/workflow.d.ts.map +1 -0
  347. package/dist/nodes/workflow.js +146 -0
  348. package/dist/nodes/workflow.js.map +1 -0
  349. package/dist/nodes/workspace.d.ts +203 -0
  350. package/dist/nodes/workspace.d.ts.map +1 -0
  351. package/dist/nodes/workspace.js +795 -0
  352. package/dist/nodes/workspace.js.map +1 -0
  353. package/package.json +103 -0
@@ -0,0 +1,2784 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { createLogger } from "@nodetool-ai/config";
8
+ import { BaseNode, prop } from "@nodetool-ai/node-sdk";
9
+ import { MultiModeAgent, Tool as AgentTool } from "@nodetool-ai/agents";
10
+ const THREAD_STORE = new Map();
11
+ const log = createLogger("nodetool.base-nodes.agents");
12
+ const DEFAULT_SYSTEM_PROMPT = "You are a friendly assistant";
13
+ const EXTRACTOR_SYSTEM_PROMPT = [
14
+ "You are a precise structured data extractor.",
15
+ "Return exactly one JSON object and no additional prose.",
16
+ "Use only information present in the input."
17
+ ].join(" ");
18
+ const CLASSIFIER_SYSTEM_PROMPT = [
19
+ "You are a precise classifier.",
20
+ "Choose exactly one category from the allowed list.",
21
+ 'Return only JSON matching {"category":"<allowed-category>"} with no extra text.'
22
+ ].join(" ");
23
+ const SUMMARIZER_SYSTEM_PROMPT = "You are an expert summarizer. Produce a concise, accurate summary.";
24
+ function asText(value) {
25
+ if (typeof value === "string")
26
+ return value;
27
+ if (typeof value === "number" || typeof value === "boolean")
28
+ return String(value);
29
+ if (!value)
30
+ return "";
31
+ if (Array.isArray(value))
32
+ return value.map(asText).join(" ");
33
+ if (typeof value === "object") {
34
+ const msg = value;
35
+ if (typeof msg.content === "string")
36
+ return msg.content;
37
+ if (Array.isArray(msg.content)) {
38
+ return msg.content
39
+ .map((part) => (part && part.type === "text" ? (part.text ?? "") : ""))
40
+ .join(" ")
41
+ .trim();
42
+ }
43
+ return JSON.stringify(value);
44
+ }
45
+ return "";
46
+ }
47
+ function summarize(text, maxSentences) {
48
+ const parts = text
49
+ .split(/(?<=[.!?])\s+/)
50
+ .map((s) => s.trim())
51
+ .filter((s) => s.length > 0);
52
+ if (parts.length === 0)
53
+ return "";
54
+ return parts.slice(0, Math.max(1, maxSentences)).join(" ");
55
+ }
56
+ function tokenize(text) {
57
+ return text
58
+ .toLowerCase()
59
+ .split(/[^a-z0-9]+/)
60
+ .map((t) => t.trim())
61
+ .filter((t) => t.length > 0);
62
+ }
63
+ function extractJson(text) {
64
+ try {
65
+ const parsed = JSON.parse(text);
66
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
67
+ ? parsed
68
+ : null;
69
+ }
70
+ catch {
71
+ const start = text.indexOf("{");
72
+ const end = text.lastIndexOf("}");
73
+ if (start >= 0 && end > start) {
74
+ try {
75
+ const parsed = JSON.parse(text.slice(start, end + 1));
76
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
77
+ ? parsed
78
+ : null;
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ }
87
+ function makeThreadId() {
88
+ return `thread_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
89
+ }
90
+ function getCategories(value) {
91
+ if (!Array.isArray(value))
92
+ return [];
93
+ return value.map((v) => String(v)).filter((v) => v.trim().length > 0);
94
+ }
95
+ function getModelConfig(props) {
96
+ const model = (props.model ?? {}) ?? {};
97
+ return {
98
+ providerId: typeof model.provider === "string" ? model.provider : "",
99
+ modelId: typeof model.id === "string" ? model.id : ""
100
+ };
101
+ }
102
+ function hasProviderSupport(context, providerId, modelId) {
103
+ return (!!context &&
104
+ typeof context.getProvider === "function" &&
105
+ !!providerId &&
106
+ !!modelId);
107
+ }
108
+ async function generateProviderMessage(provider, args) {
109
+ const call = typeof provider.generateMessageTraced === "function"
110
+ ? provider.generateMessageTraced.bind(provider)
111
+ : provider.generateMessage.bind(provider);
112
+ const result = await call(args);
113
+ return messageContentText(result.content);
114
+ }
115
+ /**
116
+ * Call a provider with a result tool to get structured output.
117
+ * The model is forced to call the tool via toolChoice, and the
118
+ * parsed args are returned directly.
119
+ */
120
+ async function generateStructured(provider, args) {
121
+ const call = typeof provider.generateMessageTraced === "function"
122
+ ? provider.generateMessageTraced.bind(provider)
123
+ : provider.generateMessage.bind(provider);
124
+ const result = await call({
125
+ messages: args.messages,
126
+ model: args.model,
127
+ maxTokens: args.maxTokens,
128
+ tools: [
129
+ {
130
+ name: args.toolName,
131
+ description: args.toolDescription,
132
+ inputSchema: args.schema
133
+ }
134
+ ],
135
+ toolChoice: args.toolName
136
+ });
137
+ const tc = result.toolCalls?.[0];
138
+ if (tc && tc.name === args.toolName) {
139
+ return tc.args;
140
+ }
141
+ // Fallback: try to parse JSON from text content
142
+ return extractJson(messageContentText(result.content));
143
+ }
144
+ function normalizeProviderStreamItem(item) {
145
+ if (!item ||
146
+ typeof item !== "object" ||
147
+ !("type" in item) ||
148
+ item.type !== "chunk") {
149
+ return item;
150
+ }
151
+ const chunk = item;
152
+ if (typeof chunk.content_type === "string" && chunk.content_type.length > 0) {
153
+ return chunk;
154
+ }
155
+ return {
156
+ ...chunk,
157
+ content_type: "text"
158
+ };
159
+ }
160
+ export async function* streamProviderMessages(provider, args) {
161
+ const request = {
162
+ ...args,
163
+ messages: [...args.messages],
164
+ tools: args.tools ? [...args.tools] : undefined
165
+ };
166
+ if (typeof provider.generateMessagesTraced === "function") {
167
+ for await (const item of provider.generateMessagesTraced(request)) {
168
+ yield normalizeProviderStreamItem(item);
169
+ }
170
+ return;
171
+ }
172
+ if (typeof provider.generateMessages === "function") {
173
+ for await (const item of provider.generateMessages(request)) {
174
+ yield normalizeProviderStreamItem(item);
175
+ }
176
+ return;
177
+ }
178
+ const result = await provider.generateMessage(request);
179
+ const content = messageContentText(result.content);
180
+ if (content || (result.toolCalls?.length ?? 0) === 0) {
181
+ yield {
182
+ type: "chunk",
183
+ content,
184
+ content_type: "text",
185
+ done: true
186
+ };
187
+ }
188
+ for (const toolCall of result.toolCalls ?? []) {
189
+ yield toolCall;
190
+ }
191
+ }
192
+ /**
193
+ * Check whether a provider supports native agentic tool execution (MCP).
194
+ * When true, the provider handles tool calls internally via onToolCall callback.
195
+ */
196
+ function isAgenticProvider(provider) {
197
+ return (provider.provider === "claude_agent");
198
+ }
199
+ function parseCategory(raw, categories) {
200
+ if (categories.length === 0)
201
+ return "Unknown";
202
+ const parsed = extractJson(raw);
203
+ const categoryValue = typeof parsed?.category === "string" ? parsed.category : "";
204
+ for (const category of categories) {
205
+ if (categoryValue.trim().toLowerCase() === category.trim().toLowerCase()) {
206
+ return category;
207
+ }
208
+ }
209
+ const lowered = raw.toLowerCase();
210
+ for (const category of categories) {
211
+ if (category.toLowerCase() && lowered.includes(category.toLowerCase())) {
212
+ return category;
213
+ }
214
+ }
215
+ for (const fallback of ["other", "unknown"]) {
216
+ for (const category of categories) {
217
+ if (category.trim().toLowerCase() === fallback)
218
+ return category;
219
+ }
220
+ }
221
+ return categories[0];
222
+ }
223
+ function messageContentText(content) {
224
+ if (typeof content === "string")
225
+ return content;
226
+ if (!Array.isArray(content))
227
+ return asText(content);
228
+ return content
229
+ .map((part) => {
230
+ if (!part || typeof part !== "object")
231
+ return asText(part);
232
+ if (part.type === "text") {
233
+ return String(part.text ?? "");
234
+ }
235
+ return "";
236
+ })
237
+ .join("")
238
+ .trim();
239
+ }
240
+ function normalizeRole(role) {
241
+ if (role === "system" ||
242
+ role === "user" ||
243
+ role === "assistant" ||
244
+ role === "tool") {
245
+ return role;
246
+ }
247
+ return null;
248
+ }
249
+ function normalizeBinaryRef(value) {
250
+ if (!value || typeof value !== "object")
251
+ return null;
252
+ const record = value;
253
+ const out = {};
254
+ if (typeof record.uri === "string" && record.uri)
255
+ out.uri = record.uri;
256
+ if (record.data instanceof Uint8Array || typeof record.data === "string")
257
+ out.data = record.data;
258
+ if (typeof record.mimeType === "string" && record.mimeType)
259
+ out.mimeType = record.mimeType;
260
+ if (typeof record.mime_type === "string" && record.mime_type)
261
+ out.mimeType = record.mime_type;
262
+ return out.uri || out.data ? out : null;
263
+ }
264
+ function normalizeMessageContent(value) {
265
+ if (value == null || typeof value === "string")
266
+ return value ?? null;
267
+ if (!Array.isArray(value))
268
+ return asText(value);
269
+ const parts = [];
270
+ for (const part of value) {
271
+ if (!part || typeof part !== "object") {
272
+ const text = asText(part);
273
+ if (text)
274
+ parts.push({ type: "text", text });
275
+ continue;
276
+ }
277
+ const record = part;
278
+ const kind = typeof record.type === "string" ? record.type : "";
279
+ if (kind === "text") {
280
+ parts.push({ type: "text", text: asText(record.text ?? "") });
281
+ continue;
282
+ }
283
+ if (kind === "image" || kind === "image_url") {
284
+ const image = normalizeBinaryRef(record.image ?? record.image_url ?? record.imageUrl);
285
+ if (image)
286
+ parts.push({ type: "image_url", image });
287
+ continue;
288
+ }
289
+ if (kind === "audio") {
290
+ const audio = normalizeBinaryRef(record.audio);
291
+ if (audio)
292
+ parts.push({ type: "audio", audio });
293
+ continue;
294
+ }
295
+ const text = asText(part);
296
+ if (text)
297
+ parts.push({ type: "text", text });
298
+ }
299
+ return parts;
300
+ }
301
+ function normalizeToolCalls(value) {
302
+ if (!Array.isArray(value))
303
+ return null;
304
+ const toolCalls = value
305
+ .filter((item) => !!item && typeof item === "object")
306
+ .map((item, index) => ({
307
+ id: typeof item.id === "string" && item.id ? item.id : `tool_${index + 1}`,
308
+ name: typeof item.name === "string" ? item.name : "",
309
+ args: item.args && typeof item.args === "object"
310
+ ? item.args
311
+ : {}
312
+ }))
313
+ .filter((item) => item.name.length > 0);
314
+ return toolCalls.length > 0 ? toolCalls : null;
315
+ }
316
+ function normalizeMessage(value) {
317
+ if (!value || typeof value !== "object")
318
+ return null;
319
+ const record = value;
320
+ const role = normalizeRole(record.role);
321
+ if (!role)
322
+ return null;
323
+ return {
324
+ role,
325
+ content: normalizeMessageContent(record.content),
326
+ toolCalls: normalizeToolCalls(record.toolCalls ?? record.tool_calls),
327
+ toolCallId: typeof record.toolCallId === "string"
328
+ ? record.toolCallId
329
+ : typeof record.tool_call_id === "string"
330
+ ? record.tool_call_id
331
+ : null,
332
+ threadId: typeof record.threadId === "string"
333
+ ? record.threadId
334
+ : typeof record.thread_id === "string"
335
+ ? record.thread_id
336
+ : null
337
+ };
338
+ }
339
+ function threadMessages(threadId) {
340
+ const thread = THREAD_STORE.get(threadId);
341
+ if (!thread)
342
+ return [];
343
+ return thread.messages.map((message) => ({ ...message }));
344
+ }
345
+ function logThreadWarning(message, error, details) {
346
+ if (process.env["NODE_ENV"] === "test")
347
+ return;
348
+ console.warn(`[AgentNode] ${message}`, {
349
+ ...details,
350
+ error: String(error)
351
+ });
352
+ }
353
+ function buildUserMessage(prompt, image, audio) {
354
+ const content = [{ type: "text", text: prompt }];
355
+ const imageRef = normalizeBinaryRef(image);
356
+ if (imageRef) {
357
+ content.push({ type: "image_url", image: imageRef });
358
+ }
359
+ const audioRef = normalizeBinaryRef(audio);
360
+ if (audioRef) {
361
+ content.push({ type: "audio", audio: audioRef });
362
+ }
363
+ return { role: "user", content };
364
+ }
365
+ async function loadThreadMessages(context, threadId) {
366
+ if (!threadId)
367
+ return [];
368
+ const threadedContext = context;
369
+ const getMessages = threadedContext?.get_messages?.bind(threadedContext) ??
370
+ threadedContext?.getThreadMessages?.bind(threadedContext);
371
+ if (getMessages) {
372
+ try {
373
+ const result = await getMessages(threadId, 1000, null, false);
374
+ const messages = (result.messages ?? [])
375
+ .map((item) => normalizeMessage(item))
376
+ .filter((message) => message !== null && message.role !== "system");
377
+ log.info("Agent thread history loaded from context", {
378
+ threadId,
379
+ messageCount: messages.length
380
+ });
381
+ return messages;
382
+ }
383
+ catch (error) {
384
+ logThreadWarning("Failed to load thread messages", error, { threadId });
385
+ }
386
+ }
387
+ const fallbackMessages = threadMessages(threadId).filter((message) => message.role !== "system");
388
+ log.info("Agent thread history loaded from fallback store", {
389
+ threadId,
390
+ messageCount: fallbackMessages.length
391
+ });
392
+ return fallbackMessages;
393
+ }
394
+ async function saveThreadMessage(context, threadId, message) {
395
+ if (!threadId)
396
+ return;
397
+ const threadedContext = context;
398
+ const createMessage = threadedContext?.create_message?.bind(threadedContext) ??
399
+ threadedContext?.createMessage?.bind(threadedContext);
400
+ if (createMessage) {
401
+ try {
402
+ await createMessage({
403
+ thread_id: threadId,
404
+ role: message.role,
405
+ content: message.content ?? null,
406
+ tool_calls: message.toolCalls ?? null,
407
+ tool_call_id: message.toolCallId ?? null
408
+ });
409
+ log.info("Agent thread message saved via context", {
410
+ threadId,
411
+ role: message.role,
412
+ hasToolCalls: (message.toolCalls?.length ?? 0) > 0,
413
+ textLength: messageContentText(message.content).length
414
+ });
415
+ return;
416
+ }
417
+ catch (error) {
418
+ logThreadWarning("Failed to save thread message", error, {
419
+ threadId,
420
+ role: message.role
421
+ });
422
+ }
423
+ }
424
+ const thread = THREAD_STORE.get(threadId) ?? {
425
+ id: threadId,
426
+ title: "Agent Conversation",
427
+ messages: []
428
+ };
429
+ thread.messages.push({
430
+ ...message,
431
+ threadId
432
+ });
433
+ THREAD_STORE.set(threadId, thread);
434
+ log.info("Agent thread message saved via fallback store", {
435
+ threadId,
436
+ role: message.role,
437
+ threadSize: thread.messages.length,
438
+ hasToolCalls: (message.toolCalls?.length ?? 0) > 0,
439
+ textLength: messageContentText(message.content).length
440
+ });
441
+ }
442
+ export function isChunkItem(item) {
443
+ return (!!item &&
444
+ typeof item === "object" &&
445
+ "type" in item &&
446
+ item.type === "chunk");
447
+ }
448
+ export function isToolCallItem(item) {
449
+ return (!!item &&
450
+ typeof item === "object" &&
451
+ "id" in item &&
452
+ "name" in item &&
453
+ !("type" in item));
454
+ }
455
+ function normalizeTools(value) {
456
+ if (!Array.isArray(value))
457
+ return [];
458
+ return value.filter((tool) => !!tool &&
459
+ typeof tool === "object" &&
460
+ typeof tool.name === "string");
461
+ }
462
+ export function toProviderTools(tools) {
463
+ return tools.map((tool) => typeof tool.toProviderTool === "function"
464
+ ? tool.toProviderTool()
465
+ : {
466
+ name: tool.name,
467
+ description: tool.description,
468
+ inputSchema: tool.inputSchema
469
+ });
470
+ }
471
+ export function serializeToolResult(value) {
472
+ if (value == null)
473
+ return value;
474
+ if (Array.isArray(value))
475
+ return value.map(serializeToolResult);
476
+ if (typeof value !== "object")
477
+ return value;
478
+ if (value instanceof Uint8Array) {
479
+ return Buffer.from(value).toString("base64");
480
+ }
481
+ const record = value;
482
+ return Object.fromEntries(Object.entries(record).map(([key, item]) => [
483
+ key,
484
+ serializeToolResult(item)
485
+ ]));
486
+ }
487
+ // ---------------------------------------------------------------------------
488
+ // Control tool support for Agent nodes with outgoing control edges
489
+ // ---------------------------------------------------------------------------
490
+ /**
491
+ * Marker symbol to identify control tools built from _control_context.
492
+ */
493
+ const CONTROL_TOOL_MARKER = Symbol("controlTool");
494
+ function isControlTool(tool) {
495
+ return (CONTROL_TOOL_MARKER in tool &&
496
+ tool[CONTROL_TOOL_MARKER] === true);
497
+ }
498
+ /** Opening tag and closers models may emit (legacy `</think>` was a typo). */
499
+ const REDACTED_THINKING_OPEN = "<think>";
500
+ const REDACTED_THINKING_CLOSES = [
501
+ "</think>",
502
+ "</" + "think>"
503
+ ];
504
+ function findEarliestThinkClose(buf) {
505
+ let best = null;
506
+ for (const c of REDACTED_THINKING_CLOSES) {
507
+ const i = buf.indexOf(c);
508
+ if (i !== -1 && (best === null || i < best.idx)) {
509
+ best = { idx: i, len: c.length };
510
+ }
511
+ }
512
+ return best;
513
+ }
514
+ /** Longest suffix of buf that is a proper prefix of one of the candidate strings (for streaming). */
515
+ function holdSuffixForPartialTag(buf, candidates) {
516
+ const maxCheck = Math.max(0, ...candidates.map((c) => Math.max(0, c.length - 1)));
517
+ const limit = Math.min(buf.length, maxCheck);
518
+ for (let len = limit; len >= 1; len--) {
519
+ const suf = buf.slice(-len);
520
+ if (candidates.some((c) => c.startsWith(suf)))
521
+ return len;
522
+ }
523
+ return 0;
524
+ }
525
+ function extractThinkTags(text) {
526
+ const parts = [];
527
+ const re = /<think>([\s\S]*?)<\/(?:redacted_thinking|think)>/g;
528
+ let cleaned = text.replace(re, (_, content) => {
529
+ parts.push(content.trim());
530
+ return "";
531
+ });
532
+ const orphan = cleaned.match(/<think>([\s\S]*)$/);
533
+ if (orphan && orphan.index !== undefined) {
534
+ parts.push(orphan[1].trim());
535
+ cleaned = cleaned.slice(0, orphan.index);
536
+ }
537
+ return { thinking: parts.filter((p) => p.length > 0).join("\n\n"), text: cleaned.trim() };
538
+ }
539
+ /**
540
+ * Splits streamed text so `<think>…` never appears on the text channel:
541
+ * reasoning goes to `thinking`, user-visible text to `text` chunks only.
542
+ */
543
+ class RedactedThinkingStreamSplitter {
544
+ buf = "";
545
+ inThink = false;
546
+ *feed(incoming) {
547
+ if (!incoming)
548
+ return;
549
+ this.buf += incoming;
550
+ while (true) {
551
+ if (!this.inThink) {
552
+ const openIdx = this.buf.indexOf(REDACTED_THINKING_OPEN);
553
+ if (openIdx === -1) {
554
+ const keep = holdSuffixForPartialTag(this.buf, [REDACTED_THINKING_OPEN]);
555
+ if (this.buf.length > keep) {
556
+ const emitEnd = this.buf.length - keep;
557
+ const out = this.buf.slice(0, emitEnd);
558
+ this.buf = this.buf.slice(emitEnd);
559
+ if (out)
560
+ yield { kind: "text", content: out };
561
+ }
562
+ return;
563
+ }
564
+ if (openIdx > 0) {
565
+ const out = this.buf.slice(0, openIdx);
566
+ if (out)
567
+ yield { kind: "text", content: out };
568
+ }
569
+ this.buf = this.buf.slice(openIdx + REDACTED_THINKING_OPEN.length);
570
+ this.inThink = true;
571
+ continue;
572
+ }
573
+ const close = findEarliestThinkClose(this.buf);
574
+ if (!close) {
575
+ const keep = holdSuffixForPartialTag(this.buf, REDACTED_THINKING_CLOSES);
576
+ if (this.buf.length > keep) {
577
+ const emitEnd = this.buf.length - keep;
578
+ const out = this.buf.slice(0, emitEnd);
579
+ this.buf = this.buf.slice(emitEnd);
580
+ if (out)
581
+ yield { kind: "thinking", content: out };
582
+ }
583
+ return;
584
+ }
585
+ const thinkBody = this.buf.slice(0, close.idx);
586
+ this.buf = this.buf.slice(close.idx + close.len);
587
+ this.inThink = false;
588
+ if (thinkBody)
589
+ yield { kind: "thinking", content: thinkBody };
590
+ }
591
+ }
592
+ *flush() {
593
+ if (!this.buf && !this.inThink)
594
+ return;
595
+ if (this.inThink) {
596
+ if (this.buf)
597
+ yield { kind: "thinking", content: this.buf };
598
+ }
599
+ else if (this.buf) {
600
+ yield { kind: "text", content: this.buf };
601
+ }
602
+ this.buf = "";
603
+ this.inThink = false;
604
+ }
605
+ }
606
+ function* yieldSplitThinkChunks(item, rawPiece, splitter) {
607
+ for (const part of splitter.feed(rawPiece)) {
608
+ if (part.kind === "text" && part.content.length > 0) {
609
+ yield {
610
+ chunk: { ...item, content: part.content },
611
+ thinking: null,
612
+ text: null,
613
+ audio: null
614
+ };
615
+ }
616
+ else if (part.kind === "thinking" && part.content.length > 0) {
617
+ yield {
618
+ chunk: null,
619
+ thinking: {
620
+ type: "chunk",
621
+ content: part.content,
622
+ thinking: true
623
+ },
624
+ text: null,
625
+ audio: null
626
+ };
627
+ }
628
+ }
629
+ }
630
+ /**
631
+ * Sanitize a node title to a valid tool name (snake_case, max 64 chars).
632
+ */
633
+ function sanitizeControlToolName(name) {
634
+ if (!name)
635
+ return "control_node";
636
+ let s = name
637
+ .replace(/[^a-zA-Z0-9]/g, "_")
638
+ .replace(/([a-z])([A-Z])/g, "$1_$2")
639
+ .toLowerCase()
640
+ .replace(/_+/g, "_")
641
+ .replace(/^_+|_+$/g, "");
642
+ if (!s)
643
+ return "control_node";
644
+ if (s.length > 64)
645
+ s = s.slice(0, 64);
646
+ return s;
647
+ }
648
+ /**
649
+ * Build ControlToolLike instances from `_control_context` input.
650
+ * These tools allow the agent's LLM to call controlled nodes via tool-calling.
651
+ */
652
+ function buildControlTools(controlContext) {
653
+ if (!controlContext || typeof controlContext !== "object")
654
+ return [];
655
+ const tools = [];
656
+ for (const [targetId, info] of Object.entries(controlContext)) {
657
+ if (!info || typeof info !== "object")
658
+ continue;
659
+ const nodeInfo = info;
660
+ const nodeTitle = String(nodeInfo.node_title ?? nodeInfo.node_type ?? targetId);
661
+ const toolName = sanitizeControlToolName(nodeTitle);
662
+ // Build input schema from control_actions.run.properties
663
+ const actions = (nodeInfo.control_actions ?? {});
664
+ const runAction = (actions.run ?? {});
665
+ const rawProperties = (runAction.properties ?? {});
666
+ const properties = {};
667
+ for (const [key, schema] of Object.entries(rawProperties)) {
668
+ if (typeof schema === "object" && schema !== null) {
669
+ properties[key] = { ...schema };
670
+ }
671
+ else {
672
+ properties[key] = { type: "string", description: String(schema) };
673
+ }
674
+ }
675
+ const inputSchema = {
676
+ type: "object",
677
+ properties,
678
+ required: []
679
+ };
680
+ let description = `Control ${nodeTitle}: trigger execution with optional property overrides`;
681
+ const propNames = Object.keys(properties);
682
+ if (propNames.length > 0) {
683
+ description += `. Available properties: ${propNames.join(", ")}`;
684
+ }
685
+ tools.push({
686
+ [CONTROL_TOOL_MARKER]: true,
687
+ targetNodeId: targetId,
688
+ name: toolName,
689
+ description,
690
+ inputSchema,
691
+ // Stub process — actual execution goes through sendControlEvent
692
+ async process(_ctx, _params) {
693
+ return { status: "dispatched", target: targetId };
694
+ },
695
+ toProviderTool() {
696
+ return { name: toolName, description, inputSchema };
697
+ }
698
+ });
699
+ }
700
+ return tools;
701
+ }
702
+ /**
703
+ * Adapter that wraps a ToolLike (from base-nodes) as an AgentTool (from @nodetool-ai/agents).
704
+ * This bridges the tool systems so MultiModeAgent can use tools defined in the node graph.
705
+ */
706
+ class ToolLikeAdapter extends AgentTool {
707
+ name;
708
+ description;
709
+ inputSchema;
710
+ _process;
711
+ constructor(toolLike) {
712
+ super();
713
+ this.name = toolLike.name;
714
+ this.description = toolLike.description ?? "";
715
+ this.inputSchema = toolLike.inputSchema ?? {
716
+ type: "object",
717
+ properties: {}
718
+ };
719
+ this._process =
720
+ toolLike.process ??
721
+ (async () => ({ error: "Tool has no process implementation" }));
722
+ }
723
+ async process(context, params) {
724
+ return this._process(context, params);
725
+ }
726
+ }
727
+ export async function runAgentLoop(options) {
728
+ const { context, providerId, modelId, systemPrompt, prompt, tools, contentParts, maxTokens = 4096, maxIterations = 10 } = options;
729
+ if (!context || typeof context.getProvider !== "function") {
730
+ throw new Error("Processing context with provider access is required");
731
+ }
732
+ const userContent = [{ type: "text", text: prompt }];
733
+ if (contentParts) {
734
+ userContent.push(...contentParts);
735
+ }
736
+ const messages = [
737
+ { role: "system", content: systemPrompt },
738
+ { role: "user", content: userContent }
739
+ ];
740
+ const providerTools = tools.length > 0 ? toProviderTools(tools) : undefined;
741
+ const provider = await context.getProvider(providerId);
742
+ let lastAssistantText = "";
743
+ // --- Agentic provider fast-path ---
744
+ if (isAgenticProvider(provider) && tools.length > 0) {
745
+ const onToolCall = async (name, args) => {
746
+ const tool = tools.find((t) => t.name === name);
747
+ if (!tool || typeof tool.process !== "function") {
748
+ return JSON.stringify({ error: `Unknown tool: ${name}` });
749
+ }
750
+ try {
751
+ const result = await tool.process(context, args);
752
+ return typeof result === "string"
753
+ ? result
754
+ : JSON.stringify(serializeToolResult(result));
755
+ }
756
+ catch (e) {
757
+ return JSON.stringify({ error: String(e) });
758
+ }
759
+ };
760
+ let assistantText = "";
761
+ for await (const item of streamProviderMessages(provider, {
762
+ messages,
763
+ model: modelId,
764
+ tools: providerTools,
765
+ maxTokens,
766
+ threadId: options.threadId,
767
+ onToolCall
768
+ })) {
769
+ if (isChunkItem(item) && !item.thinking) {
770
+ assistantText += item.content ?? "";
771
+ }
772
+ }
773
+ if (assistantText) {
774
+ lastAssistantText = assistantText;
775
+ messages.push({
776
+ role: "assistant",
777
+ content: [{ type: "text", text: assistantText }]
778
+ });
779
+ }
780
+ return { text: lastAssistantText, messages };
781
+ }
782
+ // --- Standard multi-iteration loop ---
783
+ let iteration = 0;
784
+ let shouldContinue = true;
785
+ while (shouldContinue && iteration < maxIterations) {
786
+ shouldContinue = false;
787
+ iteration++;
788
+ const assistantToolCalls = [];
789
+ let assistantText = "";
790
+ for await (const item of streamProviderMessages(provider, {
791
+ messages,
792
+ model: modelId,
793
+ tools: providerTools,
794
+ maxTokens,
795
+ threadId: options.threadId
796
+ })) {
797
+ if (isChunkItem(item)) {
798
+ if (!item.thinking) {
799
+ assistantText += item.content ?? "";
800
+ }
801
+ }
802
+ if (isToolCallItem(item)) {
803
+ assistantToolCalls.push(item);
804
+ }
805
+ }
806
+ if (assistantText) {
807
+ lastAssistantText = assistantText;
808
+ }
809
+ if (assistantText || assistantToolCalls.length > 0) {
810
+ const assistantMsg = {
811
+ role: "assistant",
812
+ content: [{ type: "text", text: assistantText }],
813
+ toolCalls: assistantToolCalls.length > 0 ? assistantToolCalls : null
814
+ };
815
+ // Propagate raw Gemini parts for thought signature replay
816
+ const rawParts = assistantToolCalls.find((tc) => tc._rawGeminiParts)?._rawGeminiParts;
817
+ if (rawParts) {
818
+ assistantMsg._rawGeminiParts = rawParts;
819
+ }
820
+ messages.push(assistantMsg);
821
+ }
822
+ for (const toolCall of assistantToolCalls) {
823
+ const tool = tools.find((t) => t.name === toolCall.name);
824
+ if (!tool || typeof tool.process !== "function") {
825
+ messages.push({
826
+ role: "tool",
827
+ toolCallId: toolCall.id,
828
+ content: JSON.stringify({ error: `Unknown tool: ${toolCall.name}` })
829
+ });
830
+ shouldContinue = true;
831
+ continue;
832
+ }
833
+ const result = await tool.process(context, toolCall.args);
834
+ messages.push({
835
+ role: "tool",
836
+ toolCallId: toolCall.id,
837
+ content: JSON.stringify(serializeToolResult(result))
838
+ });
839
+ shouldContinue = true;
840
+ }
841
+ }
842
+ if (iteration >= maxIterations) {
843
+ log.warn("runAgentLoop reached max iterations", {
844
+ maxIterations,
845
+ providerId,
846
+ modelId
847
+ });
848
+ }
849
+ return { text: lastAssistantText, messages };
850
+ }
851
+ function getStructuredOutputSchema(node) {
852
+ const outputs = node._dynamic_outputs;
853
+ if (!outputs || typeof outputs !== "object" || Array.isArray(outputs))
854
+ return null;
855
+ const properties = {};
856
+ const required = [];
857
+ for (const [name, spec] of Object.entries(outputs)) {
858
+ required.push(name);
859
+ const value = spec && typeof spec === "object" ? spec : {};
860
+ const declared = typeof value.type === "string" ? value.type.toLowerCase() : "str";
861
+ let type = "string";
862
+ if (["int", "integer"].includes(declared))
863
+ type = "integer";
864
+ else if (["float", "number"].includes(declared))
865
+ type = "number";
866
+ else if (["bool", "boolean"].includes(declared))
867
+ type = "boolean";
868
+ else if (["list", "array"].includes(declared))
869
+ type = "array";
870
+ else if (["dict", "object"].includes(declared))
871
+ type = "object";
872
+ properties[name] = { type };
873
+ }
874
+ return {
875
+ type: "object",
876
+ additionalProperties: false,
877
+ required,
878
+ properties
879
+ };
880
+ }
881
+ function hasContentType(message, type) {
882
+ return Array.isArray(message?.content)
883
+ ? message.content.some((part) => part.type === type)
884
+ : false;
885
+ }
886
+ export class SummarizerNode extends BaseNode {
887
+ static nodeType = "nodetool.agents.Summarizer";
888
+ static title = "Summarizer";
889
+ static description = "Generate concise summaries of text content using LLM providers with streaming output.\n text, summarization, nlp, content, streaming\n\n Specialized for creating high-quality summaries with real-time streaming:\n - Condensing long documents into key points\n - Creating executive summaries with live output\n - Extracting main ideas from text as they're generated\n - Maintaining factual accuracy while reducing length";
890
+ static metadataOutputTypes = {
891
+ text: "str",
892
+ chunk: "chunk"
893
+ };
894
+ static basicFields = ["text", "model", "image", "audio"];
895
+ static recommendedModels = [
896
+ {
897
+ id: "phi3.5:latest",
898
+ type: "llama_model",
899
+ name: "Phi3.5",
900
+ repo_id: "phi3.5:latest",
901
+ description: "Lightweight 3.8B model tuned for crisp instruction following and compact summaries on modest hardware.",
902
+ size_on_disk: 2362232012
903
+ },
904
+ {
905
+ id: "mistral-small:latest",
906
+ type: "llama_model",
907
+ name: "Mistral Small",
908
+ repo_id: "mistral-small:latest",
909
+ description: "Efficient mixture-of-experts model that delivers reliable abstractive summaries with low latency.",
910
+ size_on_disk: 7730941132
911
+ },
912
+ {
913
+ id: "llama3.2:3b",
914
+ type: "llama_model",
915
+ name: "Llama 3.2 - 3B",
916
+ repo_id: "llama3.2:3b",
917
+ description: "Compact Llama variant that balances coverage and brevity for everyday summarization workloads.",
918
+ size_on_disk: 2040109465
919
+ },
920
+ {
921
+ id: "gemma3:4b",
922
+ type: "llama_model",
923
+ name: "Gemma3 - 4B",
924
+ repo_id: "gemma3:4b",
925
+ description: "Google's 4B multimodal model performs strong factual summaries while staying resource friendly.",
926
+ size_on_disk: 2791728742
927
+ },
928
+ {
929
+ id: "granite3.1-moe:3b",
930
+ type: "llama_model",
931
+ name: "Granite 3.1 MOE - 3B",
932
+ repo_id: "granite3.1-moe:3b",
933
+ description: "IBM Granite MoE delivers focused meeting notes and bullet summaries with minimal VRAM needs.",
934
+ size_on_disk: 1717986918
935
+ },
936
+ {
937
+ id: "qwen3:4b",
938
+ type: "llama_model",
939
+ name: "Qwen3 - 4B",
940
+ repo_id: "qwen3:4b",
941
+ description: "Qwen3 4B offers multilingual summarization with tight, well-structured outputs.",
942
+ size_on_disk: 2684354560
943
+ },
944
+ {
945
+ id: "ggml-org/gemma-3-4b-it-GGUF:gemma-3-4b-it-Q4_K_M.gguf",
946
+ type: "llama_cpp_model",
947
+ name: "Gemma 3 4B IT (GGUF)",
948
+ repo_id: "ggml-org/gemma-3-4b-it-GGUF",
949
+ path: "gemma-3-4b-it-Q4_K_M.gguf",
950
+ description: "Efficient Gemma 3 for summarization via llama.cpp.",
951
+ size_on_disk: 3113851289,
952
+ pipeline_tag: "image-text-to-text",
953
+ tags: [
954
+ "gguf",
955
+ "image-text-to-text",
956
+ "arxiv:1905.07830",
957
+ "arxiv:1905.10044",
958
+ "arxiv:1911.11641",
959
+ "arxiv:1904.09728",
960
+ "arxiv:1705.03551",
961
+ "arxiv:1911.01547",
962
+ "arxiv:1907.10641",
963
+ "arxiv:1903.00161",
964
+ "arxiv:2009.03300",
965
+ "arxiv:2304.06364",
966
+ "arxiv:2103.03874",
967
+ "arxiv:2110.14168",
968
+ "arxiv:2311.12022",
969
+ "arxiv:2108.07732",
970
+ "arxiv:2107.03374",
971
+ "arxiv:2210.03057",
972
+ "arxiv:2106.03193",
973
+ "arxiv:1910.11856",
974
+ "arxiv:2502.12404",
975
+ "arxiv:2502.21228",
976
+ "arxiv:2404.16816",
977
+ "arxiv:2104.12756",
978
+ "arxiv:2311.16502",
979
+ "arxiv:2203.10244",
980
+ "arxiv:2404.12390",
981
+ "arxiv:1810.12440",
982
+ "arxiv:1908.02660",
983
+ "arxiv:2312.11805",
984
+ "base_model:google/gemma-3-4b-it",
985
+ "base_model:quantized:google/gemma-3-4b-it",
986
+ "license:gemma",
987
+ "endpoints_compatible",
988
+ "region:us",
989
+ "conversational"
990
+ ],
991
+ has_model_index: false,
992
+ downloads: 25779,
993
+ likes: 48
994
+ }
995
+ ];
996
+ static isStreamingOutput = true;
997
+ async process(context) {
998
+ const text = asText(this.text ?? this.text ?? "");
999
+ const maxSentences = Number(this.max_sentences ?? 3);
1000
+ const { providerId, modelId } = getModelConfig(this.serialize());
1001
+ if (hasProviderSupport(context, providerId, modelId)) {
1002
+ const provider = await context.getProvider(providerId);
1003
+ const result = await generateProviderMessage(provider, {
1004
+ model: modelId,
1005
+ maxTokens: Number.isFinite(maxSentences)
1006
+ ? Math.max(64, maxSentences * 128)
1007
+ : 384,
1008
+ messages: [
1009
+ { role: "system", content: SUMMARIZER_SYSTEM_PROMPT },
1010
+ {
1011
+ role: "user",
1012
+ content: `Summarize the following text in about ${Math.max(1, maxSentences)} sentence(s):\n\n${text}`
1013
+ }
1014
+ ]
1015
+ });
1016
+ return { text: result, output: result };
1017
+ }
1018
+ const result = summarize(text, Number.isFinite(maxSentences) ? maxSentences : 3);
1019
+ return { text: result, output: result };
1020
+ }
1021
+ }
1022
+ __decorate([
1023
+ prop({
1024
+ type: "str",
1025
+ default: "\n You are an expert summarizer. Your task is to create clear, accurate, and concise summaries using Markdown for structuring.\n Follow these guidelines:\n 1. Identify and include only the most important information.\n 2. Maintain factual accuracy - do not add or modify information.\n 3. Use clear, direct language.\n 4. Aim for approximately {self.max_tokens} tokens.\n ",
1026
+ title: "System Prompt",
1027
+ description: "The system prompt for the summarizer"
1028
+ })
1029
+ ], SummarizerNode.prototype, "system_prompt", void 0);
1030
+ __decorate([
1031
+ prop({
1032
+ type: "language_model",
1033
+ default: {
1034
+ type: "language_model",
1035
+ provider: "empty",
1036
+ id: "",
1037
+ name: "",
1038
+ path: null,
1039
+ supported_tasks: []
1040
+ },
1041
+ title: "Model",
1042
+ description: "Model to use for summarization"
1043
+ })
1044
+ ], SummarizerNode.prototype, "model", void 0);
1045
+ __decorate([
1046
+ prop({
1047
+ type: "str",
1048
+ default: "",
1049
+ title: "Text",
1050
+ description: "The text to summarize"
1051
+ })
1052
+ ], SummarizerNode.prototype, "text", void 0);
1053
+ __decorate([
1054
+ prop({
1055
+ type: "image",
1056
+ default: {
1057
+ type: "image",
1058
+ uri: "",
1059
+ asset_id: null,
1060
+ data: null,
1061
+ metadata: null
1062
+ },
1063
+ title: "Image",
1064
+ description: "Optional image to condition the summary"
1065
+ })
1066
+ ], SummarizerNode.prototype, "image", void 0);
1067
+ __decorate([
1068
+ prop({
1069
+ type: "audio",
1070
+ default: {
1071
+ type: "audio",
1072
+ uri: "",
1073
+ asset_id: null,
1074
+ data: null,
1075
+ metadata: null
1076
+ },
1077
+ title: "Audio",
1078
+ description: "Optional audio to condition the summary"
1079
+ })
1080
+ ], SummarizerNode.prototype, "audio", void 0);
1081
+ export class CreateThreadNode extends BaseNode {
1082
+ static nodeType = "nodetool.agents.CreateThread";
1083
+ static title = "Create Thread";
1084
+ static description = "Create a new conversation thread and return its ID.\n threads, chat, conversation, context\n\n Use this to seed a thread_id that downstream Agent nodes can reuse for\n persistent history across the graph or multiple runs.";
1085
+ static metadataOutputTypes = {
1086
+ thread_id: "str"
1087
+ };
1088
+ async process() {
1089
+ const requested = String(this.thread_id ?? this.thread_id ?? "").trim();
1090
+ if (requested) {
1091
+ if (!THREAD_STORE.has(requested)) {
1092
+ THREAD_STORE.set(requested, {
1093
+ id: requested,
1094
+ title: String(this.title ?? this.title ?? "Agent Conversation"),
1095
+ messages: []
1096
+ });
1097
+ }
1098
+ return { thread_id: requested };
1099
+ }
1100
+ const id = makeThreadId();
1101
+ THREAD_STORE.set(id, {
1102
+ id,
1103
+ title: String(this.title ?? this.title ?? "Agent Conversation"),
1104
+ messages: []
1105
+ });
1106
+ return { thread_id: id };
1107
+ }
1108
+ }
1109
+ __decorate([
1110
+ prop({
1111
+ type: "str",
1112
+ default: "Agent Conversation",
1113
+ title: "Title",
1114
+ description: "Optional title for the new thread"
1115
+ })
1116
+ ], CreateThreadNode.prototype, "title", void 0);
1117
+ __decorate([
1118
+ prop({
1119
+ type: "str",
1120
+ default: "",
1121
+ title: "Thread Id",
1122
+ description: "Optional custom thread ID. If provided and owned by the user, it will be reused; otherwise a new thread is created."
1123
+ })
1124
+ ], CreateThreadNode.prototype, "thread_id", void 0);
1125
+ export class ExtractorNode extends BaseNode {
1126
+ static nodeType = "nodetool.agents.Extractor";
1127
+ static title = "Extractor";
1128
+ static description = "Extract structured data from text content using LLM providers.\n data-extraction, structured-data, nlp, parsing\n\n Specialized for extracting structured information:\n - Converting unstructured text into structured data\n - Identifying and extracting specific fields from documents\n - Parsing text according to predefined schemas\n - Creating structured records from natural language content";
1129
+ static basicFields = ["text", "model", "image", "audio"];
1130
+ static supportsDynamicOutputs = true;
1131
+ static recommendedModels = [
1132
+ {
1133
+ id: "phi3.5:latest",
1134
+ type: "llama_model",
1135
+ name: "Phi3.5",
1136
+ repo_id: "phi3.5:latest",
1137
+ description: "Small Phi variant excels at JSON-style outputs and faithful field extraction on laptops.",
1138
+ size_on_disk: 2362232012
1139
+ },
1140
+ {
1141
+ id: "mistral-small:latest",
1142
+ type: "llama_model",
1143
+ name: "Mistral Small",
1144
+ repo_id: "mistral-small:latest",
1145
+ description: "MoE architecture keeps structured extraction consistent while staying resource efficient.",
1146
+ size_on_disk: 7730941132
1147
+ },
1148
+ {
1149
+ id: "granite3.1-moe:3b",
1150
+ type: "llama_model",
1151
+ name: "Granite 3.1 MOE - 3B",
1152
+ repo_id: "granite3.1-moe:3b",
1153
+ description: "Granite MoE models are tuned for business document parsing and schema-following tasks.",
1154
+ size_on_disk: 1717986918
1155
+ },
1156
+ {
1157
+ id: "gemma3:4b",
1158
+ type: "llama_model",
1159
+ name: "Gemma3 - 4B",
1160
+ repo_id: "gemma3:4b",
1161
+ description: "Gemma 3 4B handles multilingual extraction and adheres to required JSON schemas.",
1162
+ size_on_disk: 2791728742
1163
+ },
1164
+ {
1165
+ id: "qwen2.5-coder:3b",
1166
+ type: "llama_model",
1167
+ name: "Qwen2.5-Coder - 3B",
1168
+ repo_id: "qwen2.5-coder:3b",
1169
+ description: "Code-focused Qwen variant generates precise structured outputs and respects schema rules.",
1170
+ size_on_disk: 1932735283
1171
+ },
1172
+ {
1173
+ id: "deepseek-r1:7b",
1174
+ type: "llama_model",
1175
+ name: "Deepseek R1 - 7B",
1176
+ repo_id: "deepseek-r1:7b",
1177
+ description: "Reasoning-oriented DeepSeek shines when extraction needs cross-field validation.",
1178
+ size_on_disk: 4617089843
1179
+ },
1180
+ {
1181
+ id: "ggml-org/gemma-3-4b-it-GGUF:gemma-3-4b-it-Q4_K_M.gguf",
1182
+ type: "llama_cpp_model",
1183
+ name: "Gemma 3 4B IT (GGUF)",
1184
+ repo_id: "ggml-org/gemma-3-4b-it-GGUF",
1185
+ path: "gemma-3-4b-it-Q4_K_M.gguf",
1186
+ description: "Efficient Gemma 3 for extraction via llama.cpp.",
1187
+ size_on_disk: 3113851289,
1188
+ pipeline_tag: "image-text-to-text",
1189
+ tags: [
1190
+ "gguf",
1191
+ "image-text-to-text",
1192
+ "arxiv:1905.07830",
1193
+ "arxiv:1905.10044",
1194
+ "arxiv:1911.11641",
1195
+ "arxiv:1904.09728",
1196
+ "arxiv:1705.03551",
1197
+ "arxiv:1911.01547",
1198
+ "arxiv:1907.10641",
1199
+ "arxiv:1903.00161",
1200
+ "arxiv:2009.03300",
1201
+ "arxiv:2304.06364",
1202
+ "arxiv:2103.03874",
1203
+ "arxiv:2110.14168",
1204
+ "arxiv:2311.12022",
1205
+ "arxiv:2108.07732",
1206
+ "arxiv:2107.03374",
1207
+ "arxiv:2210.03057",
1208
+ "arxiv:2106.03193",
1209
+ "arxiv:1910.11856",
1210
+ "arxiv:2502.12404",
1211
+ "arxiv:2502.21228",
1212
+ "arxiv:2404.16816",
1213
+ "arxiv:2104.12756",
1214
+ "arxiv:2311.16502",
1215
+ "arxiv:2203.10244",
1216
+ "arxiv:2404.12390",
1217
+ "arxiv:1810.12440",
1218
+ "arxiv:1908.02660",
1219
+ "arxiv:2312.11805",
1220
+ "base_model:google/gemma-3-4b-it",
1221
+ "base_model:quantized:google/gemma-3-4b-it",
1222
+ "license:gemma",
1223
+ "endpoints_compatible",
1224
+ "region:us",
1225
+ "conversational"
1226
+ ],
1227
+ has_model_index: false,
1228
+ downloads: 25779,
1229
+ likes: 48
1230
+ }
1231
+ ];
1232
+ async process(context) {
1233
+ const text = asText(this.text ?? this.text ?? "");
1234
+ const { providerId, modelId } = getModelConfig(this.serialize());
1235
+ if (hasProviderSupport(context, providerId, modelId)) {
1236
+ const provider = await context.getProvider(providerId);
1237
+ const schema = getStructuredOutputSchema(this) ?? {
1238
+ type: "object",
1239
+ properties: { output: { type: "string" } },
1240
+ required: ["output"],
1241
+ additionalProperties: true
1242
+ };
1243
+ const result = await generateStructured(provider, {
1244
+ model: modelId,
1245
+ maxTokens: Number(this.max_tokens ?? 1024),
1246
+ messages: [
1247
+ { role: "system", content: EXTRACTOR_SYSTEM_PROMPT },
1248
+ { role: "user", content: text }
1249
+ ],
1250
+ toolName: "extraction_result",
1251
+ toolDescription: "Submit the extracted data.",
1252
+ schema
1253
+ });
1254
+ if (result)
1255
+ return result;
1256
+ }
1257
+ const parsed = extractJson(text);
1258
+ if (parsed)
1259
+ return parsed;
1260
+ return { output: text };
1261
+ }
1262
+ }
1263
+ __decorate([
1264
+ prop({
1265
+ type: "str",
1266
+ default: '\nYou are a precise structured data extractor.\n\nGoal\n- Extract exactly the fields described in <JSON_SCHEMA> from the content in <TEXT> (and any attached media).\n\nOutput format (MANDATORY)\n- Output exactly ONE fenced code block labeled json containing ONLY the JSON object:\n\n ```json\n { ...single JSON object matching <JSON_SCHEMA>... }\n ```\n\n- No additional prose before or after the block.\n\nExtraction rules\n- Use only information found in <TEXT> or attached media. Do not invent facts.\n- Preserve source values; normalize internal whitespace and trim leading/trailing spaces.\n- If a required field is missing or not explicitly stated, return the closest reasonable default consistent with its type:\n - string: ""\n - number: 0\n - boolean: false\n - array/object: empty value of that type (only if allowed by the schema)\n- Dates/times: prefer ISO 8601 when the schema type is string and the value represents a date/time.\n- If multiple candidates exist, choose the most precise and unambiguous one.\n\nValidation\n- Ensure the final JSON validates against <JSON_SCHEMA> exactly.\n',
1267
+ title: "System Prompt",
1268
+ description: "The system prompt for the data extractor"
1269
+ })
1270
+ ], ExtractorNode.prototype, "system_prompt", void 0);
1271
+ __decorate([
1272
+ prop({
1273
+ type: "language_model",
1274
+ default: {
1275
+ type: "language_model",
1276
+ provider: "empty",
1277
+ id: "",
1278
+ name: "",
1279
+ path: null,
1280
+ supported_tasks: []
1281
+ },
1282
+ title: "Model",
1283
+ description: "Model to use for data extraction"
1284
+ })
1285
+ ], ExtractorNode.prototype, "model", void 0);
1286
+ __decorate([
1287
+ prop({
1288
+ type: "str",
1289
+ default: "",
1290
+ title: "Text",
1291
+ description: "The text to extract data from"
1292
+ })
1293
+ ], ExtractorNode.prototype, "text", void 0);
1294
+ __decorate([
1295
+ prop({
1296
+ type: "image",
1297
+ default: {
1298
+ type: "image",
1299
+ uri: "",
1300
+ asset_id: null,
1301
+ data: null,
1302
+ metadata: null
1303
+ },
1304
+ title: "Image",
1305
+ description: "Optional image to assist extraction"
1306
+ })
1307
+ ], ExtractorNode.prototype, "image", void 0);
1308
+ __decorate([
1309
+ prop({
1310
+ type: "audio",
1311
+ default: {
1312
+ type: "audio",
1313
+ uri: "",
1314
+ asset_id: null,
1315
+ data: null,
1316
+ metadata: null
1317
+ },
1318
+ title: "Audio",
1319
+ description: "Optional audio to assist extraction"
1320
+ })
1321
+ ], ExtractorNode.prototype, "audio", void 0);
1322
+ export class ClassifierNode extends BaseNode {
1323
+ static nodeType = "nodetool.agents.Classifier";
1324
+ static title = "Classifier";
1325
+ static description = "Classify text into predefined or dynamic categories using LLM.\n classification, nlp, categorization\n\n Use cases:\n - Sentiment analysis\n - Topic classification\n - Intent detection\n - Content categorization";
1326
+ static metadataOutputTypes = {
1327
+ output: "str"
1328
+ };
1329
+ static basicFields = [
1330
+ "text",
1331
+ "categories",
1332
+ "model",
1333
+ "image",
1334
+ "audio"
1335
+ ];
1336
+ static recommendedModels = [
1337
+ {
1338
+ id: "phi3.5:latest",
1339
+ type: "llama_model",
1340
+ name: "Phi3.5",
1341
+ repo_id: "phi3.5:latest",
1342
+ description: "Reliable small model for intent and sentiment classification when VRAM is tight.",
1343
+ size_on_disk: 2362232012
1344
+ },
1345
+ {
1346
+ id: "mistral-small:latest",
1347
+ type: "llama_model",
1348
+ name: "Mistral Small",
1349
+ repo_id: "mistral-small:latest",
1350
+ description: "Fast MoE model that keeps category predictions consistent across batches.",
1351
+ size_on_disk: 7730941132
1352
+ },
1353
+ {
1354
+ id: "granite3.1-moe:1b",
1355
+ type: "llama_model",
1356
+ name: "Granite 3.1 MOE - 1B",
1357
+ repo_id: "granite3.1-moe:1b",
1358
+ description: "IBM Granite 1B excels at classification and routing tasks on CPUs and edge devices.",
1359
+ size_on_disk: 751619276
1360
+ },
1361
+ {
1362
+ id: "qwen3:1.7b",
1363
+ type: "llama_model",
1364
+ name: "Qwen3 - 1.7B",
1365
+ repo_id: "qwen3:1.7b",
1366
+ description: "Compact Qwen variant provides multilingual label understanding with low latency.",
1367
+ size_on_disk: 1073741824
1368
+ },
1369
+ {
1370
+ id: "gemma3:1b",
1371
+ type: "llama_model",
1372
+ name: "Gemma3 - 1B",
1373
+ repo_id: "gemma3:1b",
1374
+ description: "Gemma 3 1B offers deterministic small-footprint classification for mobile scenarios.",
1375
+ size_on_disk: 805306368
1376
+ },
1377
+ {
1378
+ id: "deepseek-r1:1.5b",
1379
+ type: "llama_model",
1380
+ name: "Deepseek R1 - 1.5B",
1381
+ repo_id: "deepseek-r1:1.5b",
1382
+ description: "Reasoning-focused DeepSeek variant is great for multi-step label decisions.",
1383
+ size_on_disk: 912680550
1384
+ },
1385
+ {
1386
+ id: "ggml-org/gemma-3-4b-it-GGUF:gemma-3-4b-it-Q4_K_M.gguf",
1387
+ type: "llama_cpp_model",
1388
+ name: "Gemma 3 4B IT (GGUF)",
1389
+ repo_id: "ggml-org/gemma-3-4b-it-GGUF",
1390
+ path: "gemma-3-4b-it-Q4_K_M.gguf",
1391
+ description: "Efficient Gemma 3 for classification via llama.cpp.",
1392
+ size_on_disk: 3113851289,
1393
+ pipeline_tag: "image-text-to-text",
1394
+ tags: [
1395
+ "gguf",
1396
+ "image-text-to-text",
1397
+ "arxiv:1905.07830",
1398
+ "arxiv:1905.10044",
1399
+ "arxiv:1911.11641",
1400
+ "arxiv:1904.09728",
1401
+ "arxiv:1705.03551",
1402
+ "arxiv:1911.01547",
1403
+ "arxiv:1907.10641",
1404
+ "arxiv:1903.00161",
1405
+ "arxiv:2009.03300",
1406
+ "arxiv:2304.06364",
1407
+ "arxiv:2103.03874",
1408
+ "arxiv:2110.14168",
1409
+ "arxiv:2311.12022",
1410
+ "arxiv:2108.07732",
1411
+ "arxiv:2107.03374",
1412
+ "arxiv:2210.03057",
1413
+ "arxiv:2106.03193",
1414
+ "arxiv:1910.11856",
1415
+ "arxiv:2502.12404",
1416
+ "arxiv:2502.21228",
1417
+ "arxiv:2404.16816",
1418
+ "arxiv:2104.12756",
1419
+ "arxiv:2311.16502",
1420
+ "arxiv:2203.10244",
1421
+ "arxiv:2404.12390",
1422
+ "arxiv:1810.12440",
1423
+ "arxiv:1908.02660",
1424
+ "arxiv:2312.11805",
1425
+ "base_model:google/gemma-3-4b-it",
1426
+ "base_model:quantized:google/gemma-3-4b-it",
1427
+ "license:gemma",
1428
+ "endpoints_compatible",
1429
+ "region:us",
1430
+ "conversational"
1431
+ ],
1432
+ has_model_index: false,
1433
+ downloads: 25779,
1434
+ likes: 48
1435
+ }
1436
+ ];
1437
+ async process(context) {
1438
+ const text = asText(this.text ?? this.text ?? "");
1439
+ const categories = getCategories(this.categories ?? this.categories);
1440
+ if (categories.length < 2) {
1441
+ throw new Error("At least 2 categories are required");
1442
+ }
1443
+ const { providerId, modelId } = getModelConfig(this.serialize());
1444
+ if (hasProviderSupport(context, providerId, modelId)) {
1445
+ const provider = await context.getProvider(providerId);
1446
+ const result = await generateStructured(provider, {
1447
+ model: modelId,
1448
+ maxTokens: Number(this.max_tokens ?? 256),
1449
+ messages: [
1450
+ { role: "system", content: CLASSIFIER_SYSTEM_PROMPT },
1451
+ {
1452
+ role: "user",
1453
+ content: `Allowed categories: ${categories.join(", ")}\n\nText: ${text}`
1454
+ }
1455
+ ],
1456
+ toolName: "classification_result",
1457
+ toolDescription: "Submit the classification result.",
1458
+ schema: {
1459
+ type: "object",
1460
+ properties: {
1461
+ category: { type: "string", enum: categories }
1462
+ },
1463
+ required: ["category"]
1464
+ }
1465
+ });
1466
+ const category = parseCategory(result ? String(result.category ?? "") : "", categories);
1467
+ return { output: category, category };
1468
+ }
1469
+ const tokens = tokenize(text);
1470
+ let best = categories[0];
1471
+ let bestScore = -1;
1472
+ for (const category of categories) {
1473
+ const catTokens = tokenize(category);
1474
+ let score = 0;
1475
+ for (const token of catTokens) {
1476
+ if (tokens.includes(token))
1477
+ score += 1;
1478
+ }
1479
+ if (score > bestScore) {
1480
+ best = category;
1481
+ bestScore = score;
1482
+ }
1483
+ }
1484
+ return { output: best, category: best };
1485
+ }
1486
+ }
1487
+ __decorate([
1488
+ prop({
1489
+ type: "str",
1490
+ default: '\nYou are a precise classifier.\n\nGoal\n- Select exactly one category from the list provided by the user.\n\nOutput format (MANDATORY)\n- Return ONLY a single JSON object with this exact schema and nothing else:\n {"category": "<one-of-the-allowed-categories>"}\n- No prose, no Markdown, no code fences, no explanations, no extra keys.\n\nSelection criteria\n- Choose the single best category that captures the main intent of the text.\n- If multiple categories seem plausible, pick the most probable one; do not return multiple.\n- If none fit perfectly, choose the closest allowed category. If the list includes "Other" or "Unknown", prefer it when appropriate.\n- Be robust to casing, punctuation, emojis, and minor typos. Handle negation correctly (e.g., "not spam" ≠ spam).\n- Never invent categories that are not in the provided list.\n\nBehavior\n- Be deterministic for the same input.\n- Do not ask clarifying questions; make the best choice with what\'s given.\n',
1491
+ title: "System Prompt",
1492
+ description: "The system prompt for the classifier"
1493
+ })
1494
+ ], ClassifierNode.prototype, "system_prompt", void 0);
1495
+ __decorate([
1496
+ prop({
1497
+ type: "language_model",
1498
+ default: {
1499
+ type: "language_model",
1500
+ provider: "empty",
1501
+ id: "",
1502
+ name: "",
1503
+ path: null,
1504
+ supported_tasks: []
1505
+ },
1506
+ title: "Model",
1507
+ description: "Model to use for classification"
1508
+ })
1509
+ ], ClassifierNode.prototype, "model", void 0);
1510
+ __decorate([
1511
+ prop({
1512
+ type: "str",
1513
+ default: "",
1514
+ title: "Text",
1515
+ description: "Text to classify"
1516
+ })
1517
+ ], ClassifierNode.prototype, "text", void 0);
1518
+ __decorate([
1519
+ prop({
1520
+ type: "image",
1521
+ default: {
1522
+ type: "image",
1523
+ uri: "",
1524
+ asset_id: null,
1525
+ data: null,
1526
+ metadata: null
1527
+ },
1528
+ title: "Image",
1529
+ description: "Optional image to classify in context"
1530
+ })
1531
+ ], ClassifierNode.prototype, "image", void 0);
1532
+ __decorate([
1533
+ prop({
1534
+ type: "audio",
1535
+ default: {
1536
+ type: "audio",
1537
+ uri: "",
1538
+ asset_id: null,
1539
+ data: null,
1540
+ metadata: null
1541
+ },
1542
+ title: "Audio",
1543
+ description: "Optional audio to classify in context"
1544
+ })
1545
+ ], ClassifierNode.prototype, "audio", void 0);
1546
+ __decorate([
1547
+ prop({
1548
+ type: "list[str]",
1549
+ default: [],
1550
+ title: "Categories",
1551
+ description: "List of possible categories. If empty, LLM will determine categories."
1552
+ })
1553
+ ], ClassifierNode.prototype, "categories", void 0);
1554
+ export class AgentNode extends BaseNode {
1555
+ static nodeType = "nodetool.agents.Agent";
1556
+ static title = "Agent";
1557
+ static description = "Generate natural language responses using LLM providers and streams output.\n llm, text-generation, chatbot, question-answering, streaming";
1558
+ static metadataOutputTypes = {
1559
+ text: "str",
1560
+ chunk: "chunk",
1561
+ thinking: "chunk",
1562
+ audio: "audio"
1563
+ };
1564
+ static basicFields = ["prompt", "model", "tools", "image", "audio"];
1565
+ static supportsDynamicOutputs = true;
1566
+ static recommendedModels = [
1567
+ {
1568
+ id: "gpt-oss:20b",
1569
+ type: "llama_model",
1570
+ name: "GPT - OSS",
1571
+ repo_id: "gpt-oss:20b",
1572
+ description: "OpenAI's open-weight model excels at multi-tool routing and reasoning.",
1573
+ size_on_disk: 15032385536
1574
+ },
1575
+ {
1576
+ id: "qwen3-vl:4b",
1577
+ type: "llama_model",
1578
+ name: "Qwen3 VL - 4B",
1579
+ repo_id: "qwen3-vl:4b",
1580
+ description: "The most powerful vision-language model in the Qwen model family to date.",
1581
+ size_on_disk: 3543348019
1582
+ },
1583
+ {
1584
+ id: "qwen3-vl:8b",
1585
+ type: "llama_model",
1586
+ name: "Qwen3 VL - 8B",
1587
+ repo_id: "qwen3-vl:8b",
1588
+ description: "The most powerful vision-language model in the Qwen model family to date.",
1589
+ size_on_disk: 6549825126
1590
+ },
1591
+ {
1592
+ id: "gemma3:1b",
1593
+ type: "llama_model",
1594
+ name: "Gemma3 - 1B",
1595
+ repo_id: "gemma3:1b",
1596
+ description: "Gemma3 1B is a small model that can process text and images.",
1597
+ size_on_disk: 875099586
1598
+ },
1599
+ {
1600
+ id: "gemma3:4b",
1601
+ type: "llama_model",
1602
+ name: "Gemma3 - 4B",
1603
+ repo_id: "gemma3:4b",
1604
+ description: "Gemma3 4B is a small model that can process text and images.",
1605
+ size_on_disk: 3543348019
1606
+ },
1607
+ {
1608
+ id: "llama3.2:3b",
1609
+ type: "llama_model",
1610
+ name: "Llama 3.2 - 3B",
1611
+ repo_id: "llama3.2:3b",
1612
+ description: "Compact Llama 3.2 variant keeps latency low while following tool schemas accurately.",
1613
+ size_on_disk: 2040109465
1614
+ },
1615
+ {
1616
+ id: "qwen3:4b",
1617
+ type: "llama_model",
1618
+ name: "Qwen3 - 4B",
1619
+ repo_id: "qwen3:4b",
1620
+ description: "Qwen3 4B ships strong function-calling primitives and dependable multi-turn tool use.",
1621
+ size_on_disk: 2684354560
1622
+ },
1623
+ {
1624
+ id: "qwen3:8b",
1625
+ type: "llama_model",
1626
+ name: "Qwen3 - 8B",
1627
+ repo_id: "qwen3:8b",
1628
+ description: "Qwen3 8B ships strong function-calling primitives and dependable multi-turn tool use.",
1629
+ size_on_disk: 5583457484
1630
+ },
1631
+ {
1632
+ id: "deepseek-r1:8b",
1633
+ type: "mistral_model",
1634
+ name: "Deepseek R1 - 8B",
1635
+ repo_id: "deepseek-r1:8",
1636
+ description: "DeepSeek R1 8B balances reasoning with precise function calls for iterative agents.",
1637
+ size_on_disk: 5583457484
1638
+ },
1639
+ {
1640
+ id: "ggml-org/gpt-oss-20b-GGUF:gpt-oss-20b-mxfp4.gguf",
1641
+ type: "llama_cpp_model",
1642
+ name: "GPT-OSS 20B (GGUF)",
1643
+ repo_id: "ggml-org/gpt-oss-20b-GGUF",
1644
+ path: "gpt-oss-20b-mxfp4.gguf",
1645
+ description: "OpenAI's open-weight model in efficient MXFP4 format for llama.cpp.",
1646
+ size_on_disk: 9191230013,
1647
+ tags: [
1648
+ "gguf",
1649
+ "base_model:openai/gpt-oss-20b",
1650
+ "base_model:quantized:openai/gpt-oss-20b",
1651
+ "endpoints_compatible",
1652
+ "region:us",
1653
+ "conversational"
1654
+ ],
1655
+ has_model_index: false,
1656
+ downloads: 156909,
1657
+ likes: 135
1658
+ },
1659
+ {
1660
+ id: "ggml-org/gemma-3-4b-it-GGUF:gemma-3-4b-it-Q4_K_M.gguf",
1661
+ type: "llama_cpp_model",
1662
+ name: "Gemma 3 4B IT (GGUF)",
1663
+ repo_id: "ggml-org/gemma-3-4b-it-GGUF",
1664
+ path: "gemma-3-4b-it-Q4_K_M.gguf",
1665
+ description: "Google's Gemma 3 4B in Q4_K_M quantization for efficient inference.",
1666
+ size_on_disk: 3113851289,
1667
+ pipeline_tag: "image-text-to-text",
1668
+ tags: [
1669
+ "gguf",
1670
+ "image-text-to-text",
1671
+ "arxiv:1905.07830",
1672
+ "arxiv:1905.10044",
1673
+ "arxiv:1911.11641",
1674
+ "arxiv:1904.09728",
1675
+ "arxiv:1705.03551",
1676
+ "arxiv:1911.01547",
1677
+ "arxiv:1907.10641",
1678
+ "arxiv:1903.00161",
1679
+ "arxiv:2009.03300",
1680
+ "arxiv:2304.06364",
1681
+ "arxiv:2103.03874",
1682
+ "arxiv:2110.14168",
1683
+ "arxiv:2311.12022",
1684
+ "arxiv:2108.07732",
1685
+ "arxiv:2107.03374",
1686
+ "arxiv:2210.03057",
1687
+ "arxiv:2106.03193",
1688
+ "arxiv:1910.11856",
1689
+ "arxiv:2502.12404",
1690
+ "arxiv:2502.21228",
1691
+ "arxiv:2404.16816",
1692
+ "arxiv:2104.12756",
1693
+ "arxiv:2311.16502",
1694
+ "arxiv:2203.10244",
1695
+ "arxiv:2404.12390",
1696
+ "arxiv:1810.12440",
1697
+ "arxiv:1908.02660",
1698
+ "arxiv:2312.11805",
1699
+ "base_model:google/gemma-3-4b-it",
1700
+ "base_model:quantized:google/gemma-3-4b-it",
1701
+ "license:gemma",
1702
+ "endpoints_compatible",
1703
+ "region:us",
1704
+ "conversational"
1705
+ ],
1706
+ has_model_index: false,
1707
+ downloads: 25779,
1708
+ likes: 48
1709
+ },
1710
+ {
1711
+ id: "ggml-org/gemma-3-12b-it-GGUF:gemma-3-12b-it-Q4_K_M.gguf",
1712
+ type: "llama_cpp_model",
1713
+ name: "Gemma 3 12B IT (GGUF)",
1714
+ repo_id: "ggml-org/gemma-3-12b-it-GGUF",
1715
+ path: "gemma-3-12b-it-Q4_K_M.gguf",
1716
+ description: "Google's Gemma 3 12B in Q4_K_M quantization with strong reasoning.",
1717
+ size_on_disk: 7838315315,
1718
+ pipeline_tag: "image-text-to-text",
1719
+ tags: [
1720
+ "gguf",
1721
+ "image-text-to-text",
1722
+ "arxiv:1905.07830",
1723
+ "arxiv:1905.10044",
1724
+ "arxiv:1911.11641",
1725
+ "arxiv:1904.09728",
1726
+ "arxiv:1705.03551",
1727
+ "arxiv:1911.01547",
1728
+ "arxiv:1907.10641",
1729
+ "arxiv:1903.00161",
1730
+ "arxiv:2009.03300",
1731
+ "arxiv:2304.06364",
1732
+ "arxiv:2103.03874",
1733
+ "arxiv:2110.14168",
1734
+ "arxiv:2311.12022",
1735
+ "arxiv:2108.07732",
1736
+ "arxiv:2107.03374",
1737
+ "arxiv:2210.03057",
1738
+ "arxiv:2106.03193",
1739
+ "arxiv:1910.11856",
1740
+ "arxiv:2502.12404",
1741
+ "arxiv:2502.21228",
1742
+ "arxiv:2404.16816",
1743
+ "arxiv:2104.12756",
1744
+ "arxiv:2311.16502",
1745
+ "arxiv:2203.10244",
1746
+ "arxiv:2404.12390",
1747
+ "arxiv:1810.12440",
1748
+ "arxiv:1908.02660",
1749
+ "arxiv:2312.11805",
1750
+ "base_model:google/gemma-3-12b-it",
1751
+ "base_model:quantized:google/gemma-3-12b-it",
1752
+ "license:gemma",
1753
+ "endpoints_compatible",
1754
+ "region:us",
1755
+ "conversational"
1756
+ ],
1757
+ has_model_index: false,
1758
+ downloads: 214667,
1759
+ likes: 30
1760
+ },
1761
+ {
1762
+ id: "ggml-org/Kimi-VL-A3B-Thinking-2506-GGUF:Kimi-VL-A3B-Thinking-2506-Q4_K_M.gguf",
1763
+ type: "llama_cpp_model",
1764
+ name: "Kimi VL A3B Thinking (GGUF)",
1765
+ repo_id: "ggml-org/Kimi-VL-A3B-Thinking-2506-GGUF",
1766
+ path: "Kimi-VL-A3B-Thinking-2506-Q4_K_M.gguf",
1767
+ description: "Moonshot AI's vision-language model with enhanced reasoning capabilities.",
1768
+ size_on_disk: 2362232012,
1769
+ tags: [
1770
+ "gguf",
1771
+ "base_model:moonshotai/Kimi-VL-A3B-Thinking-2506",
1772
+ "base_model:quantized:moonshotai/Kimi-VL-A3B-Thinking-2506",
1773
+ "endpoints_compatible",
1774
+ "region:us",
1775
+ "conversational"
1776
+ ],
1777
+ has_model_index: false,
1778
+ downloads: 3039,
1779
+ likes: 29
1780
+ },
1781
+ {
1782
+ id: "ggml-org/Qwen3-Coder-30B-A3B-Instruct-Q8_0-GGUF:qwen3-coder-30b-a3b-instruct-q8_0.gguf",
1783
+ type: "llama_cpp_model",
1784
+ name: "Qwen3 Coder 30B A3B (GGUF)",
1785
+ repo_id: "ggml-org/Qwen3-Coder-30B-A3B-Instruct-Q8_0-GGUF",
1786
+ path: "qwen3-coder-30b-a3b-instruct-q8_0.gguf",
1787
+ description: "MoE coding model with 3B active params, excellent for code generation.",
1788
+ size_on_disk: 3865470566,
1789
+ pipeline_tag: "text-generation",
1790
+ tags: [
1791
+ "transformers",
1792
+ "gguf",
1793
+ "llama-cpp",
1794
+ "gguf-my-repo",
1795
+ "text-generation",
1796
+ "base_model:Qwen/Qwen3-Coder-30B-A3B-Instruct",
1797
+ "base_model:quantized:Qwen/Qwen3-Coder-30B-A3B-Instruct",
1798
+ "license:apache-2.0",
1799
+ "endpoints_compatible",
1800
+ "region:us",
1801
+ "conversational"
1802
+ ],
1803
+ has_model_index: false,
1804
+ downloads: 80721,
1805
+ likes: 7
1806
+ },
1807
+ {
1808
+ id: "ggml-org/Qwen3-0.6B-GGUF:Qwen3-0.6B-Q4_0.gguf",
1809
+ type: "llama_cpp_model",
1810
+ name: "Qwen3 0.6B (GGUF)",
1811
+ repo_id: "ggml-org/Qwen3-0.6B-GGUF",
1812
+ path: "Qwen3-0.6B-Q4_0.gguf",
1813
+ description: "Ultra-lightweight Qwen3 for edge devices and fast inference.",
1814
+ size_on_disk: 429496729,
1815
+ tags: [
1816
+ "gguf",
1817
+ "base_model:Qwen/Qwen3-0.6B",
1818
+ "base_model:quantized:Qwen/Qwen3-0.6B",
1819
+ "license:apache-2.0",
1820
+ "endpoints_compatible",
1821
+ "region:us",
1822
+ "conversational"
1823
+ ],
1824
+ has_model_index: false,
1825
+ downloads: 44304,
1826
+ likes: 13
1827
+ },
1828
+ {
1829
+ id: "ggml-org/gemma-3-270m-GGUF:gemma-3-270m-Q8_0.gguf",
1830
+ type: "llama_cpp_model",
1831
+ name: "Gemma 3 270M (GGUF)",
1832
+ repo_id: "ggml-org/gemma-3-270m-GGUF",
1833
+ path: "gemma-3-270m-Q8_0.gguf",
1834
+ description: "Tiny Gemma 3 for ultra-fast inference on CPU.",
1835
+ size_on_disk: 375809638,
1836
+ tags: [
1837
+ "gguf",
1838
+ "base_model:google/gemma-3-270m",
1839
+ "base_model:quantized:google/gemma-3-270m",
1840
+ "endpoints_compatible",
1841
+ "region:us"
1842
+ ],
1843
+ has_model_index: false,
1844
+ downloads: 595,
1845
+ likes: 19
1846
+ },
1847
+ {
1848
+ id: "ggml-org/gemma-3-27b-it-GGUF:gemma-3-27b-it-Q4_K_M.gguf",
1849
+ type: "llama_cpp_model",
1850
+ name: "Gemma 3 27B IT (GGUF)",
1851
+ repo_id: "ggml-org/gemma-3-27b-it-GGUF",
1852
+ path: "gemma-3-27b-it-Q4_K_M.gguf",
1853
+ description: "Google's largest Gemma 3 with strong reasoning and tool use.",
1854
+ size_on_disk: 16965120819,
1855
+ pipeline_tag: "image-text-to-text",
1856
+ tags: [
1857
+ "gguf",
1858
+ "image-text-to-text",
1859
+ "arxiv:1905.07830",
1860
+ "arxiv:1905.10044",
1861
+ "arxiv:1911.11641",
1862
+ "arxiv:1904.09728",
1863
+ "arxiv:1705.03551",
1864
+ "arxiv:1911.01547",
1865
+ "arxiv:1907.10641",
1866
+ "arxiv:1903.00161",
1867
+ "arxiv:2009.03300",
1868
+ "arxiv:2304.06364",
1869
+ "arxiv:2103.03874",
1870
+ "arxiv:2110.14168",
1871
+ "arxiv:2311.12022",
1872
+ "arxiv:2108.07732",
1873
+ "arxiv:2107.03374",
1874
+ "arxiv:2210.03057",
1875
+ "arxiv:2106.03193",
1876
+ "arxiv:1910.11856",
1877
+ "arxiv:2502.12404",
1878
+ "arxiv:2502.21228",
1879
+ "arxiv:2404.16816",
1880
+ "arxiv:2104.12756",
1881
+ "arxiv:2311.16502",
1882
+ "arxiv:2203.10244",
1883
+ "arxiv:2404.12390",
1884
+ "arxiv:1810.12440",
1885
+ "arxiv:1908.02660",
1886
+ "arxiv:2312.11805",
1887
+ "base_model:google/gemma-3-27b-it",
1888
+ "base_model:quantized:google/gemma-3-27b-it",
1889
+ "license:gemma",
1890
+ "endpoints_compatible",
1891
+ "region:us",
1892
+ "conversational"
1893
+ ],
1894
+ has_model_index: false,
1895
+ downloads: 2604,
1896
+ likes: 23
1897
+ },
1898
+ {
1899
+ id: "Qwen/Qwen3-30B-A3B-GGUF:Qwen3-30B-A3B-Q4_K_M.gguf",
1900
+ type: "llama_cpp_model",
1901
+ name: "Qwen3 30B A3B (GGUF)",
1902
+ repo_id: "Qwen/Qwen3-30B-A3B-GGUF",
1903
+ path: "Qwen3-30B-A3B-Q4_K_M.gguf",
1904
+ description: "Qwen3 30B MoE model (3B active) in Q4_K_M quantization.",
1905
+ size_on_disk: 19327352832,
1906
+ pipeline_tag: "text-generation",
1907
+ tags: [
1908
+ "gguf",
1909
+ "text-generation",
1910
+ "arxiv:2309.00071",
1911
+ "arxiv:2505.09388",
1912
+ "base_model:Qwen/Qwen3-30B-A3B",
1913
+ "base_model:quantized:Qwen/Qwen3-30B-A3B",
1914
+ "license:apache-2.0",
1915
+ "endpoints_compatible",
1916
+ "region:us",
1917
+ "conversational"
1918
+ ],
1919
+ has_model_index: false,
1920
+ downloads: 17644,
1921
+ likes: 65
1922
+ },
1923
+ {
1924
+ id: "Qwen/Qwen3-32B-GGUF:Qwen3-32B-Q4_K_M.gguf",
1925
+ type: "llama_cpp_model",
1926
+ name: "Qwen3 32B (GGUF)",
1927
+ repo_id: "Qwen/Qwen3-32B-GGUF",
1928
+ path: "Qwen3-32B-Q4_K_M.gguf",
1929
+ description: "Qwen3 32B dense model in Q4_K_M quantization.",
1930
+ size_on_disk: 20401094656,
1931
+ pipeline_tag: "text-generation",
1932
+ tags: [
1933
+ "gguf",
1934
+ "text-generation",
1935
+ "arxiv:2309.00071",
1936
+ "base_model:Qwen/Qwen3-32B",
1937
+ "base_model:quantized:Qwen/Qwen3-32B",
1938
+ "license:apache-2.0",
1939
+ "endpoints_compatible",
1940
+ "region:us",
1941
+ "conversational"
1942
+ ],
1943
+ has_model_index: false,
1944
+ downloads: 27381,
1945
+ likes: 64
1946
+ },
1947
+ {
1948
+ id: "Qwen/Qwen3-14B-GGUF:Qwen3-14B-Q4_K_M.gguf",
1949
+ type: "llama_cpp_model",
1950
+ name: "Qwen3 14B (GGUF)",
1951
+ repo_id: "Qwen/Qwen3-14B-GGUF",
1952
+ path: "Qwen3-14B-Q4_K_M.gguf",
1953
+ description: "Qwen3 14B dense model in Q4_K_M quantization.",
1954
+ size_on_disk: 9663676416,
1955
+ pipeline_tag: "text-generation",
1956
+ tags: [
1957
+ "gguf",
1958
+ "text-generation",
1959
+ "arxiv:2309.00071",
1960
+ "base_model:Qwen/Qwen3-14B",
1961
+ "base_model:quantized:Qwen/Qwen3-14B",
1962
+ "license:apache-2.0",
1963
+ "endpoints_compatible",
1964
+ "region:us",
1965
+ "conversational"
1966
+ ],
1967
+ has_model_index: false,
1968
+ downloads: 61212,
1969
+ likes: 73
1970
+ },
1971
+ {
1972
+ id: "Qwen/Qwen3-8B-GGUF:Qwen3-8B-Q4_K_M.gguf",
1973
+ type: "llama_cpp_model",
1974
+ name: "Qwen3 8B (GGUF)",
1975
+ repo_id: "Qwen/Qwen3-8B-GGUF",
1976
+ path: "Qwen3-8B-Q4_K_M.gguf",
1977
+ description: "Qwen3 8B dense model in Q4_K_M quantization.",
1978
+ size_on_disk: 5368709120,
1979
+ pipeline_tag: "text-generation",
1980
+ tags: [
1981
+ "gguf",
1982
+ "text-generation",
1983
+ "arxiv:2309.00071",
1984
+ "arxiv:2505.09388",
1985
+ "base_model:Qwen/Qwen3-8B",
1986
+ "base_model:quantized:Qwen/Qwen3-8B",
1987
+ "license:apache-2.0",
1988
+ "endpoints_compatible",
1989
+ "region:us",
1990
+ "conversational"
1991
+ ],
1992
+ has_model_index: false,
1993
+ downloads: 94189,
1994
+ likes: 156
1995
+ },
1996
+ {
1997
+ id: "Qwen/Qwen3-4B-GGUF:Qwen3-4B-Q4_K_M.gguf",
1998
+ type: "llama_cpp_model",
1999
+ name: "Qwen3 4B (GGUF)",
2000
+ repo_id: "Qwen/Qwen3-4B-GGUF",
2001
+ path: "Qwen3-4B-Q4_K_M.gguf",
2002
+ description: "Qwen3 4B dense model in Q4_K_M quantization.",
2003
+ size_on_disk: 2684354560,
2004
+ pipeline_tag: "text-generation",
2005
+ tags: [
2006
+ "gguf",
2007
+ "text-generation",
2008
+ "arxiv:2309.00071",
2009
+ "arxiv:2505.09388",
2010
+ "base_model:Qwen/Qwen3-4B",
2011
+ "base_model:quantized:Qwen/Qwen3-4B",
2012
+ "license:apache-2.0",
2013
+ "endpoints_compatible",
2014
+ "region:us",
2015
+ "conversational"
2016
+ ],
2017
+ has_model_index: false,
2018
+ downloads: 48252,
2019
+ likes: 84
2020
+ }
2021
+ ];
2022
+ static isStreamingOutput = true;
2023
+ async *genProcess(context) {
2024
+ const { providerId, modelId } = getModelConfig(this.serialize());
2025
+ log.info("AgentNode starting", {
2026
+ nodeId: this.__node_id ?? null,
2027
+ providerId,
2028
+ modelId,
2029
+ hasContext: Boolean(context),
2030
+ hasGetProvider: Boolean(context && typeof context.getProvider === "function"),
2031
+ propKeys: Object.keys(this.serialize()),
2032
+ inputKeys: Object.keys(this.serialize())
2033
+ });
2034
+ if (!providerId || !modelId) {
2035
+ log.error("AgentNode missing model selection", {
2036
+ nodeId: this.__node_id ?? null,
2037
+ providerId,
2038
+ modelId,
2039
+ modelInput: this.model ?? null,
2040
+ modelProp: this.model ?? null
2041
+ });
2042
+ throw new Error("Select a model");
2043
+ }
2044
+ if (!context || typeof context.getProvider !== "function") {
2045
+ log.error("AgentNode missing processing context or provider access", {
2046
+ nodeId: this.__node_id ?? null,
2047
+ providerId,
2048
+ modelId
2049
+ });
2050
+ throw new Error("Processing context is required");
2051
+ }
2052
+ // --- Multi-mode dispatch for "plan" and "multi-agent" modes ---
2053
+ const agentMode = String(this.mode ?? "loop").trim();
2054
+ if (agentMode === "plan" || agentMode === "multi-agent") {
2055
+ yield* this.genProcessMultiMode(context, providerId, modelId, agentMode);
2056
+ return;
2057
+ }
2058
+ const prompt = asText(this.prompt ?? this.prompt ?? "");
2059
+ const system = asText(this.system ?? this.system ?? DEFAULT_SYSTEM_PROMPT);
2060
+ const image = this.image ?? this.image;
2061
+ const audio = this.audio ?? this.audio;
2062
+ const historyInput = this.history ?? this.history;
2063
+ const history = Array.isArray(historyInput)
2064
+ ? historyInput
2065
+ .map((item) => normalizeMessage(item))
2066
+ .filter((item) => item !== null)
2067
+ : [];
2068
+ const threadId = String(this.thread_id ?? this.thread_id ?? "").trim();
2069
+ const maxTokens = Number(this.max_tokens ?? this.max_tokens ?? 8192);
2070
+ const tools = normalizeTools(this.tools ?? this.tools);
2071
+ // Build control tools from _control_context (injected by the kernel
2072
+ // for nodes that have outgoing control edges). This lets the LLM
2073
+ // call controlled nodes as tools.
2074
+ const controlContext = this._control_context;
2075
+ const controlTools = buildControlTools(controlContext);
2076
+ if (controlTools.length > 0) {
2077
+ tools.push(...controlTools);
2078
+ log.info("AgentNode added control tools", {
2079
+ nodeId: this.__node_id ?? null,
2080
+ controlToolNames: controlTools.map((t) => t.name),
2081
+ controlTargets: controlTools.map((t) => t.targetNodeId)
2082
+ });
2083
+ }
2084
+ const structuredSchema = getStructuredOutputSchema(this);
2085
+ const messages = [
2086
+ { role: "system", content: system },
2087
+ ...(await loadThreadMessages(context, threadId)),
2088
+ ...history,
2089
+ buildUserMessage(prompt, image, audio)
2090
+ ];
2091
+ log.info("AgentNode prepared messages", {
2092
+ nodeId: this.__node_id ?? null,
2093
+ providerId,
2094
+ modelId,
2095
+ threadId: threadId || null,
2096
+ promptLength: prompt.length,
2097
+ historyCount: history.length,
2098
+ toolCount: tools.length,
2099
+ messageCount: messages.length,
2100
+ hasImage: hasContentType(messages[messages.length - 1], "image_url"),
2101
+ hasAudio: hasContentType(messages[messages.length - 1], "audio")
2102
+ });
2103
+ if (threadId) {
2104
+ await saveThreadMessage(context, threadId, messages[messages.length - 1]);
2105
+ }
2106
+ let lastTextOutput = null;
2107
+ /** Latest extracted `<think>` body (for diagnostics / truncation errors). */
2108
+ let lastExtractedThinking = null;
2109
+ const providerTools = tools.length > 0 ? toProviderTools(tools) : undefined;
2110
+ const provider = await context.getProvider(providerId);
2111
+ // --- Agentic provider fast-path (e.g. Claude Agent SDK with MCP) ---
2112
+ // The provider handles the full tool-calling loop internally.
2113
+ // We provide an onToolCall callback so tools execute natively via MCP.
2114
+ if (isAgenticProvider(provider) && tools.length > 0) {
2115
+ log.info("AgentNode using agentic provider path", {
2116
+ nodeId: this.__node_id ?? null,
2117
+ providerId,
2118
+ modelId,
2119
+ toolCount: tools.length
2120
+ });
2121
+ // Build onToolCall callback that bridges into our tool set
2122
+ const onToolCall = async (name, args) => {
2123
+ const tool = tools.find((t) => t.name === name);
2124
+ if (!tool || typeof tool.process !== "function") {
2125
+ return JSON.stringify({ error: `Unknown tool: ${name}` });
2126
+ }
2127
+ if (isControlTool(tool)) {
2128
+ const callArgs = args ?? {};
2129
+ if (context.hasControlEventSupport) {
2130
+ try {
2131
+ const controlResult = await context.sendControlEvent(tool.targetNodeId, callArgs);
2132
+ return JSON.stringify({
2133
+ status: "completed",
2134
+ target_node_id: tool.targetNodeId,
2135
+ result: controlResult
2136
+ });
2137
+ }
2138
+ catch (err) {
2139
+ return JSON.stringify({
2140
+ status: "error",
2141
+ target_node_id: tool.targetNodeId,
2142
+ error: err instanceof Error ? err.message : String(err)
2143
+ });
2144
+ }
2145
+ }
2146
+ return JSON.stringify({
2147
+ status: "error",
2148
+ error: "Control event dispatch unavailable"
2149
+ });
2150
+ }
2151
+ try {
2152
+ const result = await tool.process(context, args);
2153
+ return typeof result === "string"
2154
+ ? result
2155
+ : JSON.stringify(serializeToolResult(result));
2156
+ }
2157
+ catch (e) {
2158
+ return JSON.stringify({ error: String(e) });
2159
+ }
2160
+ };
2161
+ let assistantText = "";
2162
+ const assistantToolCalls = [];
2163
+ const thinkSplitter = new RedactedThinkingStreamSplitter();
2164
+ let streamedRedactedThinking = false;
2165
+ for await (const item of streamProviderMessages(provider, {
2166
+ messages,
2167
+ model: modelId,
2168
+ tools: providerTools,
2169
+ maxTokens,
2170
+ threadId: threadId || undefined,
2171
+ onToolCall
2172
+ })) {
2173
+ if (isChunkItem(item)) {
2174
+ if (item.thinking) {
2175
+ yield { chunk: null, thinking: item, text: null, audio: null };
2176
+ }
2177
+ else if (item.content_type === "audio") {
2178
+ yield { chunk: item, thinking: null, text: null, audio: null };
2179
+ const audioBytes = item.content
2180
+ ? Buffer.from(item.content, "base64")
2181
+ : Buffer.alloc(0);
2182
+ yield {
2183
+ chunk: null,
2184
+ thinking: null,
2185
+ text: null,
2186
+ audio: { data: new Uint8Array(audioBytes) }
2187
+ };
2188
+ }
2189
+ else {
2190
+ const rawPiece = item.content ?? "";
2191
+ assistantText += rawPiece;
2192
+ for (const y of yieldSplitThinkChunks(item, rawPiece, thinkSplitter)) {
2193
+ if (y.thinking != null)
2194
+ streamedRedactedThinking = true;
2195
+ yield y;
2196
+ }
2197
+ }
2198
+ }
2199
+ if (isToolCallItem(item)) {
2200
+ assistantToolCalls.push(item);
2201
+ log.info("AgentNode MCP tool executed", {
2202
+ nodeId: this.__node_id ?? null,
2203
+ toolName: item.name
2204
+ });
2205
+ }
2206
+ }
2207
+ for (const part of thinkSplitter.flush()) {
2208
+ if (part.kind === "thinking" && part.content.length > 0) {
2209
+ streamedRedactedThinking = true;
2210
+ yield {
2211
+ chunk: null,
2212
+ thinking: { type: "chunk", content: part.content, thinking: true },
2213
+ text: null,
2214
+ audio: null
2215
+ };
2216
+ }
2217
+ else if (part.kind === "text" && part.content.length > 0) {
2218
+ yield {
2219
+ chunk: {
2220
+ type: "chunk",
2221
+ content: part.content,
2222
+ content_type: "text",
2223
+ thinking: false,
2224
+ done: false
2225
+ },
2226
+ thinking: null,
2227
+ text: null,
2228
+ audio: null
2229
+ };
2230
+ }
2231
+ }
2232
+ if (assistantText) {
2233
+ const { thinking: thinkingText, text: cleanText } = extractThinkTags(assistantText);
2234
+ const trimmed = cleanText.trim();
2235
+ if (trimmed) {
2236
+ lastTextOutput = trimmed;
2237
+ }
2238
+ if (thinkingText.trim()) {
2239
+ lastExtractedThinking = thinkingText;
2240
+ }
2241
+ if (thinkingText && !streamedRedactedThinking) {
2242
+ yield {
2243
+ chunk: null,
2244
+ thinking: { type: "chunk", content: thinkingText, thinking: true },
2245
+ text: null,
2246
+ audio: null
2247
+ };
2248
+ }
2249
+ yield { chunk: null, thinking: null, text: cleanText, audio: null };
2250
+ }
2251
+ // Save messages to thread
2252
+ if (assistantText || assistantToolCalls.length > 0) {
2253
+ const assistantMessage = {
2254
+ role: "assistant",
2255
+ content: [{ type: "text", text: assistantText }],
2256
+ toolCalls: assistantToolCalls.length > 0 ? assistantToolCalls : null
2257
+ };
2258
+ messages.push(assistantMessage);
2259
+ await saveThreadMessage(context, threadId, assistantMessage);
2260
+ }
2261
+ log.info("AgentNode agentic path completed", {
2262
+ nodeId: this.__node_id ?? null,
2263
+ textLength: assistantText.length,
2264
+ toolCallCount: assistantToolCalls.length
2265
+ });
2266
+ }
2267
+ else {
2268
+ // --- Standard multi-iteration loop (for non-agentic providers) ---
2269
+ let shouldContinue = false;
2270
+ let firstIteration = true;
2271
+ while (firstIteration || shouldContinue) {
2272
+ firstIteration = false;
2273
+ shouldContinue = false;
2274
+ log.info("AgentNode provider iteration starting", {
2275
+ nodeId: this.__node_id ?? null,
2276
+ providerId,
2277
+ modelId,
2278
+ threadId: threadId || null,
2279
+ messageCount: messages.length
2280
+ });
2281
+ const assistantToolCalls = [];
2282
+ let assistantText = "";
2283
+ let chunkCount = 0;
2284
+ let thinkingCount = 0;
2285
+ let audioChunkCount = 0;
2286
+ const thinkSplitter = new RedactedThinkingStreamSplitter();
2287
+ let streamedRedactedThinking = false;
2288
+ for await (const item of streamProviderMessages(provider, {
2289
+ messages,
2290
+ model: modelId,
2291
+ tools: providerTools,
2292
+ maxTokens,
2293
+ threadId: threadId || undefined
2294
+ })) {
2295
+ if (isChunkItem(item)) {
2296
+ chunkCount += 1;
2297
+ if (item.thinking) {
2298
+ thinkingCount += 1;
2299
+ yield { chunk: null, thinking: item, text: null, audio: null };
2300
+ continue;
2301
+ }
2302
+ if (item.content_type === "audio") {
2303
+ audioChunkCount += 1;
2304
+ yield { chunk: item, thinking: null, text: null, audio: null };
2305
+ const audioBytes = item.content
2306
+ ? Buffer.from(item.content, "base64")
2307
+ : Buffer.alloc(0);
2308
+ yield {
2309
+ chunk: null,
2310
+ thinking: null,
2311
+ text: null,
2312
+ audio: { data: new Uint8Array(audioBytes) }
2313
+ };
2314
+ }
2315
+ else {
2316
+ const rawPiece = item.content ?? "";
2317
+ assistantText += rawPiece;
2318
+ for (const y of yieldSplitThinkChunks(item, rawPiece, thinkSplitter)) {
2319
+ if (y.thinking != null)
2320
+ streamedRedactedThinking = true;
2321
+ yield y;
2322
+ }
2323
+ }
2324
+ continue;
2325
+ }
2326
+ if (isToolCallItem(item)) {
2327
+ assistantToolCalls.push(item);
2328
+ log.info("AgentNode received tool call", {
2329
+ nodeId: this.__node_id ?? null,
2330
+ providerId,
2331
+ modelId,
2332
+ toolCallId: item.id,
2333
+ toolName: item.name,
2334
+ argKeys: Object.keys(item.args ?? {})
2335
+ });
2336
+ }
2337
+ }
2338
+ log.info("AgentNode provider iteration completed", {
2339
+ nodeId: this.__node_id ?? null,
2340
+ providerId,
2341
+ modelId,
2342
+ chunkCount,
2343
+ thinkingCount,
2344
+ audioChunkCount,
2345
+ toolCallCount: assistantToolCalls.length,
2346
+ assistantTextLength: assistantText.length
2347
+ });
2348
+ for (const part of thinkSplitter.flush()) {
2349
+ if (part.kind === "thinking" && part.content.length > 0) {
2350
+ streamedRedactedThinking = true;
2351
+ yield {
2352
+ chunk: null,
2353
+ thinking: { type: "chunk", content: part.content, thinking: true },
2354
+ text: null,
2355
+ audio: null
2356
+ };
2357
+ }
2358
+ else if (part.kind === "text" && part.content.length > 0) {
2359
+ yield {
2360
+ chunk: {
2361
+ type: "chunk",
2362
+ content: part.content,
2363
+ content_type: "text",
2364
+ done: false
2365
+ },
2366
+ thinking: null,
2367
+ text: null,
2368
+ audio: null
2369
+ };
2370
+ }
2371
+ }
2372
+ if (assistantText) {
2373
+ const { thinking: thinkingText, text: cleanText } = extractThinkTags(assistantText);
2374
+ const trimmed = cleanText.trim();
2375
+ if (trimmed) {
2376
+ lastTextOutput = trimmed;
2377
+ }
2378
+ if (thinkingText.trim()) {
2379
+ lastExtractedThinking = thinkingText;
2380
+ }
2381
+ if (thinkingText && !streamedRedactedThinking) {
2382
+ yield {
2383
+ chunk: null,
2384
+ thinking: { type: "chunk", content: thinkingText, thinking: true },
2385
+ text: null,
2386
+ audio: null
2387
+ };
2388
+ }
2389
+ yield {
2390
+ chunk: null,
2391
+ thinking: null,
2392
+ text: cleanText,
2393
+ audio: null
2394
+ };
2395
+ }
2396
+ if (assistantText || assistantToolCalls.length > 0) {
2397
+ const assistantMessage = {
2398
+ role: "assistant",
2399
+ content: [{ type: "text", text: assistantText }],
2400
+ toolCalls: assistantToolCalls.length > 0 ? assistantToolCalls : null
2401
+ };
2402
+ messages.push(assistantMessage);
2403
+ await saveThreadMessage(context, threadId, assistantMessage);
2404
+ }
2405
+ for (const toolCall of assistantToolCalls) {
2406
+ const tool = tools.find((candidate) => candidate.name === toolCall.name);
2407
+ if (!tool || typeof tool.process !== "function") {
2408
+ log.warn("AgentNode tool call had no matching executable tool", {
2409
+ nodeId: this.__node_id ?? null,
2410
+ toolCallId: toolCall.id,
2411
+ toolName: toolCall.name,
2412
+ availableTools: tools.map((candidate) => candidate.name)
2413
+ });
2414
+ continue;
2415
+ }
2416
+ let result;
2417
+ if (isControlTool(tool)) {
2418
+ const callArgs = toolCall.args ?? {};
2419
+ log.info("AgentNode dispatching control tool", {
2420
+ nodeId: this.__node_id ?? null,
2421
+ toolCallId: toolCall.id,
2422
+ toolName: toolCall.name,
2423
+ targetNodeId: tool.targetNodeId,
2424
+ argKeys: Object.keys(callArgs)
2425
+ });
2426
+ if (context.hasControlEventSupport) {
2427
+ try {
2428
+ const controlResult = await context.sendControlEvent(tool.targetNodeId, callArgs);
2429
+ result = {
2430
+ status: "completed",
2431
+ target_node_id: tool.targetNodeId,
2432
+ result: controlResult
2433
+ };
2434
+ }
2435
+ catch (err) {
2436
+ result = {
2437
+ status: "error",
2438
+ target_node_id: tool.targetNodeId,
2439
+ error: err instanceof Error ? err.message : String(err)
2440
+ };
2441
+ }
2442
+ }
2443
+ else {
2444
+ result = {
2445
+ status: "error",
2446
+ target_node_id: tool.targetNodeId,
2447
+ error: "Control event dispatch is not available in this execution context"
2448
+ };
2449
+ }
2450
+ }
2451
+ else {
2452
+ log.info("AgentNode executing tool", {
2453
+ nodeId: this.__node_id ?? null,
2454
+ toolCallId: toolCall.id,
2455
+ toolName: toolCall.name
2456
+ });
2457
+ result = await tool.process(context, toolCall.args);
2458
+ }
2459
+ const toolMessage = {
2460
+ role: "tool",
2461
+ toolCallId: toolCall.id,
2462
+ content: JSON.stringify(serializeToolResult(result))
2463
+ };
2464
+ messages.push(toolMessage);
2465
+ await saveThreadMessage(context, threadId, toolMessage);
2466
+ shouldContinue = true;
2467
+ }
2468
+ }
2469
+ }
2470
+ if (structuredSchema) {
2471
+ const structuredText = (lastTextOutput ?? "").trim();
2472
+ if (!structuredText) {
2473
+ const reasoningOnly = Boolean(lastExtractedThinking?.trim());
2474
+ log.error("AgentNode structured output missing text payload", {
2475
+ nodeId: this.__node_id ?? null,
2476
+ providerId,
2477
+ modelId,
2478
+ reasoningOnly,
2479
+ maxTokens
2480
+ });
2481
+ throw new Error(reasoningOnly
2482
+ ? "Agent did not return structured output text: the response was cut off during internal reasoning before any answer was produced. Increase max_tokens on this node (or reduce prompt / reasoning) so the model can finish with valid JSON."
2483
+ : "Agent did not return structured output text.");
2484
+ }
2485
+ lastTextOutput = structuredText;
2486
+ let parsed = extractJson(lastTextOutput);
2487
+ if (!parsed) {
2488
+ // The LLM returned plain text instead of JSON. Rather than failing
2489
+ // the workflow, build a best-effort result by assigning the raw text
2490
+ // to each required string field in the schema.
2491
+ log.warn("AgentNode structured output was not valid JSON, falling back to raw text", {
2492
+ nodeId: this.__node_id ?? null,
2493
+ providerId,
2494
+ modelId,
2495
+ textPreview: lastTextOutput.slice(0, 200)
2496
+ });
2497
+ const props = structuredSchema.properties ?? {};
2498
+ const fallback = {};
2499
+ for (const [name, spec] of Object.entries(props)) {
2500
+ if (spec.type === "string") {
2501
+ fallback[name] = lastTextOutput.trim();
2502
+ }
2503
+ else {
2504
+ fallback[name] = null;
2505
+ }
2506
+ }
2507
+ parsed = fallback;
2508
+ }
2509
+ const required = Array.isArray(structuredSchema.required)
2510
+ ? (structuredSchema.required ?? [])
2511
+ : [];
2512
+ for (const name of required) {
2513
+ if (!(name in parsed)) {
2514
+ log.error("AgentNode structured output missing required field", {
2515
+ nodeId: this.__node_id ?? null,
2516
+ missingField: name,
2517
+ parsedKeys: Object.keys(parsed)
2518
+ });
2519
+ throw new Error(`Agent structured output is missing required field '${name}'`);
2520
+ }
2521
+ }
2522
+ log.info("AgentNode yielding structured output", {
2523
+ nodeId: this.__node_id ?? null,
2524
+ keys: Object.keys(parsed)
2525
+ });
2526
+ yield parsed;
2527
+ }
2528
+ log.info("AgentNode completed", {
2529
+ nodeId: this.__node_id ?? null,
2530
+ providerId,
2531
+ modelId,
2532
+ finalTextLength: lastTextOutput?.length ?? 0,
2533
+ returnedStructured: Boolean(structuredSchema)
2534
+ });
2535
+ }
2536
+ async process(context) {
2537
+ let lastText = "";
2538
+ let lastAudio = null;
2539
+ let structuredResult = null;
2540
+ for await (const item of this.genProcess(context)) {
2541
+ if ("chunk" in item ||
2542
+ "thinking" in item ||
2543
+ "text" in item ||
2544
+ "audio" in item) {
2545
+ if (typeof item.text === "string") {
2546
+ lastText = item.text;
2547
+ }
2548
+ if (item.audio && typeof item.audio === "object") {
2549
+ lastAudio = item.audio;
2550
+ }
2551
+ }
2552
+ else {
2553
+ structuredResult = item;
2554
+ }
2555
+ }
2556
+ if (structuredResult) {
2557
+ log.info("AgentNode process() returning structured result", {
2558
+ nodeId: this.__node_id ?? null,
2559
+ keys: Object.keys(structuredResult)
2560
+ });
2561
+ return structuredResult;
2562
+ }
2563
+ log.info("AgentNode process() returning aggregate result", {
2564
+ nodeId: this.__node_id ?? null,
2565
+ textLength: lastText.length,
2566
+ hasAudio: Boolean(lastAudio)
2567
+ });
2568
+ return {
2569
+ text: lastText,
2570
+ output: lastText,
2571
+ chunk: null,
2572
+ thinking: null,
2573
+ audio: lastAudio
2574
+ };
2575
+ }
2576
+ /**
2577
+ * Dispatch to MultiModeAgent for "plan" and "multi-agent" modes.
2578
+ * Converts ToolLike[] to AgentTool[] and bridges ProcessingMessage to node outputs.
2579
+ */
2580
+ async *genProcessMultiMode(context, providerId, modelId, mode) {
2581
+ const prompt = asText(this.prompt ?? "");
2582
+ const system = asText(this.system ?? DEFAULT_SYSTEM_PROMPT);
2583
+ const rawTools = normalizeTools(this.tools ?? []);
2584
+ // Build control tools
2585
+ const controlContext = this._control_context;
2586
+ const controlTools = buildControlTools(controlContext);
2587
+ if (controlTools.length > 0) {
2588
+ rawTools.push(...controlTools);
2589
+ }
2590
+ // Convert ToolLike[] to AgentTool[] for MultiModeAgent
2591
+ const agentTools = rawTools.map((t) => new ToolLikeAdapter(t));
2592
+ const provider = await context.getProvider(providerId);
2593
+ const agent = new MultiModeAgent({
2594
+ name: `AgentNode_${this.__node_id ?? "default"}`,
2595
+ objective: prompt,
2596
+ provider,
2597
+ model: modelId,
2598
+ mode,
2599
+ tools: agentTools,
2600
+ systemPrompt: system,
2601
+ maxTokenLimit: Number(this.max_tokens ?? 8192),
2602
+ outputSchema: getStructuredOutputSchema(this) ?? undefined,
2603
+ // Plan mode options
2604
+ planningModel: modelId,
2605
+ maxSteps: 10,
2606
+ maxStepIterations: 5,
2607
+ // Multi-agent options
2608
+ numSubAgents: Number(this.num_agents ?? 3),
2609
+ teamStrategy: (this.team_strategy ?? "coordinator"),
2610
+ maxConcurrency: 5
2611
+ });
2612
+ let lastText = "";
2613
+ for await (const msg of agent.execute(context)) {
2614
+ const pmsg = msg;
2615
+ if (pmsg.type === "chunk") {
2616
+ const chunk = pmsg;
2617
+ const content = chunk.content ?? "";
2618
+ lastText += content;
2619
+ yield {
2620
+ chunk: { type: "chunk", content, done: false },
2621
+ thinking: null,
2622
+ text: null,
2623
+ audio: null
2624
+ };
2625
+ }
2626
+ else if (pmsg.type === "step_result") {
2627
+ const result = pmsg.result;
2628
+ if (result != null) {
2629
+ const resultText = typeof result === "string" ? result : JSON.stringify(result);
2630
+ lastText = resultText;
2631
+ }
2632
+ }
2633
+ else if (pmsg.type === "log_update") {
2634
+ log.info("MultiModeAgent log", {
2635
+ nodeId: this.__node_id ?? null,
2636
+ content: pmsg.content
2637
+ });
2638
+ }
2639
+ }
2640
+ const resultText = lastText ||
2641
+ (agent.getResults() != null
2642
+ ? typeof agent.getResults() === "string"
2643
+ ? agent.getResults()
2644
+ : JSON.stringify(agent.getResults())
2645
+ : "");
2646
+ yield { chunk: null, thinking: null, text: resultText, audio: null };
2647
+ return {
2648
+ text: resultText,
2649
+ output: resultText,
2650
+ chunk: null,
2651
+ thinking: null,
2652
+ audio: null
2653
+ };
2654
+ }
2655
+ }
2656
+ __decorate([
2657
+ prop({
2658
+ type: "language_model",
2659
+ default: {
2660
+ type: "language_model",
2661
+ provider: "empty",
2662
+ id: "",
2663
+ name: "",
2664
+ path: null,
2665
+ supported_tasks: []
2666
+ },
2667
+ title: "Model",
2668
+ description: "Model to use for execution"
2669
+ })
2670
+ ], AgentNode.prototype, "model", void 0);
2671
+ __decorate([
2672
+ prop({
2673
+ type: "enum",
2674
+ default: "loop",
2675
+ title: "Mode",
2676
+ description: "How the agent runs.\n\n• loop: standard tool‑calling loop — the LLM responds to the prompt and may iteratively call the connected tools until it produces a final answer. Use this for chat, Q&A, and most tool‑using tasks.\n• plan: the LLM first drafts a multi‑step task plan from the objective, then executes the steps in dependency order (independent steps run in parallel). Best for longer, structured jobs with clear sub‑tasks.\n• multi-agent: auto‑specialises a team of sub‑agents (count controlled by Num Agents) that collaborate on the objective using the chosen Team Strategy. Best for open‑ended objectives that benefit from different roles working together.",
2677
+ values: ["loop", "plan", "multi-agent"]
2678
+ })
2679
+ ], AgentNode.prototype, "mode", void 0);
2680
+ __decorate([
2681
+ prop({
2682
+ type: "str",
2683
+ default: "You are a friendly assistant",
2684
+ title: "System",
2685
+ description: "Instructions that define the agent's persona, role, tone, and global behaviour. Sent to the model as the system message at the start of every run, before any history or user prompt. Use it for things that should always hold (e.g. \"You are a senior Python reviewer. Reply in Markdown.\"). Leave the prompt itself for the per‑run task."
2686
+ })
2687
+ ], AgentNode.prototype, "system", void 0);
2688
+ __decorate([
2689
+ prop({
2690
+ type: "str",
2691
+ default: "",
2692
+ title: "Prompt",
2693
+ description: "The user message for this run — the actual question, task, or content the agent should act on. Appended after the system prompt and conversation history as the latest user turn. Any connected Image or Audio inputs are attached to this message. In plan and multi-agent modes this is treated as the objective for planning."
2694
+ })
2695
+ ], AgentNode.prototype, "prompt", void 0);
2696
+ __decorate([
2697
+ prop({
2698
+ type: "list[tool_name]",
2699
+ default: [],
2700
+ title: "Tools",
2701
+ description: "Tools to enable for the agent. Select workspace tools (read_file, write_file, list_directory) to enable file operations."
2702
+ })
2703
+ ], AgentNode.prototype, "tools", void 0);
2704
+ __decorate([
2705
+ prop({
2706
+ type: "image",
2707
+ default: {
2708
+ type: "image",
2709
+ uri: "",
2710
+ asset_id: null,
2711
+ data: null,
2712
+ metadata: null
2713
+ },
2714
+ title: "Image",
2715
+ description: "The image to analyze"
2716
+ })
2717
+ ], AgentNode.prototype, "image", void 0);
2718
+ __decorate([
2719
+ prop({
2720
+ type: "audio",
2721
+ default: {
2722
+ type: "audio",
2723
+ uri: "",
2724
+ asset_id: null,
2725
+ data: null,
2726
+ metadata: null
2727
+ },
2728
+ title: "Audio",
2729
+ description: "The audio to analyze"
2730
+ })
2731
+ ], AgentNode.prototype, "audio", void 0);
2732
+ __decorate([
2733
+ prop({
2734
+ type: "list[message]",
2735
+ default: [],
2736
+ title: "Messages",
2737
+ description: "Prior conversation turns to include before the current prompt, in chronological order (oldest first). Each item is a Message with a role (user/assistant/tool) and content. Use this to supply ad‑hoc context — for example, few‑shot examples, a previous chat transcript piped in from another node, or the messages output of an upstream Agent. Inserted between the system prompt and the new user prompt. If a Thread ID is also set, history loaded from the thread comes first, then this list, then the current prompt."
2738
+ })
2739
+ ], AgentNode.prototype, "history", void 0);
2740
+ __decorate([
2741
+ prop({
2742
+ type: "str",
2743
+ default: "",
2744
+ title: "Thread ID",
2745
+ description: "Identifier for a persistent conversation thread. When set, the agent loads all earlier messages stored under this ID before this turn and saves the new user message, assistant reply, and any tool messages back to it — giving the agent long‑term memory across runs and across nodes that share the same ID. Leave empty for a stateless one‑shot call. Use the Create Thread node to mint a fresh ID, or wire in the same string from upstream to continue an existing conversation."
2746
+ })
2747
+ ], AgentNode.prototype, "thread_id", void 0);
2748
+ __decorate([
2749
+ prop({
2750
+ type: "int",
2751
+ default: 8192,
2752
+ title: "Max Tokens",
2753
+ description: "Upper bound on the number of tokens the model may generate per response (the model's reply, not the prompt). Higher values allow longer answers but cost more and take longer; very low values may cause the model to truncate mid‑sentence. This is also the maxTokenLimit passed to plan and multi-agent executors. Typical values: 1024 for short answers, 4096–8192 for normal use, 16k+ for long‑form generation. Must be within the chosen model's context window.",
2754
+ min: 1,
2755
+ max: 100000
2756
+ })
2757
+ ], AgentNode.prototype, "max_tokens", void 0);
2758
+ __decorate([
2759
+ prop({
2760
+ type: "int",
2761
+ default: 3,
2762
+ title: "Num Agents",
2763
+ description: "Number of sub‑agents to auto‑specialise when Mode is multi-agent. The planner inspects the objective and creates this many distinct roles (e.g. researcher, writer, critic), each with its own skill set, that then collaborate via the Team Strategy. Ignored in loop and plan modes. More agents allow more division of labour but increase token usage and coordination overhead.",
2764
+ min: 2,
2765
+ max: 10
2766
+ })
2767
+ ], AgentNode.prototype, "num_agents", void 0);
2768
+ __decorate([
2769
+ prop({
2770
+ type: "enum",
2771
+ default: "coordinator",
2772
+ title: "Team Strategy",
2773
+ description: "How the auto‑specialised sub‑agents collaborate when Mode is multi-agent. Ignored in other modes.\n\n• coordinator: the first agent acts as a project manager — it decomposes the objective into tasks on a shared task board, and the remaining agents claim and execute them. Best for well‑defined goals where you want predictable orchestration.\n• autonomous: every agent sees the full objective and self‑organises, claiming tasks, posting messages, and creating new tasks as needed without a central planner. Best for open‑ended exploration.\n• hybrid: a coordinator seeds an initial plan, but worker agents may also create their own subtasks while executing. Balances structure with flexibility.",
2774
+ values: ["coordinator", "autonomous", "hybrid"]
2775
+ })
2776
+ ], AgentNode.prototype, "team_strategy", void 0);
2777
+ export const AGENT_NODES = [
2778
+ SummarizerNode,
2779
+ CreateThreadNode,
2780
+ ExtractorNode,
2781
+ ClassifierNode,
2782
+ AgentNode
2783
+ ];
2784
+ //# sourceMappingURL=agents.js.map