@robota-sdk/agent-core 3.0.0-beta.2 → 3.0.0-beta.21

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 (486) hide show
  1. package/README.md +64 -184
  2. package/dist/abstracts/abstract-agent.d.ts.map +1 -1
  3. package/dist/abstracts/abstract-agent.js.map +1 -1
  4. package/dist/abstracts/abstract-ai-provider.d.ts +8 -7
  5. package/dist/abstracts/abstract-ai-provider.d.ts.map +1 -1
  6. package/dist/abstracts/abstract-ai-provider.js +21 -15
  7. package/dist/abstracts/abstract-ai-provider.js.map +1 -1
  8. package/dist/abstracts/abstract-executor.d.ts.map +1 -1
  9. package/dist/abstracts/abstract-executor.js +20 -12
  10. package/dist/abstracts/abstract-executor.js.map +1 -1
  11. package/dist/abstracts/abstract-manager.d.ts.map +1 -1
  12. package/dist/abstracts/abstract-manager.js.map +1 -1
  13. package/dist/abstracts/abstract-module-events.d.ts +77 -0
  14. package/dist/abstracts/abstract-module-events.d.ts.map +1 -0
  15. package/dist/abstracts/abstract-module-events.js +8 -0
  16. package/dist/abstracts/abstract-module-events.js.map +1 -0
  17. package/dist/abstracts/abstract-module-types.d.ts +110 -0
  18. package/dist/abstracts/abstract-module-types.d.ts.map +1 -0
  19. package/dist/abstracts/abstract-module-types.js +20 -0
  20. package/dist/abstracts/abstract-module-types.js.map +1 -0
  21. package/dist/abstracts/abstract-module.d.ts +13 -312
  22. package/dist/abstracts/abstract-module.d.ts.map +1 -1
  23. package/dist/abstracts/abstract-module.js +137 -302
  24. package/dist/abstracts/abstract-module.js.map +1 -1
  25. package/dist/abstracts/abstract-module.test.d.ts +2 -0
  26. package/dist/abstracts/abstract-module.test.d.ts.map +1 -0
  27. package/dist/abstracts/abstract-module.test.js +150 -0
  28. package/dist/abstracts/abstract-module.test.js.map +1 -0
  29. package/dist/abstracts/abstract-plugin-types.d.ts +152 -0
  30. package/dist/abstracts/abstract-plugin-types.d.ts.map +1 -0
  31. package/dist/abstracts/abstract-plugin-types.js +29 -0
  32. package/dist/abstracts/abstract-plugin-types.js.map +1 -0
  33. package/dist/abstracts/abstract-plugin.d.ts +15 -304
  34. package/dist/abstracts/abstract-plugin.d.ts.map +1 -1
  35. package/dist/abstracts/abstract-plugin.js +28 -144
  36. package/dist/abstracts/abstract-plugin.js.map +1 -1
  37. package/dist/abstracts/abstract-tool.d.ts +1 -1
  38. package/dist/abstracts/abstract-tool.d.ts.map +1 -1
  39. package/dist/abstracts/abstract-tool.js +2 -2
  40. package/dist/abstracts/abstract-tool.js.map +1 -1
  41. package/dist/abstracts/abstract-workflow-converter.d.ts.map +1 -1
  42. package/dist/abstracts/abstract-workflow-converter.js +29 -20
  43. package/dist/abstracts/abstract-workflow-converter.js.map +1 -1
  44. package/dist/abstracts/abstract-workflow-converter.test.d.ts +2 -0
  45. package/dist/abstracts/abstract-workflow-converter.test.d.ts.map +1 -0
  46. package/dist/abstracts/abstract-workflow-converter.test.js +144 -0
  47. package/dist/abstracts/abstract-workflow-converter.test.js.map +1 -0
  48. package/dist/abstracts/abstract-workflow-validator-helpers.d.ts +37 -0
  49. package/dist/abstracts/abstract-workflow-validator-helpers.d.ts.map +1 -0
  50. package/dist/abstracts/abstract-workflow-validator-helpers.js +147 -0
  51. package/dist/abstracts/abstract-workflow-validator-helpers.js.map +1 -0
  52. package/dist/abstracts/abstract-workflow-validator-helpers.test.d.ts +2 -0
  53. package/dist/abstracts/abstract-workflow-validator-helpers.test.d.ts.map +1 -0
  54. package/dist/abstracts/abstract-workflow-validator-helpers.test.js +157 -0
  55. package/dist/abstracts/abstract-workflow-validator-helpers.test.js.map +1 -0
  56. package/dist/abstracts/abstract-workflow-validator.d.ts +18 -130
  57. package/dist/abstracts/abstract-workflow-validator.d.ts.map +1 -1
  58. package/dist/abstracts/abstract-workflow-validator.js +58 -326
  59. package/dist/abstracts/abstract-workflow-validator.js.map +1 -1
  60. package/dist/abstracts/abstract-workflow-validator.test.d.ts +2 -0
  61. package/dist/abstracts/abstract-workflow-validator.test.d.ts.map +1 -0
  62. package/dist/abstracts/abstract-workflow-validator.test.js +142 -0
  63. package/dist/abstracts/abstract-workflow-validator.test.js.map +1 -0
  64. package/dist/abstracts/index.d.ts +1 -0
  65. package/dist/abstracts/index.d.ts.map +1 -1
  66. package/dist/abstracts/index.js +3 -2
  67. package/dist/abstracts/index.js.map +1 -1
  68. package/dist/agents/constants.d.ts +1 -1
  69. package/dist/agents/constants.d.ts.map +1 -1
  70. package/dist/agents/constants.js +1 -1
  71. package/dist/agents/constants.js.map +1 -1
  72. package/dist/agents/index.js +1 -1
  73. package/dist/agents/index.js.map +1 -1
  74. package/dist/agents/robota.test.js +36 -36
  75. package/dist/agents/robota.test.js.map +1 -1
  76. package/dist/browser/index.d.ts +32 -2
  77. package/dist/browser/index.js +6 -4
  78. package/dist/context/index.d.ts +4 -0
  79. package/dist/context/index.d.ts.map +1 -0
  80. package/dist/context/index.js +2 -0
  81. package/dist/context/index.js.map +1 -0
  82. package/dist/context/models.d.ts +27 -0
  83. package/dist/context/models.d.ts.map +1 -0
  84. package/dist/context/models.js +76 -0
  85. package/dist/context/models.js.map +1 -0
  86. package/dist/context/models.test.d.ts +2 -0
  87. package/dist/context/models.test.d.ts.map +1 -0
  88. package/dist/context/models.test.js +51 -0
  89. package/dist/context/models.test.js.map +1 -0
  90. package/dist/context/types.d.ts +25 -0
  91. package/dist/context/types.d.ts.map +1 -0
  92. package/dist/context/types.js +8 -0
  93. package/dist/context/types.js.map +1 -0
  94. package/dist/core/robota-config-manager.d.ts +78 -0
  95. package/dist/core/robota-config-manager.d.ts.map +1 -0
  96. package/dist/core/robota-config-manager.js +216 -0
  97. package/dist/core/robota-config-manager.js.map +1 -0
  98. package/dist/core/robota-execution.d.ts +23 -0
  99. package/dist/core/robota-execution.d.ts.map +1 -0
  100. package/dist/core/robota-execution.js +81 -0
  101. package/dist/core/robota-execution.js.map +1 -0
  102. package/dist/core/robota-initializer.d.ts +37 -0
  103. package/dist/core/robota-initializer.d.ts.map +1 -0
  104. package/dist/core/robota-initializer.js +77 -0
  105. package/dist/core/robota-initializer.js.map +1 -0
  106. package/dist/core/robota-lifecycle.d.ts +53 -0
  107. package/dist/core/robota-lifecycle.d.ts.map +1 -0
  108. package/dist/core/robota-lifecycle.js +73 -0
  109. package/dist/core/robota-lifecycle.js.map +1 -0
  110. package/dist/core/robota-module-manager.d.ts +78 -0
  111. package/dist/core/robota-module-manager.d.ts.map +1 -0
  112. package/dist/core/robota-module-manager.js +114 -0
  113. package/dist/core/robota-module-manager.js.map +1 -0
  114. package/dist/core/robota-plugin-manager.d.ts +40 -0
  115. package/dist/core/robota-plugin-manager.d.ts.map +1 -0
  116. package/dist/core/robota-plugin-manager.js +71 -0
  117. package/dist/core/robota-plugin-manager.js.map +1 -0
  118. package/dist/core/robota.d.ts +56 -606
  119. package/dist/core/robota.d.ts.map +1 -1
  120. package/dist/core/robota.js +210 -1152
  121. package/dist/core/robota.js.map +1 -1
  122. package/dist/core/robota.test.d.ts +2 -0
  123. package/dist/core/robota.test.d.ts.map +1 -0
  124. package/dist/core/robota.test.js +353 -0
  125. package/dist/core/robota.test.js.map +1 -0
  126. package/dist/event-service/event-service.d.ts +61 -0
  127. package/dist/event-service/event-service.d.ts.map +1 -0
  128. package/dist/event-service/event-service.js +120 -0
  129. package/dist/event-service/event-service.js.map +1 -0
  130. package/dist/event-service/index.d.ts +6 -0
  131. package/dist/event-service/index.d.ts.map +1 -0
  132. package/dist/event-service/index.js +4 -0
  133. package/dist/event-service/index.js.map +1 -0
  134. package/dist/event-service/interfaces.d.ts +98 -0
  135. package/dist/event-service/interfaces.d.ts.map +1 -0
  136. package/dist/event-service/interfaces.js +8 -0
  137. package/dist/event-service/interfaces.js.map +1 -0
  138. package/dist/event-service/task-events.d.ts +6 -0
  139. package/dist/event-service/task-events.d.ts.map +1 -0
  140. package/dist/event-service/task-events.js +6 -0
  141. package/dist/event-service/task-events.js.map +1 -0
  142. package/dist/event-service/user-events.d.ts +7 -0
  143. package/dist/event-service/user-events.d.ts.map +1 -0
  144. package/dist/event-service/user-events.js +6 -0
  145. package/dist/event-service/user-events.js.map +1 -0
  146. package/dist/executors/local-executor.d.ts +2 -2
  147. package/dist/executors/local-executor.d.ts.map +1 -1
  148. package/dist/executors/local-executor.js +7 -7
  149. package/dist/executors/local-executor.js.map +1 -1
  150. package/dist/executors/local-executor.test.js +16 -16
  151. package/dist/executors/local-executor.test.js.map +1 -1
  152. package/dist/hooks/hook-runner.d.ts +20 -0
  153. package/dist/hooks/hook-runner.d.ts.map +1 -0
  154. package/dist/hooks/hook-runner.js +95 -0
  155. package/dist/hooks/hook-runner.js.map +1 -0
  156. package/dist/hooks/index.d.ts +3 -0
  157. package/dist/hooks/index.d.ts.map +1 -0
  158. package/dist/hooks/index.js +3 -0
  159. package/dist/hooks/index.js.map +1 -0
  160. package/dist/hooks/types.d.ts +40 -0
  161. package/dist/hooks/types.d.ts.map +1 -0
  162. package/dist/hooks/types.js +5 -0
  163. package/dist/hooks/types.js.map +1 -0
  164. package/dist/index.d.ts +49 -75
  165. package/dist/index.d.ts.map +1 -1
  166. package/dist/index.js +26 -71
  167. package/dist/index.js.map +1 -1
  168. package/dist/interfaces/agent.d.ts +3 -2
  169. package/dist/interfaces/agent.d.ts.map +1 -1
  170. package/dist/interfaces/cache.d.ts +64 -0
  171. package/dist/interfaces/cache.d.ts.map +1 -0
  172. package/dist/interfaces/cache.js +2 -0
  173. package/dist/interfaces/cache.js.map +1 -0
  174. package/dist/interfaces/event-service.d.ts +4 -75
  175. package/dist/interfaces/event-service.d.ts.map +1 -1
  176. package/dist/interfaces/executor.d.ts.map +1 -1
  177. package/dist/interfaces/history-module.d.ts.map +1 -1
  178. package/dist/interfaces/index.d.ts +12 -9
  179. package/dist/interfaces/index.d.ts.map +1 -1
  180. package/dist/interfaces/index.js +2 -1
  181. package/dist/interfaces/index.js.map +1 -1
  182. package/dist/interfaces/manager.d.ts.map +1 -1
  183. package/dist/interfaces/media-provider.d.ts +86 -0
  184. package/dist/interfaces/media-provider.d.ts.map +1 -0
  185. package/dist/interfaces/media-provider.js +13 -0
  186. package/dist/interfaces/media-provider.js.map +1 -0
  187. package/dist/interfaces/messages.d.ts +23 -1
  188. package/dist/interfaces/messages.d.ts.map +1 -1
  189. package/dist/interfaces/messages.js.map +1 -1
  190. package/dist/interfaces/progress-reporting.d.ts.map +1 -1
  191. package/dist/interfaces/progress-reporting.js +1 -3
  192. package/dist/interfaces/progress-reporting.js.map +1 -1
  193. package/dist/interfaces/provider.d.ts +12 -1
  194. package/dist/interfaces/provider.d.ts.map +1 -1
  195. package/dist/interfaces/service.d.ts.map +1 -1
  196. package/dist/interfaces/tool.d.ts +1 -0
  197. package/dist/interfaces/tool.d.ts.map +1 -1
  198. package/dist/interfaces/types.d.ts.map +1 -1
  199. package/dist/interfaces/types.js +6 -6
  200. package/dist/interfaces/types.js.map +1 -1
  201. package/dist/interfaces/workflow-converter.d.ts.map +1 -1
  202. package/dist/interfaces/workflow-validator.d.ts.map +1 -1
  203. package/dist/interfaces/workflow-validator.js.map +1 -1
  204. package/dist/managers/agent-factory.d.ts.map +1 -1
  205. package/dist/managers/agent-factory.js +24 -16
  206. package/dist/managers/agent-factory.js.map +1 -1
  207. package/dist/managers/agent-factory.test.js +35 -25
  208. package/dist/managers/agent-factory.test.js.map +1 -1
  209. package/dist/managers/agent-templates.d.ts.map +1 -1
  210. package/dist/managers/agent-templates.js +22 -10
  211. package/dist/managers/agent-templates.js.map +1 -1
  212. package/dist/managers/agent-templates.test.d.ts +2 -0
  213. package/dist/managers/agent-templates.test.d.ts.map +1 -0
  214. package/dist/managers/agent-templates.test.js +175 -0
  215. package/dist/managers/agent-templates.test.js.map +1 -0
  216. package/dist/managers/ai-provider-manager.d.ts.map +1 -1
  217. package/dist/managers/ai-provider-manager.js +8 -10
  218. package/dist/managers/ai-provider-manager.js.map +1 -1
  219. package/dist/managers/ai-provider-manager.test.d.ts +2 -0
  220. package/dist/managers/ai-provider-manager.test.d.ts.map +1 -0
  221. package/dist/managers/ai-provider-manager.test.js +327 -0
  222. package/dist/managers/ai-provider-manager.test.js.map +1 -0
  223. package/dist/managers/conversation-history-manager.d.ts +13 -425
  224. package/dist/managers/conversation-history-manager.d.ts.map +1 -1
  225. package/dist/managers/conversation-history-manager.js +21 -537
  226. package/dist/managers/conversation-history-manager.js.map +1 -1
  227. package/dist/managers/conversation-history-manager.test.js +82 -43
  228. package/dist/managers/conversation-history-manager.test.js.map +1 -1
  229. package/dist/managers/conversation-message-factory.d.ts +40 -0
  230. package/dist/managers/conversation-message-factory.d.ts.map +1 -0
  231. package/dist/managers/conversation-message-factory.js +66 -0
  232. package/dist/managers/conversation-message-factory.js.map +1 -0
  233. package/dist/managers/conversation-message-factory.test.d.ts +2 -0
  234. package/dist/managers/conversation-message-factory.test.d.ts.map +1 -0
  235. package/dist/managers/conversation-message-factory.test.js +95 -0
  236. package/dist/managers/conversation-message-factory.test.js.map +1 -0
  237. package/dist/managers/conversation-session.d.ts +90 -0
  238. package/dist/managers/conversation-session.d.ts.map +1 -0
  239. package/dist/managers/conversation-session.js +177 -0
  240. package/dist/managers/conversation-session.js.map +1 -0
  241. package/dist/managers/index.d.ts +2 -2
  242. package/dist/managers/index.d.ts.map +1 -1
  243. package/dist/managers/index.js +1 -1
  244. package/dist/managers/index.js.map +1 -1
  245. package/dist/managers/module-registry-validation.d.ts +38 -0
  246. package/dist/managers/module-registry-validation.d.ts.map +1 -0
  247. package/dist/managers/module-registry-validation.js +100 -0
  248. package/dist/managers/module-registry-validation.js.map +1 -0
  249. package/dist/managers/module-registry-validation.test.d.ts +2 -0
  250. package/dist/managers/module-registry-validation.test.d.ts.map +1 -0
  251. package/dist/managers/module-registry-validation.test.js +144 -0
  252. package/dist/managers/module-registry-validation.test.js.map +1 -0
  253. package/dist/managers/module-registry.d.ts +13 -111
  254. package/dist/managers/module-registry.d.ts.map +1 -1
  255. package/dist/managers/module-registry.js +74 -344
  256. package/dist/managers/module-registry.js.map +1 -1
  257. package/dist/managers/module-type-registry-helpers.d.ts +34 -0
  258. package/dist/managers/module-type-registry-helpers.d.ts.map +1 -0
  259. package/dist/managers/module-type-registry-helpers.js +255 -0
  260. package/dist/managers/module-type-registry-helpers.js.map +1 -0
  261. package/dist/managers/module-type-registry.d.ts +10 -64
  262. package/dist/managers/module-type-registry.d.ts.map +1 -1
  263. package/dist/managers/module-type-registry.js +25 -373
  264. package/dist/managers/module-type-registry.js.map +1 -1
  265. package/dist/managers/plugins-helpers.d.ts +18 -0
  266. package/dist/managers/plugins-helpers.d.ts.map +1 -0
  267. package/dist/managers/plugins-helpers.js +118 -0
  268. package/dist/managers/plugins-helpers.js.map +1 -0
  269. package/dist/managers/plugins-helpers.test.d.ts +2 -0
  270. package/dist/managers/plugins-helpers.test.d.ts.map +1 -0
  271. package/dist/managers/plugins-helpers.test.js +68 -0
  272. package/dist/managers/plugins-helpers.test.js.map +1 -0
  273. package/dist/managers/plugins.d.ts +16 -109
  274. package/dist/managers/plugins.d.ts.map +1 -1
  275. package/dist/managers/plugins.js +29 -220
  276. package/dist/managers/plugins.js.map +1 -1
  277. package/dist/managers/plugins.test.d.ts +2 -0
  278. package/dist/managers/plugins.test.d.ts.map +1 -0
  279. package/dist/managers/plugins.test.js +160 -0
  280. package/dist/managers/plugins.test.js.map +1 -0
  281. package/dist/managers/tool-manager.d.ts +1 -1
  282. package/dist/managers/tool-manager.d.ts.map +1 -1
  283. package/dist/managers/tool-manager.js +15 -5
  284. package/dist/managers/tool-manager.js.map +1 -1
  285. package/dist/managers/tool-manager.test.js +32 -16
  286. package/dist/managers/tool-manager.test.js.map +1 -1
  287. package/dist/node/index.cjs +6 -4
  288. package/dist/node/index.d.cts +32 -2
  289. package/dist/node/index.d.ts +32 -2
  290. package/dist/node/index.js +6 -4
  291. package/dist/permissions/index.d.ts +7 -0
  292. package/dist/permissions/index.d.ts.map +1 -0
  293. package/dist/permissions/index.js +4 -0
  294. package/dist/permissions/index.js.map +1 -0
  295. package/dist/permissions/permission-gate.d.ts +38 -0
  296. package/dist/permissions/permission-gate.d.ts.map +1 -0
  297. package/dist/permissions/permission-gate.js +119 -0
  298. package/dist/permissions/permission-gate.js.map +1 -0
  299. package/dist/permissions/permission-mode.d.ts +25 -0
  300. package/dist/permissions/permission-mode.d.ts.map +1 -0
  301. package/dist/permissions/permission-mode.js +58 -0
  302. package/dist/permissions/permission-mode.js.map +1 -0
  303. package/dist/permissions/types.d.ts +27 -0
  304. package/dist/permissions/types.d.ts.map +1 -0
  305. package/dist/permissions/types.js +9 -0
  306. package/dist/permissions/types.js.map +1 -0
  307. package/dist/plugins/event-emitter/metrics.d.ts.map +1 -1
  308. package/dist/plugins/event-emitter/metrics.js +1 -1
  309. package/dist/plugins/event-emitter/metrics.js.map +1 -1
  310. package/dist/plugins/event-emitter/plugin-types.d.ts +88 -0
  311. package/dist/plugins/event-emitter/plugin-types.d.ts.map +1 -0
  312. package/dist/plugins/event-emitter/plugin-types.js +2 -0
  313. package/dist/plugins/event-emitter/plugin-types.js.map +1 -0
  314. package/dist/plugins/event-emitter/types.d.ts +3 -3
  315. package/dist/plugins/event-emitter/types.d.ts.map +1 -1
  316. package/dist/plugins/event-emitter/types.js +4 -4
  317. package/dist/plugins/event-emitter/types.js.map +1 -1
  318. package/dist/plugins/event-emitter-plugin.d.ts +9 -184
  319. package/dist/plugins/event-emitter-plugin.d.ts.map +1 -1
  320. package/dist/plugins/event-emitter-plugin.js +91 -235
  321. package/dist/plugins/event-emitter-plugin.js.map +1 -1
  322. package/dist/schemas/agent-template-schema.d.ts +12 -12
  323. package/dist/schemas/agent-template-schema.d.ts.map +1 -1
  324. package/dist/schemas/agent-template-schema.js +35 -33
  325. package/dist/schemas/agent-template-schema.js.map +1 -1
  326. package/dist/services/cache/cache-key-builder.d.ts +13 -0
  327. package/dist/services/cache/cache-key-builder.d.ts.map +1 -0
  328. package/dist/services/cache/cache-key-builder.js +30 -0
  329. package/dist/services/cache/cache-key-builder.js.map +1 -0
  330. package/dist/services/cache/cache-key-builder.test.d.ts +2 -0
  331. package/dist/services/cache/cache-key-builder.test.d.ts.map +1 -0
  332. package/dist/services/cache/cache-key-builder.test.js +56 -0
  333. package/dist/services/cache/cache-key-builder.test.js.map +1 -0
  334. package/dist/services/cache/execution-cache-service.d.ts +18 -0
  335. package/dist/services/cache/execution-cache-service.d.ts.map +1 -0
  336. package/dist/services/cache/execution-cache-service.js +26 -0
  337. package/dist/services/cache/execution-cache-service.js.map +1 -0
  338. package/dist/services/cache/execution-cache-service.test.d.ts +2 -0
  339. package/dist/services/cache/execution-cache-service.test.d.ts.map +1 -0
  340. package/dist/services/cache/execution-cache-service.test.js +59 -0
  341. package/dist/services/cache/execution-cache-service.test.js.map +1 -0
  342. package/dist/services/cache/index.d.ts +4 -0
  343. package/dist/services/cache/index.d.ts.map +1 -0
  344. package/dist/services/cache/index.js +4 -0
  345. package/dist/services/cache/index.js.map +1 -0
  346. package/dist/services/cache/memory-cache-storage.d.ts +25 -0
  347. package/dist/services/cache/memory-cache-storage.d.ts.map +1 -0
  348. package/dist/services/cache/memory-cache-storage.js +89 -0
  349. package/dist/services/cache/memory-cache-storage.js.map +1 -0
  350. package/dist/services/cache/memory-cache-storage.test.d.ts +2 -0
  351. package/dist/services/cache/memory-cache-storage.test.d.ts.map +1 -0
  352. package/dist/services/cache/memory-cache-storage.test.js +94 -0
  353. package/dist/services/cache/memory-cache-storage.test.js.map +1 -0
  354. package/dist/services/conversation-service/conversation-service.test.d.ts +2 -0
  355. package/dist/services/conversation-service/conversation-service.test.d.ts.map +1 -0
  356. package/dist/services/conversation-service/conversation-service.test.js +248 -0
  357. package/dist/services/conversation-service/conversation-service.test.js.map +1 -0
  358. package/dist/services/conversation-service/index.d.ts +10 -71
  359. package/dist/services/conversation-service/index.d.ts.map +1 -1
  360. package/dist/services/conversation-service/index.js +58 -355
  361. package/dist/services/conversation-service/index.js.map +1 -1
  362. package/dist/services/conversation-service/message-helpers.d.ts +47 -0
  363. package/dist/services/conversation-service/message-helpers.d.ts.map +1 -0
  364. package/dist/services/conversation-service/message-helpers.js +150 -0
  365. package/dist/services/conversation-service/message-helpers.js.map +1 -0
  366. package/dist/services/conversation-service/types.d.ts.map +1 -1
  367. package/dist/services/execution-constants.d.ts +16 -0
  368. package/dist/services/execution-constants.d.ts.map +1 -0
  369. package/dist/services/execution-constants.js +16 -0
  370. package/dist/services/execution-constants.js.map +1 -0
  371. package/dist/services/execution-event-emitter.d.ts +60 -0
  372. package/dist/services/execution-event-emitter.d.ts.map +1 -0
  373. package/dist/services/execution-event-emitter.js +323 -0
  374. package/dist/services/execution-event-emitter.js.map +1 -0
  375. package/dist/services/execution-round.d.ts +58 -0
  376. package/dist/services/execution-round.d.ts.map +1 -0
  377. package/dist/services/execution-round.js +323 -0
  378. package/dist/services/execution-round.js.map +1 -0
  379. package/dist/services/execution-round.test.d.ts +2 -0
  380. package/dist/services/execution-round.test.d.ts.map +1 -0
  381. package/dist/services/execution-round.test.js +188 -0
  382. package/dist/services/execution-round.test.js.map +1 -0
  383. package/dist/services/execution-service.d.ts +28 -125
  384. package/dist/services/execution-service.d.ts.map +1 -1
  385. package/dist/services/execution-service.js +222 -1134
  386. package/dist/services/execution-service.js.map +1 -1
  387. package/dist/services/execution-service.test.js +115 -72
  388. package/dist/services/execution-service.test.js.map +1 -1
  389. package/dist/services/execution-stream.d.ts +32 -0
  390. package/dist/services/execution-stream.d.ts.map +1 -0
  391. package/dist/services/execution-stream.js +268 -0
  392. package/dist/services/execution-stream.js.map +1 -0
  393. package/dist/services/execution-types.d.ts +122 -0
  394. package/dist/services/execution-types.d.ts.map +1 -0
  395. package/dist/services/execution-types.js +48 -0
  396. package/dist/services/execution-types.js.map +1 -0
  397. package/dist/services/history-module.d.ts.map +1 -1
  398. package/dist/services/history-module.js +1 -1
  399. package/dist/services/history-module.js.map +1 -1
  400. package/dist/services/history-module.test.d.ts +2 -0
  401. package/dist/services/history-module.test.d.ts.map +1 -0
  402. package/dist/services/history-module.test.js +137 -0
  403. package/dist/services/history-module.test.js.map +1 -0
  404. package/dist/services/in-memory-history-store.d.ts.map +1 -1
  405. package/dist/services/in-memory-history-store.js +1 -1
  406. package/dist/services/in-memory-history-store.js.map +1 -1
  407. package/dist/services/index.d.ts +0 -1
  408. package/dist/services/index.d.ts.map +1 -1
  409. package/dist/services/index.js +2 -3
  410. package/dist/services/index.js.map +1 -1
  411. package/dist/services/plugin-hook-dispatcher.d.ts +11 -0
  412. package/dist/services/plugin-hook-dispatcher.d.ts.map +1 -0
  413. package/dist/services/plugin-hook-dispatcher.js +68 -0
  414. package/dist/services/plugin-hook-dispatcher.js.map +1 -0
  415. package/dist/services/plugin-hook-dispatcher.test.d.ts +2 -0
  416. package/dist/services/plugin-hook-dispatcher.test.d.ts.map +1 -0
  417. package/dist/services/plugin-hook-dispatcher.test.js +93 -0
  418. package/dist/services/plugin-hook-dispatcher.test.js.map +1 -0
  419. package/dist/services/plugin-priority.test.d.ts +2 -0
  420. package/dist/services/plugin-priority.test.d.ts.map +1 -0
  421. package/dist/services/plugin-priority.test.js +119 -0
  422. package/dist/services/plugin-priority.test.js.map +1 -0
  423. package/dist/services/tool-execution-service.d.ts +1 -0
  424. package/dist/services/tool-execution-service.d.ts.map +1 -1
  425. package/dist/services/tool-execution-service.js +89 -46
  426. package/dist/services/tool-execution-service.js.map +1 -1
  427. package/dist/services/tool-execution-service.test.d.ts +2 -0
  428. package/dist/services/tool-execution-service.test.d.ts.map +1 -0
  429. package/dist/services/tool-execution-service.test.js +358 -0
  430. package/dist/services/tool-execution-service.test.js.map +1 -0
  431. package/dist/tool-registry/function-tool.d.ts +54 -0
  432. package/dist/tool-registry/function-tool.d.ts.map +1 -0
  433. package/dist/tool-registry/function-tool.js +190 -0
  434. package/dist/tool-registry/function-tool.js.map +1 -0
  435. package/dist/tool-registry/index.d.ts +3 -0
  436. package/dist/tool-registry/index.d.ts.map +1 -0
  437. package/dist/tool-registry/index.js +3 -0
  438. package/dist/tool-registry/index.js.map +1 -0
  439. package/dist/tool-registry/tool-registry.d.ts +54 -0
  440. package/dist/tool-registry/tool-registry.d.ts.map +1 -0
  441. package/dist/tool-registry/tool-registry.js +148 -0
  442. package/dist/tool-registry/tool-registry.js.map +1 -0
  443. package/dist/utils/errors.d.ts +11 -2
  444. package/dist/utils/errors.d.ts.map +1 -1
  445. package/dist/utils/errors.js +13 -2
  446. package/dist/utils/errors.js.map +1 -1
  447. package/dist/utils/errors.test.d.ts +2 -0
  448. package/dist/utils/errors.test.d.ts.map +1 -0
  449. package/dist/utils/errors.test.js +361 -0
  450. package/dist/utils/errors.test.js.map +1 -0
  451. package/dist/utils/execution-proxy.d.ts +7 -3
  452. package/dist/utils/execution-proxy.d.ts.map +1 -1
  453. package/dist/utils/execution-proxy.js +64 -42
  454. package/dist/utils/execution-proxy.js.map +1 -1
  455. package/dist/utils/execution-proxy.test.d.ts +2 -0
  456. package/dist/utils/execution-proxy.test.d.ts.map +1 -0
  457. package/dist/utils/execution-proxy.test.js +179 -0
  458. package/dist/utils/execution-proxy.test.js.map +1 -0
  459. package/dist/utils/logger.d.ts +3 -3
  460. package/dist/utils/logger.d.ts.map +1 -1
  461. package/dist/utils/logger.js +9 -5
  462. package/dist/utils/logger.js.map +1 -1
  463. package/dist/utils/message-converter.d.ts +1 -4
  464. package/dist/utils/message-converter.d.ts.map +1 -1
  465. package/dist/utils/message-converter.js +13 -12
  466. package/dist/utils/message-converter.js.map +1 -1
  467. package/dist/utils/message-converter.test.d.ts +2 -0
  468. package/dist/utils/message-converter.test.d.ts.map +1 -0
  469. package/dist/utils/message-converter.test.js +164 -0
  470. package/dist/utils/message-converter.test.js.map +1 -0
  471. package/dist/utils/periodic-task.d.ts +1 -1
  472. package/dist/utils/periodic-task.d.ts.map +1 -1
  473. package/dist/utils/periodic-task.js +1 -1
  474. package/dist/utils/periodic-task.js.map +1 -1
  475. package/dist/utils/periodic-task.test.d.ts +2 -0
  476. package/dist/utils/periodic-task.test.d.ts.map +1 -0
  477. package/dist/utils/periodic-task.test.js +87 -0
  478. package/dist/utils/periodic-task.test.js.map +1 -0
  479. package/dist/utils/validation.d.ts.map +1 -1
  480. package/dist/utils/validation.js +17 -11
  481. package/dist/utils/validation.js.map +1 -1
  482. package/dist/utils/validation.test.d.ts +2 -0
  483. package/dist/utils/validation.test.d.ts.map +1 -0
  484. package/dist/utils/validation.test.js +249 -0
  485. package/dist/utils/validation.test.js.map +1 -0
  486. package/package.json +1 -1
@@ -1,29 +1,17 @@
1
- import { ToolExecutionService, TOOL_EVENT_PREFIX } from './tool-execution-service';
1
+ import { ToolExecutionService } from './tool-execution-service';
2
2
  import { createLogger } from '../utils/logger';
3
- import { isDefaultEventService, bindWithOwnerPath } from './event-service';
3
+ // Re-export constants for public API compatibility
4
+ export { EXECUTION_EVENTS, EXECUTION_EVENT_PREFIX } from './execution-constants';
5
+ import { EXECUTION_EVENTS } from './execution-constants';
6
+ import { PREVIEW_LENGTH, ID_RADIX, ID_RANDOM_LENGTH, } from './execution-types';
7
+ import { ExecutionEventEmitter } from './execution-event-emitter';
8
+ import { callPluginHook } from './plugin-hook-dispatcher';
9
+ import { executeStream as executeStreamFn } from './execution-stream';
10
+ import { executeRound } from './execution-round';
4
11
  /**
5
- * ExecutionService owned events
6
- * Local event names only (no dots). Full names are composed at emit time.
7
- */
8
- export const EXECUTION_EVENTS = {
9
- START: 'start',
10
- COMPLETE: 'complete',
11
- ERROR: 'error',
12
- ASSISTANT_MESSAGE_START: 'assistant_message_start',
13
- ASSISTANT_MESSAGE_COMPLETE: 'assistant_message_complete',
14
- USER_MESSAGE: 'user_message',
15
- TOOL_RESULTS_TO_LLM: 'tool_results_to_llm',
16
- TOOL_RESULTS_READY: 'tool_results_ready'
17
- };
18
- export const EXECUTION_EVENT_PREFIX = 'execution';
19
- // Type guard to check if error has execution properties
20
- function isExecutionError(error) {
21
- return 'executionId' in error || 'toolName' in error;
22
- }
23
- /**
24
- * Service that orchestrates the entire execution pipeline
25
- * Coordinates AI provider execution, tool execution service, and plugin lifecycle
26
- * Uses centralized conversation history management
12
+ * Service that orchestrates the entire execution pipeline.
13
+ * Coordinates AI provider execution, tool execution service, and plugin lifecycle.
14
+ * Uses centralized conversation history management.
27
15
  */
28
16
  export class ExecutionService {
29
17
  toolExecutionService;
@@ -32,15 +20,9 @@ export class ExecutionService {
32
20
  conversationHistory;
33
21
  plugins = [];
34
22
  logger;
35
- baseEventService;
36
- executionContext; // 🎯 [CONTEXT-INJECTION] Parent execution context
37
- ownerPathBase;
38
- toolEventServices;
39
- agentOwnerPathBase;
40
- // Path-only: remove lastResponseExecutionId tracking
41
- lastResponseExecutionId;
42
- constructor(aiProviders, tools, conversationHistory, eventService, executionContext // 🎯 [CONTEXT-INJECTION] Accept parent context
43
- ) {
23
+ eventEmitter;
24
+ cacheService;
25
+ constructor(aiProviders, tools, conversationHistory, eventService, executionContext, cacheService) {
44
26
  this.toolExecutionService = new ToolExecutionService(tools);
45
27
  this.aiProviders = aiProviders;
46
28
  this.tools = tools;
@@ -50,30 +32,31 @@ export class ExecutionService {
50
32
  if (!eventService) {
51
33
  throw new Error('[EXECUTION] EventService is required');
52
34
  }
53
- this.baseEventService = eventService;
54
- this.executionContext = executionContext; // 🎯 [CONTEXT-INJECTION] Store parent context
55
- this.ownerPathBase = this.buildBaseOwnerPath(executionContext);
56
- this.toolEventServices = new Map();
57
- this.agentOwnerPathBase = [];
35
+ this.eventEmitter = new ExecutionEventEmitter(eventService, this.logger, executionContext);
36
+ this.cacheService = cacheService;
58
37
  }
59
- /**
60
- * Register a plugin
61
- */
38
+ /** Register a plugin */
62
39
  registerPlugin(plugin) {
63
- this.plugins.push(plugin);
40
+ const pluginPriority = plugin.priority ?? 0;
41
+ const insertIndex = this.plugins.findIndex((p) => (p.priority ?? 0) < pluginPriority);
42
+ if (insertIndex === -1) {
43
+ this.plugins.push(plugin);
44
+ }
45
+ else {
46
+ this.plugins.splice(insertIndex, 0, plugin);
47
+ }
64
48
  this.logger.debug('Plugin registered', {
65
49
  pluginName: plugin.name,
50
+ priority: pluginPriority,
66
51
  hasBeforeRun: typeof plugin.beforeRun,
67
52
  hasAfterRun: typeof plugin.afterRun,
68
53
  hasBeforeProviderCall: typeof plugin.beforeProviderCall,
69
- hasAfterProviderCall: typeof plugin.afterProviderCall
54
+ hasAfterProviderCall: typeof plugin.afterProviderCall,
70
55
  });
71
56
  }
72
- /**
73
- * Remove a plugin
74
- */
57
+ /** Remove a plugin */
75
58
  removePlugin(pluginName) {
76
- const index = this.plugins.findIndex(p => p.name === pluginName);
59
+ const index = this.plugins.findIndex((p) => p.name === pluginName);
77
60
  if (index !== -1) {
78
61
  this.plugins.splice(index, 1);
79
62
  this.logger.debug('Plugin removed', { pluginName });
@@ -81,24 +64,16 @@ export class ExecutionService {
81
64
  }
82
65
  return false;
83
66
  }
84
- /**
85
- * Get a plugin by name
86
- */
67
+ /** Get a plugin by name */
87
68
  getPlugin(pluginName) {
88
- return this.plugins.find(p => p.name === pluginName) ?? null;
69
+ return this.plugins.find((p) => p.name === pluginName);
89
70
  }
90
- /**
91
- * Get all registered plugins
92
- */
71
+ /** Get all registered plugins */
93
72
  getPlugins() {
94
73
  return [...this.plugins];
95
74
  }
96
- /**
97
- * Execute the full pipeline with centralized history management
98
- */
75
+ /** Execute the full pipeline with centralized history management */
99
76
  async execute(input, messages, config, context) {
100
- // [EXECUTION-DEBUG] ExecutionService.execute entrypoint
101
- // Avoid console usage; use injected logger only.
102
77
  const executionId = this.generateExecutionId();
103
78
  const startTime = new Date();
104
79
  const conversationId = this.requireConversationId(context, 'execute');
@@ -110,1128 +85,246 @@ export class ExecutionService {
110
85
  conversationId,
111
86
  ...(context?.sessionId && { sessionId: context.sessionId }),
112
87
  ...(context?.userId && { userId: context.userId }),
113
- ...(context?.metadata && { metadata: context.metadata })
88
+ ...(context?.metadata && { metadata: context.metadata }),
114
89
  };
115
- this.prepareOwnerPathBases(conversationId);
90
+ this.eventEmitter.prepareOwnerPathBases(conversationId);
116
91
  this.logger.debug('Starting execution pipeline', {
117
92
  executionId,
118
93
  conversationId,
119
94
  messageCount: messages.length,
120
- hasContext: !!context
95
+ hasContext: !!context,
121
96
  });
122
- // Get current provider info and tools for rich data
123
- const currentInfo = this.aiProviders.getCurrentProvider();
124
- const provider = currentInfo ? this.aiProviders.getProvider(currentInfo.provider) : null;
125
- if (!currentInfo || !currentInfo.provider || !provider) {
126
- throw new Error('[EXECUTION] Provider is required');
127
- }
128
- const availableTools = this.tools.getTools();
129
- // Emit execution start event
130
- const rootId = conversationId;
131
- const aiProviderInfo = {
132
- providerName: currentInfo.provider,
133
- model: config.defaultModel.model,
134
- temperature: config.defaultModel.temperature,
135
- maxTokens: config.defaultModel.maxTokens
136
- };
137
- const toolsInfo = availableTools.map((tool) => {
138
- const paramSchema = tool.parameters;
139
- const props = paramSchema?.properties;
140
- if (!tool.description || tool.description.length === 0) {
141
- throw new Error(`[EXECUTION] Tool "${tool.name}" is missing description`);
142
- }
143
- return {
144
- name: tool.name,
145
- description: tool.description,
146
- parameters: props && typeof props === 'object' ? Object.keys(props) : []
147
- };
148
- });
149
- this.emitExecution(EXECUTION_EVENTS.START, {
150
- parameters: {
151
- input,
152
- agentConfiguration: aiProviderInfo,
153
- availableTools: toolsInfo,
154
- toolCount: toolsInfo.length,
155
- hasTools: toolsInfo.length > 0,
156
- systemMessage: config.defaultModel.systemMessage,
157
- provider: config.defaultModel.provider,
158
- model: config.defaultModel.model,
159
- temperature: config.defaultModel.temperature,
160
- maxTokens: config.defaultModel.maxTokens
161
- },
162
- metadata: {
163
- method: 'execute',
164
- inputLength: input.length,
165
- messageCount: messages.length,
166
- aiProvider: aiProviderInfo.providerName,
167
- model: aiProviderInfo.model,
168
- toolsAvailable: toolsInfo.map((t) => t.name),
169
- agentCapabilities: {
170
- canUseTools: toolsInfo.length > 0,
171
- supportedActions: toolsInfo.map((t) => t.name)
172
- }
173
- }
174
- }, rootId, executionId);
97
+ const resolved = this.resolveProviderAndTools(config);
98
+ this.eventEmitter.emitExecutionStartEvent(input, config, messages, resolved, conversationId, executionId);
175
99
  try {
176
- // Get conversation session for this conversation
177
- const conversationSession = this.conversationHistory.getConversationSession(conversationId);
178
- // Initialize conversation history with existing messages if this is first time
179
- if (conversationSession.getMessageCount() === 0 && messages.length > 0) {
180
- // Add all messages in the order they appear in the messages array
181
- // This preserves the original order including multiple system messages
182
- messages.forEach(msg => {
183
- if (msg.role === 'user') {
184
- conversationSession.addUserMessage(msg.content, msg.metadata);
185
- }
186
- else if (msg.role === 'assistant') {
187
- conversationSession.addAssistantMessage(msg.content, msg.toolCalls, msg.metadata);
188
- }
189
- else if (msg.role === 'system') {
190
- conversationSession.addSystemMessage(msg.content, msg.metadata);
191
- }
192
- else if (msg.role === 'tool') {
193
- const toolName = msg.metadata?.['toolName'];
194
- if (typeof toolName !== 'string' || toolName.length === 0) {
195
- throw new Error('[EXECUTION] Tool message missing toolName metadata');
196
- }
197
- conversationSession.addToolMessageWithId(msg.content, msg.toolCallId, toolName, msg.metadata);
198
- }
199
- });
200
- }
201
- // Add system message from config if provided and not already present
202
- // This allows for additional system messages during execution but prevents duplicates
203
- if (config.systemMessage) {
204
- const existingMessages = conversationSession.getMessages();
205
- const hasConfigSystemMessage = existingMessages.some(msg => msg.role === 'system' && msg.content === config.systemMessage);
206
- if (!hasConfigSystemMessage) {
207
- conversationSession.addSystemMessage(config.systemMessage, { executionId });
208
- }
209
- }
210
- // Only add the current input if it's not already the last message in the conversation
211
- const existingMessages = conversationSession.getMessages();
212
- const lastMessage = existingMessages[existingMessages.length - 1];
213
- const shouldAddInput = !lastMessage ||
214
- lastMessage.role !== 'user' ||
215
- lastMessage.content !== input;
216
- if (shouldAddInput) {
217
- conversationSession.addUserMessage(input, { executionId });
218
- const rootId = conversationId;
219
- this.emitExecution(EXECUTION_EVENTS.USER_MESSAGE, {
220
- parameters: {
221
- input,
222
- userPrompt: input,
223
- userMessageContent: input,
224
- messageLength: input.length,
225
- wordCount: input.split(/\s+/).filter(word => word.length > 0).length,
226
- characterCount: input.length
227
- },
228
- metadata: {
229
- messageRole: 'user',
230
- inputLength: input.length,
231
- messageType: 'user_message',
232
- hasQuestions: input.includes('?'),
233
- containsUrgency: /urgent|asap|critical|emergency/i.test(input),
234
- estimatedComplexity: input.length > 200 ? 'high' : input.length > 50 ? 'medium' : 'low'
235
- }
236
- }, rootId, executionId);
237
- }
238
- // Call beforeRun hook on all plugins
239
- await this.callPluginHook('beforeRun', {
100
+ const conversationSession = this.initializeConversationSession(conversationId, messages, config, executionId);
101
+ conversationSession.addUserMessage(input, { executionId });
102
+ this.eventEmitter.emitUserMessageEvent(input, conversationId, executionId);
103
+ await callPluginHook(this.plugins, 'beforeRun', {
240
104
  input,
241
- ...(context?.metadata ? { metadata: context.metadata } : {})
242
- });
243
- // Use already retrieved provider info from rich data collection above
244
- if (!currentInfo) {
245
- throw new Error('No AI provider configured');
246
- }
247
- if (!provider) {
248
- throw new Error(`AI provider '${currentInfo.provider}' not found`);
249
- }
250
- // Provider implements IAIProvider, so chat() must exist.
251
- if (typeof provider.chat !== 'function') {
252
- throw new Error('Provider must have chat method to support execution');
253
- }
254
- // Process with conversation loop - now delegated to provider
255
- let toolsExecuted = [];
256
- let maxRounds = 10; // Increased limit for complex team delegation scenarios
257
- let currentRound = 0;
258
- while (currentRound < maxRounds) {
259
- currentRound++;
260
- // [ROUND-DEBUG] Round start logging
261
- this.logger.info(`🔄 [ROUND-DEBUG] Starting Round ${currentRound} for agent ${fullContext.conversationId}`);
262
- this.logger.debug(`🔄 [ROUND-${currentRound}] Starting execution round ${currentRound}`, {
263
- executionId,
264
- conversationId: fullContext.conversationId,
265
- round: currentRound,
266
- maxRounds: maxRounds
267
- });
268
- // Generate the thinking node id for this round at round start.
269
- const rootId = conversationId;
270
- // Path-only stable thinking id: conversation-level round (next assistant turn)
271
- const historyMessages = conversationSession.getMessages();
272
- if (!Array.isArray(historyMessages)) {
273
- throw new Error('[EXECUTION] Conversation messages must be an array');
274
- }
275
- const assistantMessages = historyMessages.filter((message) => message.role === 'assistant');
276
- const assistantMessageCount = assistantMessages.length;
277
- const latestAssistantMessage = assistantMessages.length > 0
278
- ? assistantMessages[assistantMessages.length - 1]
279
- : undefined;
280
- const shouldChainFromPreviousToolResult = Array.isArray(latestAssistantMessage?.toolCalls) &&
281
- latestAssistantMessage.toolCalls.length > 0;
282
- const thinkingNodeId = `thinking_${rootId}_round${assistantMessageCount + 1}`;
283
- const previousThinkingNodeId = shouldChainFromPreviousToolResult
284
- ? `thinking_${rootId}_round${assistantMessageCount}`
285
- : undefined;
286
- // Get messages from conversation history
287
- const conversationMessages = historyMessages;
288
- this.logger.debug('Current conversation messages', {
289
- round: currentRound,
290
- messageCount: conversationMessages.length,
291
- fullHistory: conversationMessages.map((m, index) => ({
292
- index,
293
- role: m.role,
294
- content: m.content?.substring(0, 100),
295
- hasToolCalls: 'toolCalls' in m ? !!m.toolCalls?.length : false,
296
- toolCallId: 'toolCallId' in m ? m.toolCallId : undefined,
297
- toolCallsCount: 'toolCalls' in m ? m.toolCalls?.length : 0
298
- }))
299
- });
300
- // Call beforeProviderCall hook
301
- await this.callPluginHook('beforeProviderCall', {
302
- messages: conversationMessages
303
- });
304
- this.logger.debug('Sending messages to AI provider', {
305
- round: currentRound,
306
- messageCount: conversationMessages.length,
307
- lastFewMessages: conversationMessages.slice(-5).map(m => ({
308
- role: m.role,
309
- content: m.content?.substring(0, 50),
310
- hasToolCalls: 'toolCalls' in m ? !!m.toolCalls?.length : false,
311
- toolCallId: 'toolCallId' in m ? m.toolCallId : undefined
312
- }))
313
- });
314
- // Validate required model configuration - use new defaultModel format
315
- if (!config.defaultModel?.model) {
316
- throw new Error('Model is required in defaultModel configuration. Please specify a model.');
317
- }
318
- if (typeof config.defaultModel.model !== 'string' || config.defaultModel.model.trim() === '') {
319
- throw new Error('Model must be a non-empty string in defaultModel configuration.');
320
- }
321
- // Delegate entire execution to provider
322
- const availableTools = this.tools.getTools();
323
- const chatOptions = {
324
- model: config.defaultModel.model,
325
- ...(config.defaultModel.maxTokens !== undefined && { maxTokens: config.defaultModel.maxTokens }),
326
- ...(config.defaultModel.temperature !== undefined && { temperature: config.defaultModel.temperature }),
327
- ...(availableTools.length > 0 && { tools: availableTools })
328
- };
329
- // Emit assistant message start event for each thinking phase.
330
- // Absolute path-only: the path tail must be the thinking node id for this round.
331
- this.emitWithContext(EXECUTION_EVENTS.ASSISTANT_MESSAGE_START, {
332
- parameters: {
333
- round: currentRound,
334
- messageCount: conversationMessages.length
335
- },
336
- metadata: {
337
- round: currentRound,
338
- thinkingNodeId
339
- }
340
- }, () => this.buildThinkingOwnerContext(rootId, executionId, thinkingNodeId, previousThinkingNodeId), ctx => {
341
- if (!ctx.ownerType || !ctx.ownerId) {
342
- throw new Error('[EXECUTION] Missing owner context for thinking event');
343
- }
344
- return bindWithOwnerPath(this.baseEventService, {
345
- ownerType: ctx.ownerType,
346
- ownerId: ctx.ownerId,
347
- ownerPath: ctx.ownerPath
348
- });
349
- });
350
- const response = await provider.chat(conversationMessages, chatOptions);
351
- const assistantToolCallsFromResponse = response.role === 'assistant'
352
- ? response.toolCalls
353
- : undefined;
354
- if (typeof response.content !== 'string') {
355
- throw new Error('[EXECUTION] Provider response content is required');
356
- }
357
- if (assistantToolCallsFromResponse && !Array.isArray(assistantToolCallsFromResponse)) {
358
- throw new Error('[EXECUTION] assistant toolCalls must be an array');
359
- }
360
- this.logger.debug(`🤖 [ROUND-${currentRound}] Provider response completed`, {
361
- executionId,
362
- conversationId: fullContext.conversationId,
363
- round: currentRound,
364
- responseLength: response.content.length,
365
- hasToolCalls: Array.isArray(assistantToolCallsFromResponse) && assistantToolCallsFromResponse.length > 0,
366
- toolCallsCount: Array.isArray(assistantToolCallsFromResponse) ? assistantToolCallsFromResponse.length : 0
367
- });
368
- // Call afterProviderCall hook
369
- await this.callPluginHook('afterProviderCall', {
370
- messages: conversationMessages,
371
- responseMessage: response
372
- });
373
- // Add assistant response to history
374
- // Response from AI provider should always be assistant message
375
- if (response.role !== 'assistant') {
376
- throw new Error(`Unexpected response role: ${response.role}`);
377
- }
378
- const assistantResponse = response;
379
- if (typeof assistantResponse.content !== 'string') {
380
- throw new Error('[EXECUTION] assistant response content is required');
381
- }
382
- const assistantToolCalls = assistantResponse.toolCalls ?? [];
383
- if (!Array.isArray(assistantToolCalls)) {
384
- throw new Error('[EXECUTION] assistantResponse.toolCalls must be an array');
385
- }
386
- conversationSession.addAssistantMessage(assistantResponse.content, assistantToolCalls, {
387
- round: currentRound,
388
- ...(assistantResponse.metadata?.['usage'] && { usage: assistantResponse.metadata['usage'] })
389
- });
390
- this.logger.debug(`[RULE-9-DEBUG] Round ${currentRound} response check: toolCalls=${assistantToolCalls.length}`, {
391
- round: currentRound,
392
- hasToolCalls: assistantToolCalls.length > 0,
393
- toolCallsLength: assistantToolCalls.length,
394
- responseContent: assistantResponse.content.substring(0, 100) + '...'
395
- });
396
- // [ROUND2-DEBUG] Extra diagnostics for Round 2 response
397
- if (currentRound === 2) {
398
- this.logger.info(`🔍 [ROUND2-DEBUG] Round 2 AI Response for agent ${fullContext.conversationId}:`);
399
- this.logger.info(`🔍 [ROUND2-DEBUG] - Content: ${assistantResponse.content.substring(0, 200)}...`);
400
- this.logger.info(`🔍 [ROUND2-DEBUG] - Tool Calls: ${assistantToolCalls.length}`);
401
- if (assistantToolCalls.length > 0) {
402
- this.logger.info(`🔍 [ROUND2-DEBUG] - Tool Call Details: ${JSON.stringify(assistantToolCalls.map(tc => ({ id: tc.id, name: tc.function?.name })))}`);
403
- }
404
- }
405
- if (assistantToolCalls.length === 0) {
406
- // No tools to execute, we're done
407
- this.logger.info(`🔄 [ROUND-DEBUG] Round ${currentRound} ENDING - no tool calls for agent ${fullContext.conversationId}`);
408
- this.logger.debug(`[AGENT-FLOW-CONTROL] Round ${currentRound} completed - no tool calls, execution finished for agent ${fullContext.conversationId}`);
409
- // [VERIFICATION] Validate ExecutionService flow-control logic
410
- this.logger.info(`🔍 [EXECUTION-VERIFICATION] Agent ${fullContext.conversationId} - Round ${currentRound} - No tool calls detected`);
411
- this.logger.info(`🔍 [EXECUTION-VERIFICATION] ExecutionContext exists: ${!!this.executionContext}`);
412
- if (this.executionContext) {
413
- this.logger.info(`🔍 [EXECUTION-VERIFICATION] Parent ID: ${this.executionContext.parentExecutionId}`);
414
- this.logger.info(`🔍 [EXECUTION-VERIFICATION] Execution Level: ${this.executionContext.executionLevel}`);
415
- }
416
- // [EVENT-ORTHODOXY] Emit events consistently; do not conditionally suppress emission.
417
- // The handler decides whether to process the event based on context.
418
- this.logger.info(`🔧 [EVENT-ORTHODOXY] Emitting assistant_message_complete for Round ${currentRound} completion (no tool calls)`);
419
- if (typeof assistantResponse.content !== 'string' || assistantResponse.content.length === 0) {
420
- throw new Error('[EXECUTION] assistant response content is required');
421
- }
422
- if (!(assistantResponse.timestamp instanceof Date)) {
423
- throw new Error('[EXECUTION] assistant response timestamp is required');
424
- }
425
- const responseContent = assistantResponse.content;
426
- const responseStartTime = assistantResponse.timestamp;
427
- const responseDuration = new Date().getTime() - responseStartTime.getTime();
428
- this.emitWithContext(EXECUTION_EVENTS.ASSISTANT_MESSAGE_COMPLETE, {
429
- parameters: {
430
- assistantMessage: responseContent,
431
- responseLength: responseContent.length,
432
- wordCount: responseContent.split(/\s+/).filter(word => word.length > 0).length,
433
- responseTime: responseDuration,
434
- contentPreview: responseContent.length > 200
435
- ? responseContent.substring(0, 200) + '...'
436
- : responseContent
437
- },
438
- result: {
439
- success: true,
440
- data: responseContent.substring(0, 100) + '...',
441
- fullResponse: responseContent,
442
- responseMetrics: {
443
- length: responseContent.length,
444
- estimatedReadTime: Math.ceil(responseContent.split(/\s+/).length / 200),
445
- hasCodeBlocks: /```/.test(responseContent),
446
- hasLinks: /https?:\/\//.test(responseContent),
447
- complexity: responseContent.length > 1000 ? 'high' : responseContent.length > 300 ? 'medium' : 'low'
448
- }
449
- },
450
- metadata: {
451
- executionId,
452
- round: currentRound,
453
- completed: true,
454
- reason: 'no_tool_calls',
455
- responseCharacteristics: {
456
- hasQuestions: responseContent.includes('?'),
457
- isError: /error|fail|wrong/i.test(responseContent),
458
- isComplete: /complete|done|finish/i.test(responseContent),
459
- containsNumbers: /\d/.test(responseContent)
460
- }
461
- }
462
- }, () => this.buildResponseOwnerContext(rootId, executionId, thinkingNodeId, previousThinkingNodeId), ctx => {
463
- if (!ctx.ownerType || !ctx.ownerId) {
464
- throw new Error('[EXECUTION] Missing owner context for response event');
465
- }
466
- return bindWithOwnerPath(this.baseEventService, {
467
- ownerType: ctx.ownerType,
468
- ownerId: ctx.ownerId,
469
- ownerPath: ctx.ownerPath
470
- });
471
- });
472
- this.logger.info(`🔍 [EXECUTION-VERIFICATION] Breaking execution loop - should prevent Round ${currentRound + 1}`);
473
- break;
474
- }
475
- else {
476
- // Tools are triggered in this round. Do not emit assistant_message_complete yet.
477
- // Completion will be emitted when a subsequent assistant turn finishes without tool calls.
478
- }
479
- // [ROUND-DEBUG] Continue round: tool calls present
480
- this.logger.info(`🔄 [ROUND-DEBUG] Round ${currentRound} CONTINUING - ${assistantToolCalls.length} tool calls for agent ${fullContext.conversationId}`);
481
- this.logger.info(`🔄 [ROUND-DEBUG] Agent instance conversationId=${fullContext.conversationId}`);
482
- this.logger.debug('Tool calls detected, executing tools', {
483
- toolCallCount: assistantToolCalls.length,
484
- round: currentRound,
485
- toolCalls: assistantToolCalls.map((tc) => ({ id: tc.id, name: tc.function?.name }))
486
- });
487
- // Execute tools
488
- // Ensure proper ID hierarchy for tool execution
489
- const toolRootId = conversationId;
490
- const rootForTools = toolRootId;
491
- // Absolute path-only: tool calls must be children of the thinking node (fork point).
492
- const toolOwnerPathBase = this.buildThinkingOwnerContext(rootForTools, executionId, thinkingNodeId, previousThinkingNodeId).ownerPath;
493
- const expectedCountForBatch = assistantToolCalls.length;
494
- const batchId = `${thinkingNodeId}`;
495
- const toolRequestsBase = this.toolExecutionService.createExecutionRequestsWithContext(assistantToolCalls, {
496
- ownerPathBase: toolOwnerPathBase,
497
- metadataFactory: toolCall => ({
498
- conversationId: toolRootId,
499
- round: currentRound,
500
- directParentId: thinkingNodeId,
501
- batchId,
502
- expectedCount: expectedCountForBatch,
503
- toolCallId: toolCall.id
504
- })
505
- });
506
- const toolRequests = toolRequestsBase.map(request => {
507
- if (!request.ownerId) {
508
- throw new Error('[EXECUTION] Tool request missing ownerId');
509
- }
510
- return {
511
- ...request,
512
- eventService: this.ensureToolEventService(request.ownerId, request.ownerPath),
513
- baseEventService: this.baseEventService
514
- };
515
- });
516
- const toolContext = {
517
- requests: toolRequests,
518
- mode: 'parallel',
519
- maxConcurrency: 5,
520
- continueOnError: true
521
- };
522
- const toolSummary = await this.toolExecutionService.executeTools(toolContext);
523
- toolsExecuted.push(...toolSummary.results.map(r => {
524
- if (!r.toolName || r.toolName.length === 0) {
525
- throw new Error('[EXECUTION] Tool result missing toolName');
526
- }
527
- return r.toolName;
528
- }));
529
- // Add tool results to history in the order they were called
530
- // This ensures proper conversation flow and prevents any duplicate entries
531
- for (const toolCall of assistantToolCalls) {
532
- if (!toolCall.id) {
533
- throw new Error(`Tool call missing ID: ${JSON.stringify(toolCall)}`);
534
- }
535
- const toolCallName = toolCall.function?.name;
536
- if (!toolCallName || toolCallName.length === 0) {
537
- throw new Error(`[EXECUTION] Tool call "${toolCall.id}" missing function name`);
538
- }
539
- // Find the corresponding result for this tool call
540
- const result = toolSummary.results.find(r => r.executionId === toolCall.id);
541
- const error = toolSummary.errors.find(e => isExecutionError(e) && e.executionId === toolCall.id);
542
- let content;
543
- let metadata = { round: currentRound };
544
- if (result && result.success) {
545
- if (typeof result.result === 'undefined') {
546
- throw new Error('[EXECUTION] Tool result missing result payload');
547
- }
548
- content = typeof result.result === 'string'
549
- ? result.result
550
- : JSON.stringify(result.result);
551
- metadata['success'] = true;
552
- if (result.toolName) {
553
- metadata['toolName'] = result.toolName;
554
- }
555
- }
556
- else if (result && !result.success) {
557
- // Tool execution failed (result is still present to preserve deterministic ordering)
558
- if (!result.error || result.error.length === 0) {
559
- throw new Error('[EXECUTION] Tool result missing error message');
560
- }
561
- content = `Error: ${result.error}`;
562
- metadata['success'] = false;
563
- metadata['error'] = result.error;
564
- if (result.toolName) {
565
- metadata['toolName'] = result.toolName;
566
- }
567
- }
568
- else if (error) {
569
- // Tool execution failed
570
- const execError = error;
571
- const execMessage = (() => {
572
- if (execError.error?.message)
573
- return execError.error.message;
574
- if (execError.message)
575
- return execError.message;
576
- return '';
577
- })();
578
- if (!execMessage || execMessage.length === 0) {
579
- throw new Error('[EXECUTION] Tool execution error missing message');
580
- }
581
- content = `Error: ${execMessage}`;
582
- metadata['success'] = false;
583
- metadata['error'] = execMessage;
584
- if (execError.toolName) {
585
- metadata['toolName'] = execError.toolName;
586
- }
587
- }
588
- else {
589
- // No result found for this tool call
590
- throw new Error(`No execution result found for tool call ID: ${toolCall.id}`);
591
- }
592
- // Add tool result to conversation history
593
- // This will throw an error if duplicate toolCallId is detected
594
- this.logger.debug('Adding tool result to conversation', {
595
- toolCallId: toolCall.id,
596
- toolName: toolCallName,
597
- content: content.substring(0, 100),
598
- round: currentRound,
599
- currentHistoryLength: conversationSession.getMessages().length
600
- });
601
- conversationSession.addToolMessageWithId(content, toolCall.id, toolCallName, metadata);
602
- this.logger.debug('Tool result added to history', {
603
- toolCallId: toolCall.id,
604
- newHistoryLength: conversationSession.getMessages().length,
605
- round: currentRound
606
- });
607
- }
608
- // Emit tool results ready (join trigger) and then delivery to LLM
609
- const toolResultsRootId = rootId;
610
- const toolCallIds = assistantToolCalls.map(toolCall => {
611
- if (!toolCall.id || toolCall.id.length === 0) {
612
- throw new Error('[EXECUTION] Tool call missing id for tool results ready payload');
613
- }
614
- return toolCall.id;
615
- });
616
- if (toolCallIds.length === 0) {
617
- throw new Error('[EXECUTION] Tool results ready requires toolCallIds');
105
+ ...(context?.metadata ? { metadata: context.metadata } : {}),
106
+ }, this.logger);
107
+ this.validateProvider(resolved);
108
+ const roundState = {
109
+ toolsExecuted: [],
110
+ currentRound: 0,
111
+ runningAssistantCount: 0,
112
+ lastTrackedAssistantMessage: undefined,
113
+ cumulativeInputTokens: 0,
114
+ };
115
+ const initialMessages = conversationSession.getMessages();
116
+ for (const msg of initialMessages) {
117
+ if (msg.role === 'assistant') {
118
+ roundState.runningAssistantCount++;
119
+ roundState.lastTrackedAssistantMessage = msg;
618
120
  }
619
- this.emitWithContext(EXECUTION_EVENTS.TOOL_RESULTS_READY, {
620
- parameters: {
621
- toolCallIds,
622
- round: currentRound
623
- },
624
- metadata: {
625
- round: currentRound
626
- }
627
- }, () => this.buildThinkingOwnerContext(rootId, executionId, thinkingNodeId, previousThinkingNodeId), ctx => {
628
- if (!ctx.ownerType || !ctx.ownerId) {
629
- throw new Error('[EXECUTION] Missing owner context for tool results ready');
630
- }
631
- return bindWithOwnerPath(this.baseEventService, {
632
- ownerType: ctx.ownerType,
633
- ownerId: ctx.ownerId,
634
- ownerPath: ctx.ownerPath
635
- });
636
- });
637
- this.emitWithContext(EXECUTION_EVENTS.TOOL_RESULTS_TO_LLM, {
638
- parameters: {
639
- toolsExecuted: toolsExecuted.length,
640
- round: currentRound
641
- },
642
- metadata: {
643
- toolsExecuted: toolSummary.results.map(r => {
644
- if (!r.toolName || r.toolName.length === 0) {
645
- throw new Error('[EXECUTION] Tool result missing toolName');
646
- }
647
- return r.toolName;
648
- }),
649
- round: currentRound
650
- }
651
- }, () => this.buildThinkingOwnerContext(toolResultsRootId, executionId, thinkingNodeId, previousThinkingNodeId), ctx => {
652
- if (!ctx.ownerType || !ctx.ownerId) {
653
- throw new Error('[EXECUTION] Missing owner context for tool results to llm');
654
- }
655
- return bindWithOwnerPath(this.baseEventService, {
656
- ownerType: ctx.ownerType,
657
- ownerId: ctx.ownerId,
658
- ownerPath: ctx.ownerPath
659
- });
660
- });
661
- // Continue to next round - let the AI decide if more tools are needed
662
- // The AI will see the tool results and can either:
663
- // 1. Call more tools if needed
664
- // 2. Provide a final response without tool calls
665
- this.logger.info(`🔄 [ROUND-DEBUG] Round ${currentRound} COMPLETED - continuing to Round ${currentRound + 1} for agent ${fullContext.conversationId}`);
666
121
  }
667
- // Check if we hit the round limit
668
- if (currentRound >= maxRounds) {
669
- this.logger.warn('Maximum execution rounds reached', {
670
- maxRounds,
671
- conversationId
672
- });
122
+ const maxRounds = 10;
123
+ const roundDeps = {
124
+ toolExecutionService: this.toolExecutionService,
125
+ plugins: this.plugins,
126
+ logger: this.logger,
127
+ eventEmitter: this.eventEmitter,
128
+ cacheService: this.cacheService,
129
+ };
130
+ while (roundState.currentRound < maxRounds) {
131
+ roundState.currentRound++;
132
+ const shouldBreak = await executeRound(roundState, maxRounds, conversationSession, conversationId, executionId, fullContext, config, resolved, roundDeps);
133
+ if (shouldBreak)
134
+ break;
673
135
  }
674
- // Get final messages from history
675
- const finalMessages = conversationSession.getMessages();
676
- const lastAssistantMessage = finalMessages
677
- .filter(msg => msg.role === 'assistant')
678
- .pop();
679
- if (!lastAssistantMessage || typeof lastAssistantMessage.content !== 'string' || lastAssistantMessage.content.length === 0) {
680
- throw new Error('[EXECUTION] Final assistant message is required');
136
+ if (roundState.currentRound >= maxRounds) {
137
+ this.logger.warn('Maximum execution rounds reached', { maxRounds, conversationId });
681
138
  }
682
- const duration = Date.now() - startTime.getTime();
683
- const result = {
684
- response: lastAssistantMessage.content,
685
- messages: finalMessages.map(msg => {
686
- if (typeof msg.content !== 'string') {
687
- throw new Error('[EXECUTION] Message content is required');
688
- }
689
- return {
690
- role: msg.role,
691
- content: msg.content,
692
- timestamp: msg.timestamp,
693
- metadata: msg.metadata,
694
- ...(msg.role === 'assistant' && 'toolCalls' in msg ? { toolCalls: msg.toolCalls } : {}),
695
- ...(msg.role === 'tool' && 'toolCallId' in msg ? { toolCallId: msg.toolCallId } : {})
696
- };
697
- }),
698
- executionId,
699
- duration,
700
- tokensUsed: finalMessages
701
- .filter(msg => msg.metadata?.['usage'])
702
- .reduce((sum, msg) => {
703
- const usage = msg.metadata?.['usage'];
704
- if (usage && typeof usage === 'object' && 'totalTokens' in usage) {
705
- const totalTokens = Number(usage.totalTokens);
706
- if (Number.isNaN(totalTokens)) {
707
- throw new Error('[EXECUTION] totalTokens must be a number');
708
- }
709
- return sum + totalTokens;
710
- }
711
- return sum;
712
- }, 0),
713
- toolsExecuted,
714
- success: true
715
- };
716
- // Call afterRun hook on all plugins
717
- await this.callPluginHook('afterRun', { input, response: result.response, metadata: context?.metadata });
139
+ const result = this.buildFinalResult(conversationSession, executionId, startTime, roundState.toolsExecuted);
140
+ await callPluginHook(this.plugins, 'afterRun', {
141
+ input,
142
+ response: result.response,
143
+ metadata: context?.metadata,
144
+ }, this.logger);
718
145
  this.logger.debug('Execution pipeline completed successfully', {
719
146
  executionId,
720
147
  conversationId,
721
- duration,
148
+ duration: result.duration,
722
149
  tokensUsed: result.tokensUsed,
723
150
  toolsExecuted: result.toolsExecuted.length,
724
- rounds: currentRound
151
+ rounds: roundState.currentRound,
725
152
  });
726
- // Emit assistant message complete event
727
- // execution.assistant_message_complete emission is handled in the main execution loop.
728
- // Emit execution complete event
729
- const rootIdComplete = conversationId;
730
- this.emitExecution(EXECUTION_EVENTS.COMPLETE, {
153
+ this.eventEmitter.emitExecution(EXECUTION_EVENTS.COMPLETE, {
731
154
  result: {
732
155
  success: true,
733
- data: result.response.substring(0, 100) + '...'
156
+ data: result.response.substring(0, PREVIEW_LENGTH) + '...',
734
157
  },
735
158
  metadata: {
736
159
  method: 'execute',
737
160
  success: true,
738
- duration,
161
+ duration: result.duration,
739
162
  tokensUsed: result.tokensUsed,
740
- toolsExecuted: result.toolsExecuted
741
- }
742
- }, rootIdComplete, executionId);
163
+ toolsExecuted: result.toolsExecuted,
164
+ },
165
+ }, conversationId, executionId);
743
166
  return result;
744
167
  }
745
168
  catch (error) {
746
- const duration = Date.now() - startTime.getTime();
747
- // Call onError hook on all plugins
748
- await this.callPluginHook('onError', {
749
- error: error,
750
- executionContext: this.convertExecutionContextToPluginFormat(fullContext)
751
- });
752
- this.logger.error('Execution pipeline failed', {
753
- executionId,
754
- conversationId,
755
- duration,
756
- error: error instanceof Error ? error.message : String(error)
757
- });
758
- // Emit execution error event
759
- this.emitExecution(EXECUTION_EVENTS.ERROR, {
760
- error: error instanceof Error ? error.message : String(error),
761
- metadata: {
762
- method: 'execute',
763
- success: false,
764
- duration,
765
- // Identity/hierarchy fields are derived from EventContext.ownerPath.
766
- }
767
- }, conversationId, executionId);
169
+ await this.handleExecutionError(error, fullContext, startTime, conversationId, executionId);
768
170
  throw error;
769
171
  }
770
172
  finally {
771
- this.resetOwnerPathBases();
173
+ this.eventEmitter.resetOwnerPathBases();
772
174
  }
773
175
  }
774
- /**
775
- * Execute with streaming response
776
- */
176
+ /** Execute with streaming response */
777
177
  async *executeStream(input, messages, config, context) {
778
- this.logger.debug('ExecutionService.executeStream called');
779
- const executionId = this.generateExecutionId();
780
- const startTime = Date.now();
781
- if (!context?.conversationId || context.conversationId.length === 0) {
782
- throw new Error('[EXECUTION] conversationId is required for streaming');
783
- }
784
- const streamingConversationId = this.requireConversationId(context, 'streaming');
785
- this.prepareOwnerPathBases(streamingConversationId);
786
- try {
787
- // Create conversation session for this execution
788
- const conversationSession = this.conversationHistory.getConversationSession(context.conversationId);
789
- // Add user input to conversation
790
- if (input) {
791
- conversationSession.addUserMessage(input, { executionId });
792
- }
793
- // Call beforeRun hook on all plugins
794
- await this.callPluginHook('beforeRun', {
795
- input,
796
- ...(context?.metadata ? { metadata: context.metadata } : {})
797
- });
798
- // Get current provider info
799
- const currentInfo = this.aiProviders.getCurrentProvider();
800
- if (!currentInfo) {
801
- throw new Error('No AI provider configured');
802
- }
803
- // Get actual provider instance
804
- const provider = this.aiProviders.getProvider(currentInfo.provider);
805
- if (!provider) {
806
- throw new Error(`AI provider '${currentInfo.provider}' not found`);
807
- }
808
- // Ensure provider has chatStream method (streaming is optional on IAIProvider)
809
- if (typeof provider.chatStream !== 'function') {
810
- throw new Error('Provider must have chatStream method to support streaming execution');
811
- }
812
- this.logger.debug('ExecutionService calling provider.chatStream');
813
- // Get conversation messages for provider
814
- const conversationMessages = conversationSession.getMessages();
815
- // Create chat options
816
- const configToolsLength = Array.isArray(config.tools) ? config.tools.length : undefined;
817
- this.logger.debug('🔍 [EXECUTION-SERVICE] config.tools:', { length: configToolsLength });
818
- const toolSchemas = this.tools.getTools();
819
- const toolSchemasLength = Array.isArray(toolSchemas) ? toolSchemas.length : undefined;
820
- this.logger.debug('🔍 [EXECUTION-SERVICE] this.tools.getTools():', { length: toolSchemasLength });
821
- this.logger.debug('🔍 [EXECUTION-SERVICE] config.tools exists:', { exists: !!config.tools });
822
- this.logger.debug('🔍 [EXECUTION-SERVICE] config.tools.length > 0:', { hasTools: config.tools && config.tools.length > 0 });
823
- const chatOptions = {
824
- model: config.defaultModel.model,
825
- ...(config.tools && config.tools.length > 0 && { tools: this.tools.getTools() })
826
- };
827
- this.logger.debug('🔍 [EXECUTION-SERVICE] Final chatOptions has tools:', { hasTools: !!chatOptions.tools });
828
- const chatOptionsToolsLength = Array.isArray(chatOptions.tools) ? chatOptions.tools.length : undefined;
829
- this.logger.debug('🔍 [EXECUTION-SERVICE] Final chatOptions.tools length:', { length: chatOptionsToolsLength });
830
- const chatStream = provider.chatStream;
831
- if (!chatStream) {
832
- throw new Error('Provider does not support streaming');
833
- }
834
- const stream = chatStream.call(provider, conversationMessages, chatOptions);
835
- let fullResponse = '';
836
- let toolCalls = [];
837
- let currentToolCallIndex = -1; // Index of the currently active tool call during streaming
838
- // Collect streaming chunks and tool calls
839
- for await (const chunk of stream) {
840
- if (chunk.content) {
841
- fullResponse += chunk.content;
842
- yield { chunk: chunk.content, isComplete: false };
843
- }
844
- // Collect tool calls from streaming chunks (type assertion for AssistantMessage)
845
- if (chunk.role === 'assistant') {
846
- const assistantChunk = chunk;
847
- if (Array.isArray(assistantChunk.toolCalls) && assistantChunk.toolCalls.length > 0) {
848
- // Manage tool call state while streaming
849
- for (const chunkToolCall of assistantChunk.toolCalls) {
850
- if (chunkToolCall.id && chunkToolCall.id !== '') {
851
- // ✅ Tool call id present: start a new tool call
852
- if (!chunkToolCall.type || chunkToolCall.type.length === 0) {
853
- throw new Error(`[EXECUTION] Tool call "${chunkToolCall.id}" missing type in stream`);
854
- }
855
- if (!chunkToolCall.function?.name || chunkToolCall.function.name.length === 0) {
856
- throw new Error(`[EXECUTION] Tool call "${chunkToolCall.id}" missing function name in stream`);
857
- }
858
- if (typeof chunkToolCall.function.arguments !== 'string') {
859
- throw new Error(`[EXECUTION] Tool call "${chunkToolCall.id}" missing arguments in stream`);
860
- }
861
- currentToolCallIndex = toolCalls.length;
862
- toolCalls.push({
863
- id: chunkToolCall.id,
864
- type: chunkToolCall.type,
865
- function: {
866
- name: chunkToolCall.function.name,
867
- arguments: chunkToolCall.function.arguments
868
- }
869
- });
870
- this.logger.debug(`🆕 [TOOL-STREAM] New tool call started: ${chunkToolCall.id} (${chunkToolCall.function?.name})`);
871
- }
872
- else if (currentToolCallIndex >= 0) {
873
- // ✅ Tool call id missing: append fragments to the current tool call
874
- const hasNameFragment = typeof chunkToolCall.function?.name === 'string' && chunkToolCall.function.name.length > 0;
875
- const hasArgumentsFragment = typeof chunkToolCall.function?.arguments === 'string' && chunkToolCall.function.arguments.length > 0;
876
- if (!hasNameFragment && !hasArgumentsFragment) {
877
- throw new Error(`[EXECUTION] Tool call fragment missing name/arguments for ${toolCalls[currentToolCallIndex].id}`);
878
- }
879
- if (hasNameFragment) {
880
- toolCalls[currentToolCallIndex].function.name += chunkToolCall.function.name;
881
- }
882
- if (hasArgumentsFragment) {
883
- toolCalls[currentToolCallIndex].function.arguments += chunkToolCall.function.arguments;
884
- }
885
- const fragmentPreview = hasArgumentsFragment
886
- ? chunkToolCall.function.arguments
887
- : chunkToolCall.function.name;
888
- this.logger.debug(`📝 [TOOL-STREAM] Adding fragment to tool ${toolCalls[currentToolCallIndex].id}: "${fragmentPreview}"`);
889
- }
890
- }
891
- }
892
- }
893
- }
894
- this.logger.debug('🔥 [EXECUTION-SERVICE-STREAM] Stream completed, toolCalls detected:', { count: toolCalls.length });
895
- if (typeof fullResponse !== 'string') {
896
- throw new Error('[EXECUTION] Streaming response content is required');
897
- }
898
- conversationSession.addAssistantMessage(fullResponse, toolCalls, { executionId });
899
- // Execute tools if detected
900
- if (toolCalls.length > 0) {
901
- this.logger.debug('🔥 [EXECUTION-SERVICE-STREAM] Executing tools:', { tools: toolCalls.map(tc => tc.function.name) });
902
- // Execute tools with hierarchical context
903
- const streamingRootId = streamingConversationId;
904
- // Generate thinking node ID for streaming mode (direct provision)
905
- const streamingThinkingNodeId = `thinking_${streamingRootId}_${Date.now()}_${executionId}`;
906
- const streamingOwnerPathBase = [...this.buildExecutionOwnerContext(streamingRootId, executionId).ownerPath, { type: 'thinking', id: streamingThinkingNodeId }];
907
- const toolRequests = this.toolExecutionService.createExecutionRequestsWithContext(toolCalls, {
908
- ownerPathBase: streamingOwnerPathBase
909
- });
910
- const toolContext = {
911
- requests: toolRequests,
912
- mode: 'parallel',
913
- maxConcurrency: 5,
914
- continueOnError: true
915
- };
916
- const toolSummary = await this.toolExecutionService.executeTools(toolContext);
917
- // Add tool results to conversation in the order they were called
918
- for (const toolCall of toolCalls) {
919
- if (!toolCall.id) {
920
- throw new Error('[EXECUTION] Tool call missing id in streaming mode');
921
- }
922
- if (!toolCall.function?.name || toolCall.function.name.length === 0) {
923
- throw new Error(`[EXECUTION] Tool call "${toolCall.id}" missing function name in streaming mode`);
924
- }
925
- // Find the corresponding result for this tool call
926
- const result = toolSummary.results.find(r => r.executionId === toolCall.id);
927
- const error = toolSummary.errors.find(e => isExecutionError(e) && e.executionId === toolCall.id);
928
- let content;
929
- let metadata = { executionId };
930
- if (result && result.success) {
931
- if (typeof result.result === 'undefined') {
932
- throw new Error('[EXECUTION] Tool result missing result payload in streaming mode');
933
- }
934
- content = typeof result.result === 'string'
935
- ? result.result
936
- : JSON.stringify(result.result);
937
- metadata['success'] = true;
938
- if (result.toolName) {
939
- metadata['toolName'] = result.toolName;
940
- }
941
- // Yield tool result as streaming chunk
942
- yield { chunk: `\n[Tool: ${toolCall.function.name} executed successfully]`, isComplete: false };
943
- }
944
- else if (error) {
945
- // Tool execution failed
946
- const execError = error;
947
- const execMessage = (() => {
948
- if (execError.error?.message)
949
- return execError.error.message;
950
- if (execError.message)
951
- return execError.message;
952
- return '';
953
- })();
954
- if (!execMessage || execMessage.length === 0) {
955
- throw new Error('[EXECUTION] Tool execution error missing message in streaming mode');
956
- }
957
- content = `Error: ${execMessage}`;
958
- metadata['success'] = false;
959
- metadata['error'] = execMessage;
960
- if (execError.toolName) {
961
- metadata['toolName'] = execError.toolName;
962
- }
963
- // Yield error as streaming chunk
964
- yield { chunk: `\n[Tool: ${toolCall.function.name} failed: ${execMessage}]`, isComplete: false };
965
- }
966
- else {
967
- throw new Error(`[EXECUTION] Missing tool result for tool call "${toolCall.id}" in streaming mode`);
968
- }
969
- // Add tool result to conversation history
970
- conversationSession.addToolMessageWithId(content, toolCall.id, toolCall.function.name, metadata);
971
- }
972
- // After all tool responses are emitted and recorded, trigger aggregation join
973
- const streamingRoot = streamingConversationId;
974
- const streamingToolCallIds = toolCalls.map(toolCall => {
975
- if (!toolCall.id || toolCall.id.length === 0) {
976
- throw new Error('[EXECUTION] Tool call missing id for streaming tool results ready payload');
977
- }
978
- return toolCall.id;
979
- });
980
- if (streamingToolCallIds.length === 0) {
981
- throw new Error('[EXECUTION] Tool results ready requires toolCallIds in streaming mode');
982
- }
983
- this.emitExecution(EXECUTION_EVENTS.TOOL_RESULTS_READY, {
984
- parameters: {
985
- toolCallIds: streamingToolCallIds,
986
- round: 1
987
- },
988
- metadata: {
989
- toolsExecuted: toolSummary.results.map(r => {
990
- if (!r.toolName || r.toolName.length === 0) {
991
- throw new Error('[EXECUTION] Tool result missing toolName');
992
- }
993
- return r.toolName;
994
- }),
995
- round: 1,
996
- }
997
- }, streamingRoot, executionId);
998
- }
999
- // Call afterRun hook
1000
- await this.callPluginHook('afterRun', {
1001
- input,
1002
- response: fullResponse,
1003
- ...(context?.metadata ? { metadata: context.metadata } : {})
1004
- });
1005
- yield { chunk: '', isComplete: true };
1006
- }
1007
- catch (error) {
1008
- this.logger.error('ExecutionService streaming execution failed', {
1009
- error: error instanceof Error ? error.message : String(error),
1010
- executionTime: Date.now() - startTime
1011
- });
1012
- // Call error hook
1013
- await this.callPluginHook('onError', {
1014
- input,
1015
- error: error instanceof Error ? error : new Error(String(error)),
1016
- ...(context?.metadata ? { metadata: context.metadata } : {})
1017
- });
1018
- throw error;
1019
- }
1020
- finally {
1021
- this.resetOwnerPathBases();
1022
- }
1023
- }
1024
- /**
1025
- * Generate a unique execution ID
1026
- */
1027
- generateExecutionId() {
1028
- return `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
178
+ yield* executeStreamFn(input, messages, config, context, {
179
+ aiProviders: this.aiProviders,
180
+ tools: this.tools,
181
+ conversationHistory: this.conversationHistory,
182
+ toolExecutionService: this.toolExecutionService,
183
+ plugins: this.plugins,
184
+ logger: this.logger,
185
+ eventEmitter: this.eventEmitter,
186
+ generateExecutionId: () => this.generateExecutionId(),
187
+ });
1029
188
  }
1030
- /**
1031
- * Get execution statistics from plugins
1032
- */
189
+ /** Get execution statistics from plugins */
1033
190
  async getStats() {
1034
- const stats = {
191
+ return {
1035
192
  pluginCount: this.plugins.length,
1036
- pluginNames: this.plugins.map(p => p.name),
1037
- historyStats: this.conversationHistory.getStats()
193
+ pluginNames: this.plugins.map((p) => p.name),
194
+ historyStats: this.conversationHistory.getStats(),
1038
195
  };
1039
- // Note: Plugin-specific stats are not included here to avoid type conflicts
1040
- // Plugins can implement their own getStats() method returning their specific stat types
1041
- return stats;
1042
196
  }
1043
- /**
1044
- * Clear all plugins
1045
- */
197
+ /** Clear all plugins */
1046
198
  clearPlugins() {
1047
199
  this.plugins = [];
1048
200
  this.logger.debug('All plugins cleared');
1049
201
  }
1050
- /**
1051
- * Call a hook method on all plugins that implement it
1052
- * Handles different hook signatures properly
1053
- */
1054
- async callPluginHook(hookName, context) {
1055
- for (const plugin of this.plugins) {
1056
- try {
1057
- // Use type assertion to access the hook methods
1058
- const pluginWithHooks = plugin;
1059
- // Call the appropriate hook method with correct parameters
1060
- switch (hookName) {
1061
- case 'beforeRun':
1062
- if (pluginWithHooks.beforeRun && context.input) {
1063
- await pluginWithHooks.beforeRun(context.input, context.metadata);
1064
- }
1065
- break;
1066
- case 'afterRun':
1067
- if (pluginWithHooks.afterRun && context.input && context.response) {
1068
- await pluginWithHooks.afterRun(context.input, context.response, context.metadata);
1069
- }
1070
- break;
1071
- case 'beforeProviderCall':
1072
- if (pluginWithHooks.beforeProviderCall && context.messages) {
1073
- await pluginWithHooks.beforeProviderCall(context.messages);
1074
- }
1075
- break;
1076
- case 'afterProviderCall':
1077
- if (pluginWithHooks.afterProviderCall && context.messages && context.responseMessage) {
1078
- await pluginWithHooks.afterProviderCall(context.messages, context.responseMessage);
1079
- }
1080
- break;
1081
- case 'onError':
1082
- if (pluginWithHooks.onError && context.error) {
1083
- const errorContext = {
1084
- action: `${EXECUTION_EVENT_PREFIX}.${EXECUTION_EVENTS.ERROR}`,
1085
- metadata: {}
1086
- };
1087
- const executionIdValue = context.executionContext?.['executionId'];
1088
- if (typeof executionIdValue === 'string' && executionIdValue.length > 0) {
1089
- errorContext.executionId = executionIdValue;
1090
- }
1091
- const sessionIdValue = context.executionContext?.['sessionId'];
1092
- if (typeof sessionIdValue === 'string' && sessionIdValue.length > 0) {
1093
- errorContext.sessionId = sessionIdValue;
1094
- }
1095
- const userIdValue = context.executionContext?.['userId'];
1096
- if (typeof userIdValue === 'string' && userIdValue.length > 0) {
1097
- errorContext.userId = userIdValue;
1098
- }
1099
- await pluginWithHooks.onError(context.error, errorContext);
1100
- }
1101
- break;
1102
- }
1103
- }
1104
- catch (error) {
1105
- this.logger.warn('Plugin hook failed', {
1106
- pluginName: plugin.name,
1107
- hookName,
1108
- error: error instanceof Error ? error.message : String(error)
1109
- });
1110
- }
1111
- }
1112
- }
1113
- ensureToolEventService(ownerId, ownerPath) {
1114
- if (isDefaultEventService(this.baseEventService)) {
1115
- return this.baseEventService;
1116
- }
1117
- if (!ownerId) {
1118
- throw new Error('[EVENT-SERVICE] Missing ownerId for tool event context');
1119
- }
1120
- if (!ownerPath || ownerPath.length === 0) {
1121
- throw new Error('[EVENT-SERVICE] Missing ownerPath for tool event context');
1122
- }
1123
- if (this.toolEventServices.has(ownerId)) {
1124
- return this.toolEventServices.get(ownerId);
202
+ // --- Private helpers ---
203
+ resolveProviderAndTools(config) {
204
+ const currentInfo = this.aiProviders.getCurrentProvider();
205
+ const provider = currentInfo ? this.aiProviders.getProvider(currentInfo.provider) : null;
206
+ if (!currentInfo || !currentInfo.provider || !provider) {
207
+ throw new Error('[EXECUTION] Provider is required');
1125
208
  }
1126
- const scoped = bindWithOwnerPath(this.baseEventService, {
1127
- ownerType: TOOL_EVENT_PREFIX,
1128
- ownerId,
1129
- ownerPath: ownerPath.map(segment => ({ ...segment })),
209
+ const availableTools = this.tools.getTools();
210
+ const aiProviderInfo = {
211
+ providerName: currentInfo.provider,
212
+ model: config.defaultModel.model,
213
+ temperature: config.defaultModel.temperature,
214
+ maxTokens: config.defaultModel.maxTokens,
215
+ };
216
+ const toolsInfo = availableTools.map((tool) => {
217
+ const paramSchema = tool.parameters;
218
+ const props = paramSchema?.properties;
219
+ if (!tool.description || tool.description.length === 0) {
220
+ throw new Error(`[EXECUTION] Tool "${tool.name}" is missing description`);
221
+ }
222
+ return {
223
+ name: tool.name,
224
+ description: tool.description,
225
+ parameters: props && typeof props === 'object' ? Object.keys(props) : [],
226
+ };
1130
227
  });
1131
- this.toolEventServices.set(ownerId, scoped);
1132
- return scoped;
1133
- }
1134
- prepareOwnerPathBases(conversationId) {
1135
- this.toolEventServices.clear();
1136
- const ownerPath = [...this.ownerPathBase, { type: 'agent', id: conversationId }];
1137
- this.agentOwnerPathBase = ownerPath;
1138
- }
1139
- resetOwnerPathBases() {
1140
- this.toolEventServices.clear();
1141
- this.agentOwnerPathBase = [];
1142
- }
1143
- buildBaseOwnerPath(executionContext) {
1144
- if (!executionContext?.ownerPath?.length) {
1145
- return [];
1146
- }
1147
- return executionContext.ownerPath.map(segment => ({ ...segment }));
228
+ return { provider, currentInfo, aiProviderInfo, toolsInfo, availableTools };
1148
229
  }
1149
- buildExecutionOwnerContext(rootId, executionId) {
1150
- if (!rootId || rootId.length === 0) {
1151
- throw new Error('[EXECUTION] Missing rootId for execution owner context');
230
+ validateProvider(resolved) {
231
+ if (!resolved.currentInfo)
232
+ throw new Error('No AI provider configured');
233
+ if (!resolved.provider)
234
+ throw new Error(`AI provider '${resolved.currentInfo.provider}' not found`);
235
+ if (typeof resolved.provider.chat !== 'function') {
236
+ throw new Error('Provider must have chat method to support execution');
1152
237
  }
1153
- if (!executionId || executionId.length === 0) {
1154
- throw new Error('[EXECUTION] Missing executionId for execution owner context');
1155
- }
1156
- const basePath = this.agentOwnerPathBase.length ? this.agentOwnerPathBase : this.ownerPathBase;
1157
- const path = [...basePath];
1158
- if (rootId && !path.some(segment => segment.type === 'agent' && segment.id === rootId)) {
1159
- path.push({ type: 'agent', id: rootId });
1160
- }
1161
- path.push({ type: 'execution', id: executionId });
1162
- return {
1163
- ownerType: EXECUTION_EVENT_PREFIX,
1164
- ownerId: executionId,
1165
- ownerPath: path
1166
- };
1167
238
  }
1168
- buildThinkingOwnerContext(rootId, executionId, thinkingNodeId, previousThinkingNodeId) {
1169
- if (!thinkingNodeId || thinkingNodeId.length === 0) {
1170
- throw new Error('[EXECUTION] Missing thinkingNodeId for thinking owner context');
1171
- }
1172
- const base = this.buildExecutionOwnerContext(rootId, executionId).ownerPath;
1173
- const path = [...base];
1174
- if (previousThinkingNodeId) {
1175
- path.push({ type: 'thinking', id: previousThinkingNodeId });
1176
- path.push({ type: 'tool_result', id: `tool_result_${previousThinkingNodeId}` });
239
+ initializeConversationSession(conversationId, messages, config, executionId) {
240
+ const session = this.conversationHistory.getConversationSession(conversationId);
241
+ if (session.getMessageCount() === 0 && messages.length > 0) {
242
+ for (const msg of messages) {
243
+ if (msg.role === 'user') {
244
+ session.addUserMessage(msg.content, msg.metadata, msg.parts);
245
+ }
246
+ else if (msg.role === 'assistant') {
247
+ session.addAssistantMessage(msg.content, msg.toolCalls, msg.metadata, msg.parts);
248
+ }
249
+ else if (msg.role === 'system') {
250
+ session.addSystemMessage(msg.content, msg.metadata, msg.parts);
251
+ }
252
+ else if (msg.role === 'tool') {
253
+ const toolName = msg.metadata?.['toolName'];
254
+ if (typeof toolName !== 'string' || toolName.length === 0) {
255
+ throw new Error('[EXECUTION] Tool message missing toolName metadata');
256
+ }
257
+ session.addToolMessageWithId(msg.content, msg.toolCallId, toolName, msg.metadata, msg.parts);
258
+ }
259
+ }
1177
260
  }
1178
- path.push({ type: 'thinking', id: thinkingNodeId });
1179
- return {
1180
- ownerType: EXECUTION_EVENT_PREFIX,
1181
- ownerId: executionId,
1182
- ownerPath: path
1183
- };
1184
- }
1185
- buildToolOwnerContext(rootId, executionId, toolCallId) {
1186
- // Tool calls are always children of a specific thinking phase (fork point).
1187
- // The caller must provide an ownerPathBase that already includes `{ type: 'thinking', id }`.
1188
- if (!toolCallId || toolCallId.length === 0) {
1189
- throw new Error('[EXECUTION] Missing toolCallId for tool owner context');
261
+ if (config.systemMessage) {
262
+ session.addSystemMessage(config.systemMessage, { executionId });
1190
263
  }
1191
- const base = this.buildExecutionOwnerContext(rootId, executionId).ownerPath;
1192
- const path = [...base, { type: 'tool', id: toolCallId }];
1193
- return {
1194
- ownerType: TOOL_EVENT_PREFIX,
1195
- ownerId: toolCallId,
1196
- ownerPath: path
1197
- };
264
+ return session;
1198
265
  }
1199
- buildResponseOwnerContext(rootId, executionId, thinkingNodeId, previousThinkingNodeId) {
1200
- const thinkingPath = this.buildThinkingOwnerContext(rootId, executionId, thinkingNodeId, previousThinkingNodeId).ownerPath;
1201
- const responseNodeId = `response_${thinkingNodeId}`;
1202
- const path = [...thinkingPath, { type: 'response', id: responseNodeId }];
266
+ buildFinalResult(conversationSession, executionId, startTime, toolsExecuted) {
267
+ const finalMessages = conversationSession.getMessages();
268
+ const lastAssistantMessage = finalMessages.filter((msg) => msg.role === 'assistant').pop();
269
+ const hasValidResponse = !!lastAssistantMessage &&
270
+ typeof lastAssistantMessage.content === 'string' &&
271
+ lastAssistantMessage.content.length > 0;
272
+ const response = hasValidResponse
273
+ ? lastAssistantMessage.content
274
+ : '(execution interrupted: no final assistant response — possible context overflow or max turn limit)';
275
+ const duration = Date.now() - startTime.getTime();
1203
276
  return {
1204
- ownerType: EXECUTION_EVENT_PREFIX,
1205
- ownerId: executionId,
1206
- ownerPath: path
277
+ response,
278
+ messages: finalMessages.map((msg) => {
279
+ if (typeof msg.content !== 'string')
280
+ throw new Error('[EXECUTION] Message content is required');
281
+ return {
282
+ role: msg.role,
283
+ content: msg.content,
284
+ timestamp: msg.timestamp,
285
+ metadata: msg.metadata,
286
+ ...(msg.role === 'assistant' && 'toolCalls' in msg ? { toolCalls: msg.toolCalls } : {}),
287
+ ...(msg.role === 'tool' && 'toolCallId' in msg ? { toolCallId: msg.toolCallId } : {}),
288
+ };
289
+ }),
290
+ executionId,
291
+ duration,
292
+ tokensUsed: finalMessages
293
+ .filter((msg) => msg.metadata?.['usage'])
294
+ .reduce((sum, msg) => {
295
+ const usage = msg.metadata?.['usage'];
296
+ if (usage && typeof usage === 'object' && 'totalTokens' in usage) {
297
+ const totalTokens = Number(usage.totalTokens);
298
+ if (Number.isNaN(totalTokens))
299
+ throw new Error('[EXECUTION] totalTokens must be a number');
300
+ return sum + totalTokens;
301
+ }
302
+ return sum;
303
+ }, 0),
304
+ toolsExecuted,
305
+ success: hasValidResponse,
1207
306
  };
1208
307
  }
1209
- emitExecution(eventType, data, rootId, executionId) {
1210
- this.emitWithContext(eventType, data, () => this.buildExecutionOwnerContext(rootId, executionId), context => {
1211
- if (!context.ownerType || !context.ownerId) {
1212
- throw new Error('[EXECUTION] Missing owner context for execution event');
1213
- }
1214
- return bindWithOwnerPath(this.baseEventService, {
1215
- ownerType: context.ownerType,
1216
- ownerId: context.ownerId,
1217
- ownerPath: context.ownerPath
1218
- });
308
+ async handleExecutionError(error, fullContext, startTime, conversationId, executionId) {
309
+ const duration = Date.now() - startTime.getTime();
310
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
311
+ await callPluginHook(this.plugins, 'onError', {
312
+ error: normalizedError,
313
+ executionContext: this.convertExecutionContextToPluginFormat(fullContext),
314
+ }, this.logger);
315
+ this.logger.error('Execution pipeline failed', {
316
+ executionId,
317
+ conversationId,
318
+ duration,
319
+ error: error instanceof Error ? error.message : String(error),
1219
320
  });
321
+ this.eventEmitter.emitExecution(EXECUTION_EVENTS.ERROR, {
322
+ error: error instanceof Error ? error.message : String(error),
323
+ metadata: { method: 'execute', success: false, duration },
324
+ }, conversationId, executionId);
1220
325
  }
1221
- emitTool(eventType, data, rootId, executionId, toolCallId) {
1222
- this.emitWithContext(eventType, data, () => this.buildToolOwnerContext(rootId, executionId, toolCallId), context => this.ensureToolEventService(context.ownerId, context.ownerPath));
1223
- }
1224
- emitWithContext(eventType, data, buildContext, resolveService) {
1225
- if (isDefaultEventService(this.baseEventService)) {
1226
- return;
1227
- }
1228
- const context = buildContext();
1229
- const service = resolveService(context);
1230
- const payload = {
1231
- timestamp: new Date(),
1232
- ...data,
1233
- };
1234
- service.emit(eventType, payload, context);
326
+ generateExecutionId() {
327
+ return `exec_${Date.now()}_${Math.random().toString(ID_RADIX).substr(2, ID_RANDOM_LENGTH)}`;
1235
328
  }
1236
329
  requireConversationId(context, label) {
1237
330
  if (!context?.conversationId || context.conversationId.length === 0) {
@@ -1239,23 +332,18 @@ export class ExecutionService {
1239
332
  }
1240
333
  return context.conversationId;
1241
334
  }
1242
- /**
1243
- * Convert IExecutionContext to IPluginContext compatible format
1244
- */
1245
335
  convertExecutionContextToPluginFormat(context) {
1246
336
  const conversationId = this.requireConversationId(context, 'plugin-context');
1247
337
  const payload = {
1248
- conversationId: conversationId,
338
+ conversationId,
1249
339
  executionId: context.executionId,
1250
340
  startTime: context.startTime.toISOString(),
1251
- messageCount: context.messages.length
341
+ messageCount: context.messages.length,
1252
342
  };
1253
- if (context.sessionId) {
343
+ if (context.sessionId)
1254
344
  payload.sessionId = context.sessionId;
1255
- }
1256
- if (context.userId) {
345
+ if (context.userId)
1257
346
  payload.userId = context.userId;
1258
- }
1259
347
  return payload;
1260
348
  }
1261
349
  }