@ai-setting/roy-agent-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/dist/index.js +99145 -0
  2. package/package.json +114 -0
  3. package/src/config/config-component.test.ts +627 -0
  4. package/src/config/config-component.ts +906 -0
  5. package/src/config/config-parser.test.ts +319 -0
  6. package/src/config/config-parser.ts +203 -0
  7. package/src/config/decentralized-config.test.ts +740 -0
  8. package/src/config/env-key.ts +210 -0
  9. package/src/config/env-source.test.ts +252 -0
  10. package/src/config/env-source.ts +301 -0
  11. package/src/config/file-source.test.ts +357 -0
  12. package/src/config/file-source.ts +421 -0
  13. package/src/config/index.ts +24 -0
  14. package/src/config/protocol-resolver.test.ts +217 -0
  15. package/src/config/protocol-resolver.ts +228 -0
  16. package/src/env/agent/agent-component.abort.test.ts +511 -0
  17. package/src/env/agent/agent-component.record-session.test.ts +349 -0
  18. package/src/env/agent/agent-component.test.ts +1389 -0
  19. package/src/env/agent/agent-component.tool-error.test.ts +327 -0
  20. package/src/env/agent/agent-component.ts +1711 -0
  21. package/src/env/agent/agent-config-registration.test.ts +226 -0
  22. package/src/env/agent/agent-config-registration.ts +46 -0
  23. package/src/env/agent/agent-reminder-plugin.integration.test.ts +243 -0
  24. package/src/env/agent/index.ts +10 -0
  25. package/src/env/agent/summary-agent.parse-hint.test.ts +360 -0
  26. package/src/env/agent/summary-agent.ts +508 -0
  27. package/src/env/agent/types.ts +536 -0
  28. package/src/env/commands/commands-component.test.ts +364 -0
  29. package/src/env/commands/commands-component.ts +604 -0
  30. package/src/env/commands/commands-config-registration.test.ts +198 -0
  31. package/src/env/commands/commands-config-registration.ts +38 -0
  32. package/src/env/commands/index.ts +21 -0
  33. package/src/env/commands/parser.test.ts +203 -0
  34. package/src/env/commands/parser.ts +115 -0
  35. package/src/env/commands/types.ts +184 -0
  36. package/src/env/commands-prompt-integration.test.ts +243 -0
  37. package/src/env/component-env.test.ts +119 -0
  38. package/src/env/component.ts +335 -0
  39. package/src/env/constants.test.ts +72 -0
  40. package/src/env/constants.ts +123 -0
  41. package/src/env/debug/debug-component.test.ts +114 -0
  42. package/src/env/debug/debug-component.ts +547 -0
  43. package/src/env/debug/formatters/index.ts +9 -0
  44. package/src/env/debug/formatters/repl-formatter.test.ts +139 -0
  45. package/src/env/debug/formatters/repl-formatter.ts +358 -0
  46. package/src/env/debug/formatters/trace-formatter.test.ts +119 -0
  47. package/src/env/debug/formatters/trace-formatter.ts +191 -0
  48. package/src/env/debug/formatters/tree-formatter.test.ts +107 -0
  49. package/src/env/debug/formatters/tree-formatter.ts +325 -0
  50. package/src/env/debug/index.ts +38 -0
  51. package/src/env/debug/parser/regex-parser.test.ts +201 -0
  52. package/src/env/debug/parser/regex-parser.ts +196 -0
  53. package/src/env/debug/parser/span-builder.test.ts +241 -0
  54. package/src/env/debug/parser/span-builder.ts +386 -0
  55. package/src/env/debug/reader/log-reader.test.ts +170 -0
  56. package/src/env/debug/reader/log-reader.ts +186 -0
  57. package/src/env/debug/reader/span-db-reader.test.ts +118 -0
  58. package/src/env/debug/reader/span-db-reader.ts +201 -0
  59. package/src/env/debug/types.test.ts +187 -0
  60. package/src/env/debug/types.ts +171 -0
  61. package/src/env/environment-init.test.ts +183 -0
  62. package/src/env/environment-lifecycle.test.ts +516 -0
  63. package/src/env/environment-service.test.ts +332 -0
  64. package/src/env/environment.handle-query.test.ts +96 -0
  65. package/src/env/environment.test.ts +232 -0
  66. package/src/env/environment.ts +708 -0
  67. package/src/env/errors.test.ts +165 -0
  68. package/src/env/errors.ts +157 -0
  69. package/src/env/event-source/event-source-agent-handler.test.ts +193 -0
  70. package/src/env/event-source/event-source-agent-handler.ts +111 -0
  71. package/src/env/event-source/event-source-component.process-cleanup.test.ts +236 -0
  72. package/src/env/event-source/event-source-component.stop.test.ts +346 -0
  73. package/src/env/event-source/event-source-component.test.ts +1207 -0
  74. package/src/env/event-source/event-source-component.ts +1379 -0
  75. package/src/env/event-source/event-source-config-registration.test.ts +242 -0
  76. package/src/env/event-source/event-source-config-registration.ts +37 -0
  77. package/src/env/event-source/event-source-integration.test.ts +320 -0
  78. package/src/env/event-source/event-source-platform.test.ts +630 -0
  79. package/src/env/event-source/types.ts +298 -0
  80. package/src/env/hook/global-hook-manager.ts +162 -0
  81. package/src/env/hook/hook-manager.test.ts +374 -0
  82. package/src/env/hook/hook-manager.ts +309 -0
  83. package/src/env/hook/index.ts +38 -0
  84. package/src/env/hook/types.ts +138 -0
  85. package/src/env/index.ts +144 -0
  86. package/src/env/interface.ts +203 -0
  87. package/src/env/llm/hooks.test.ts +293 -0
  88. package/src/env/llm/hooks.ts +316 -0
  89. package/src/env/llm/index.ts +61 -0
  90. package/src/env/llm/invoke-threshold-check.test.ts +88 -0
  91. package/src/env/llm/invoke-timeout.test.ts +54 -0
  92. package/src/env/llm/invoke.test.ts +71 -0
  93. package/src/env/llm/invoke.ts +1039 -0
  94. package/src/env/llm/llm-config.test.ts +523 -0
  95. package/src/env/llm/llm.test.ts +233 -0
  96. package/src/env/llm/llm.ts +568 -0
  97. package/src/env/llm/provider.test.ts +182 -0
  98. package/src/env/llm/provider.ts +108 -0
  99. package/src/env/llm/transform.test.ts +251 -0
  100. package/src/env/llm/transform.ts +286 -0
  101. package/src/env/llm/types.test.ts +580 -0
  102. package/src/env/llm/types.ts +424 -0
  103. package/src/env/log-trace/decorator-otel.test.ts +182 -0
  104. package/src/env/log-trace/decorator.ts +230 -0
  105. package/src/env/log-trace/index.ts +79 -0
  106. package/src/env/log-trace/log-trace-component.test.ts +242 -0
  107. package/src/env/log-trace/log-trace-component.ts +497 -0
  108. package/src/env/log-trace/log-trace-config-registration.test.ts +348 -0
  109. package/src/env/log-trace/log-trace-config-registration.ts +45 -0
  110. package/src/env/log-trace/logger.test.ts +149 -0
  111. package/src/env/log-trace/logger.ts +522 -0
  112. package/src/env/log-trace/opentelemetry/cli-propagation.test.ts +147 -0
  113. package/src/env/log-trace/opentelemetry/cli-propagation.ts +194 -0
  114. package/src/env/log-trace/opentelemetry/integration.test.ts +668 -0
  115. package/src/env/log-trace/opentelemetry/mod.ts +25 -0
  116. package/src/env/log-trace/opentelemetry/propagation-env.test.ts +181 -0
  117. package/src/env/log-trace/opentelemetry/propagation-env.ts +136 -0
  118. package/src/env/log-trace/opentelemetry/propagation.test.ts +259 -0
  119. package/src/env/log-trace/opentelemetry/propagation.ts +215 -0
  120. package/src/env/log-trace/opentelemetry/tracer-provider-context.test.ts +166 -0
  121. package/src/env/log-trace/opentelemetry/tracer-provider.test.ts +379 -0
  122. package/src/env/log-trace/opentelemetry/tracer-provider.ts +612 -0
  123. package/src/env/log-trace/span-storage.test.ts +145 -0
  124. package/src/env/log-trace/span-storage.ts +230 -0
  125. package/src/env/log-trace/trace-context.test.ts +187 -0
  126. package/src/env/log-trace/trace-context.ts +162 -0
  127. package/src/env/log-trace/types.test.ts +63 -0
  128. package/src/env/log-trace/types.ts +172 -0
  129. package/src/env/mcp/README.md +244 -0
  130. package/src/env/mcp/__integration__/mcp-component.integration.test.ts +373 -0
  131. package/src/env/mcp/config.test.ts +74 -0
  132. package/src/env/mcp/config.ts +116 -0
  133. package/src/env/mcp/index.ts +41 -0
  134. package/src/env/mcp/loader.test.ts +161 -0
  135. package/src/env/mcp/loader.ts +209 -0
  136. package/src/env/mcp/mcp-component.test.ts +111 -0
  137. package/src/env/mcp/mcp-component.ts +358 -0
  138. package/src/env/mcp/mcp-config-registration.test.ts +304 -0
  139. package/src/env/mcp/mcp-config-registration.ts +50 -0
  140. package/src/env/mcp/scanner.test.ts +170 -0
  141. package/src/env/mcp/scanner.ts +246 -0
  142. package/src/env/mcp/tool/adapter.test.ts +520 -0
  143. package/src/env/mcp/tool/adapter.ts +521 -0
  144. package/src/env/mcp/tool/index.ts +5 -0
  145. package/src/env/mcp/types.test.ts +171 -0
  146. package/src/env/mcp/types.ts +79 -0
  147. package/src/env/memory/README.md +177 -0
  148. package/src/env/memory/built-in/index.ts +59 -0
  149. package/src/env/memory/built-in/recall-memory.ts +103 -0
  150. package/src/env/memory/built-in/record-memory.ts +148 -0
  151. package/src/env/memory/index.ts +20 -0
  152. package/src/env/memory/memory-component.test.ts +239 -0
  153. package/src/env/memory/memory-component.ts +503 -0
  154. package/src/env/memory/memory-config-registration.test.ts +67 -0
  155. package/src/env/memory/memory-config-registration.ts +48 -0
  156. package/src/env/memory/memory-config.ts +45 -0
  157. package/src/env/memory/memory-file.test.ts +268 -0
  158. package/src/env/memory/plugin/index.ts +48 -0
  159. package/src/env/memory/plugin/memory-agent.test.ts +249 -0
  160. package/src/env/memory/plugin/memory-agent.ts +365 -0
  161. package/src/env/memory/plugin/memory-manager.ts +198 -0
  162. package/src/env/memory/plugin/memory-plugin-agent.test.ts +145 -0
  163. package/src/env/memory/plugin/memory-plugin.ts +210 -0
  164. package/src/env/memory/plugin/plugin-simplified.test.ts +51 -0
  165. package/src/env/memory/plugin/recall-memory.test.ts +106 -0
  166. package/src/env/memory/plugin/recall-memory.ts +53 -0
  167. package/src/env/memory/plugin/types.ts +101 -0
  168. package/src/env/memory/tools/memory-agent-tools.ts +228 -0
  169. package/src/env/memory/types.ts +85 -0
  170. package/src/env/paths.ts +118 -0
  171. package/src/env/prompt/index.ts +18 -0
  172. package/src/env/prompt/memory-prompts.test.ts +91 -0
  173. package/src/env/prompt/prompt-component.test.ts +491 -0
  174. package/src/env/prompt/prompt-component.ts +619 -0
  175. package/src/env/prompt/prompt-config-registration.test.ts +213 -0
  176. package/src/env/prompt/prompt-config-registration.ts +39 -0
  177. package/src/env/prompt/prompts-index.ts +504 -0
  178. package/src/env/prompt/renderer.ts +67 -0
  179. package/src/env/prompt/types.ts +136 -0
  180. package/src/env/session/hooks.ts +18 -0
  181. package/src/env/session/index.ts +37 -0
  182. package/src/env/session/search-query-parser.test.ts +425 -0
  183. package/src/env/session/search-query-parser.ts +171 -0
  184. package/src/env/session/session-checkpoint.test.ts +523 -0
  185. package/src/env/session/session-component.extract-recent-messages.test.ts +209 -0
  186. package/src/env/session/session-component.test.ts +132 -0
  187. package/src/env/session/session-component.ts +1249 -0
  188. package/src/env/session/session-config-registration.test.ts +138 -0
  189. package/src/env/session/session-config-registration.ts +52 -0
  190. package/src/env/session/session-message-converter.test.ts +763 -0
  191. package/src/env/session/session-message-converter.ts +415 -0
  192. package/src/env/session/session-message-e2e.test.ts +448 -0
  193. package/src/env/session/session-search.test.ts +391 -0
  194. package/src/env/session/session-store.test.ts +362 -0
  195. package/src/env/session/session-store.ts +141 -0
  196. package/src/env/session/storage/index.ts +6 -0
  197. package/src/env/session/storage/memory.ts +502 -0
  198. package/src/env/session/storage/sqlite.ts +794 -0
  199. package/src/env/session/types.ts +742 -0
  200. package/src/env/skill/config.ts +39 -0
  201. package/src/env/skill/index.ts +6 -0
  202. package/src/env/skill/parser.test.ts +116 -0
  203. package/src/env/skill/parser.ts +77 -0
  204. package/src/env/skill/scanner.test.ts +211 -0
  205. package/src/env/skill/scanner.ts +119 -0
  206. package/src/env/skill/skill-component.test.ts +234 -0
  207. package/src/env/skill/skill-component.ts +352 -0
  208. package/src/env/skill/skill-config-registration.test.ts +60 -0
  209. package/src/env/skill/skill-config-registration.ts +43 -0
  210. package/src/env/skill/tool/index.ts +1 -0
  211. package/src/env/skill/tool/skill-tool.test.ts +100 -0
  212. package/src/env/skill/tool/skill-tool.ts +72 -0
  213. package/src/env/skill/types.ts +64 -0
  214. package/src/env/task/delegate/delegate-tool.test.ts +498 -0
  215. package/src/env/task/delegate/delegate-tool.ts +1014 -0
  216. package/src/env/task/delegate/index.ts +18 -0
  217. package/src/env/task/delegate/stop-tool.test.ts +140 -0
  218. package/src/env/task/delegate/stop-tool.ts +119 -0
  219. package/src/env/task/delegate/task-events.test.ts +178 -0
  220. package/src/env/task/delegate/task-events.ts +143 -0
  221. package/src/env/task/hooks/contexts.test.ts +92 -0
  222. package/src/env/task/hooks/contexts.ts +192 -0
  223. package/src/env/task/hooks/index.ts +23 -0
  224. package/src/env/task/hooks/task-hook-points.test.ts +32 -0
  225. package/src/env/task/hooks/task-hook-points.ts +54 -0
  226. package/src/env/task/index.ts +7 -0
  227. package/src/env/task/plugins/index.ts +13 -0
  228. package/src/env/task/plugins/task-plugin.test.ts +74 -0
  229. package/src/env/task/plugins/task-plugin.ts +89 -0
  230. package/src/env/task/plugins/task-tag-plugin.test.ts +377 -0
  231. package/src/env/task/plugins/task-tag-plugin.ts +319 -0
  232. package/src/env/task/plugins/task-workflow-extractor.integration.test.ts +226 -0
  233. package/src/env/task/plugins/workflow-extractor-agent.test.ts +107 -0
  234. package/src/env/task/plugins/workflow-extractor-agent.ts +225 -0
  235. package/src/env/task/storage/index.ts +6 -0
  236. package/src/env/task/storage/sqlite-task-store.test.ts +283 -0
  237. package/src/env/task/storage/sqlite-task-store.ts +903 -0
  238. package/src/env/task/storage/task-search.test.ts +291 -0
  239. package/src/env/task/tag-service.test.ts +198 -0
  240. package/src/env/task/tag-service.ts +264 -0
  241. package/src/env/task/task-component.test.ts +193 -0
  242. package/src/env/task/task-component.ts +658 -0
  243. package/src/env/task/task-config-registration.test.ts +57 -0
  244. package/src/env/task/task-config-registration.ts +37 -0
  245. package/src/env/task/task-types.test.ts +137 -0
  246. package/src/env/task/tools/complete-tool.ts +44 -0
  247. package/src/env/task/tools/create-tool.ts +49 -0
  248. package/src/env/task/tools/delete-tool.ts +43 -0
  249. package/src/env/task/tools/get-tool.ts +59 -0
  250. package/src/env/task/tools/index.ts +10 -0
  251. package/src/env/task/tools/list-tool.ts +40 -0
  252. package/src/env/task/tools/operation/create-tool.ts +48 -0
  253. package/src/env/task/tools/operation/delete-tool.ts +43 -0
  254. package/src/env/task/tools/operation/get-tool.ts +43 -0
  255. package/src/env/task/tools/operation/index.ts +9 -0
  256. package/src/env/task/tools/operation/list-tool.ts +40 -0
  257. package/src/env/task/tools/operation/operation-tools.test.ts +274 -0
  258. package/src/env/task/tools/operation/operation-types.ts +75 -0
  259. package/src/env/task/tools/operation/update-tool.ts +47 -0
  260. package/src/env/task/tools/task-tools.test.ts +203 -0
  261. package/src/env/task/tools/task-types.test.ts +75 -0
  262. package/src/env/task/tools/task-types.ts +68 -0
  263. package/src/env/task/tools/update-tool.ts +70 -0
  264. package/src/env/task/types.ts +160 -0
  265. package/src/env/tool/built-in/bash.ts +201 -0
  266. package/src/env/tool/built-in/echo.ts +29 -0
  267. package/src/env/tool/built-in/edit-file.test.ts +136 -0
  268. package/src/env/tool/built-in/edit-file.ts +92 -0
  269. package/src/env/tool/built-in/glob.test.ts +94 -0
  270. package/src/env/tool/built-in/glob.ts +65 -0
  271. package/src/env/tool/built-in/grep.test.ts +122 -0
  272. package/src/env/tool/built-in/grep.ts +108 -0
  273. package/src/env/tool/built-in/index.ts +44 -0
  274. package/src/env/tool/built-in/read-file.test.ts +84 -0
  275. package/src/env/tool/built-in/read-file.ts +75 -0
  276. package/src/env/tool/built-in/write-file.test.ts +119 -0
  277. package/src/env/tool/built-in/write-file.ts +68 -0
  278. package/src/env/tool/index.ts +24 -0
  279. package/src/env/tool/registry.test.ts +257 -0
  280. package/src/env/tool/registry.ts +167 -0
  281. package/src/env/tool/tool-component.test.ts +559 -0
  282. package/src/env/tool/tool-component.ts +563 -0
  283. package/src/env/tool/tool-config-registration.test.ts +249 -0
  284. package/src/env/tool/tool-config-registration.ts +46 -0
  285. package/src/env/tool/types.ts +267 -0
  286. package/src/env/tool/validator.test.ts +143 -0
  287. package/src/env/tool/validator.ts +44 -0
  288. package/src/env/types.ts +180 -0
  289. package/src/env/workflow/ask-user-tool-registration.test.ts +216 -0
  290. package/src/env/workflow/complex-workflow.integration.test.ts +1900 -0
  291. package/src/env/workflow/decorators/decorator-node.ts +229 -0
  292. package/src/env/workflow/decorators/decorator.test.ts +196 -0
  293. package/src/env/workflow/decorators/edge.ts +82 -0
  294. package/src/env/workflow/decorators/index.ts +31 -0
  295. package/src/env/workflow/decorators/node-as.ts +98 -0
  296. package/src/env/workflow/decorators/workflow.ts +54 -0
  297. package/src/env/workflow/engine/dag-manager.test.ts +570 -0
  298. package/src/env/workflow/engine/dag-manager.ts +594 -0
  299. package/src/env/workflow/engine/engine.ts +1422 -0
  300. package/src/env/workflow/engine/event-bus.test.ts +359 -0
  301. package/src/env/workflow/engine/event-bus.ts +156 -0
  302. package/src/env/workflow/engine/executor-agent-session.test.ts +84 -0
  303. package/src/env/workflow/engine/executor.test.ts +619 -0
  304. package/src/env/workflow/engine/executor.ts +593 -0
  305. package/src/env/workflow/engine/index.ts +24 -0
  306. package/src/env/workflow/engine/node-registry.test.ts +560 -0
  307. package/src/env/workflow/engine/node-registry.ts +289 -0
  308. package/src/env/workflow/engine/resume-removed.test.ts +22 -0
  309. package/src/env/workflow/engine/scheduler.test.ts +715 -0
  310. package/src/env/workflow/engine/scheduler.ts +318 -0
  311. package/src/env/workflow/engine/workflow-engine.test.ts +815 -0
  312. package/src/env/workflow/extractor/workflow-converter.ts +306 -0
  313. package/src/env/workflow/fixtures.ts +380 -0
  314. package/src/env/workflow/index.ts +38 -0
  315. package/src/env/workflow/integration/run-resume-unified.test.ts +186 -0
  316. package/src/env/workflow/integration/service-integration.test.ts +267 -0
  317. package/src/env/workflow/metadata/keys.ts +12 -0
  318. package/src/env/workflow/nodes/agent-component-adapter.test.ts +318 -0
  319. package/src/env/workflow/nodes/agent-component-adapter.ts +448 -0
  320. package/src/env/workflow/nodes/agent-node.test.ts +371 -0
  321. package/src/env/workflow/nodes/agent-node.ts +598 -0
  322. package/src/env/workflow/nodes/ask-user-node.ts +113 -0
  323. package/src/env/workflow/nodes/condition-node.ts +200 -0
  324. package/src/env/workflow/nodes/index.ts +9 -0
  325. package/src/env/workflow/nodes/merge-node.ts +141 -0
  326. package/src/env/workflow/nodes/skill-node.test.ts +253 -0
  327. package/src/env/workflow/nodes/skill-node.ts +393 -0
  328. package/src/env/workflow/nodes/tool-node.test.ts +251 -0
  329. package/src/env/workflow/nodes/tool-node.ts +493 -0
  330. package/src/env/workflow/nodes/workflow-llm-history.test.ts +455 -0
  331. package/src/env/workflow/nodes/workflow-node.test.ts +315 -0
  332. package/src/env/workflow/nodes/workflow-node.ts +311 -0
  333. package/src/env/workflow/service/index.ts +27 -0
  334. package/src/env/workflow/service/registry.test.ts +133 -0
  335. package/src/env/workflow/service/registry.ts +71 -0
  336. package/src/env/workflow/service/workflow-service.test.ts +310 -0
  337. package/src/env/workflow/service/workflow-service.ts +393 -0
  338. package/src/env/workflow/storage/index.ts +28 -0
  339. package/src/env/workflow/storage/mock-repositories.ts +385 -0
  340. package/src/env/workflow/storage/sqlite.test.ts +179 -0
  341. package/src/env/workflow/storage/sqlite.ts +163 -0
  342. package/src/env/workflow/storage/workflow-repo.test.ts +780 -0
  343. package/src/env/workflow/storage/workflow-repo.ts +342 -0
  344. package/src/env/workflow/tools/ask-user-tool.ts +82 -0
  345. package/src/env/workflow/tools/index.ts +26 -0
  346. package/src/env/workflow/tools/run-workflow.test.ts +352 -0
  347. package/src/env/workflow/tools/run-workflow.ts +214 -0
  348. package/src/env/workflow/types/context.ts +18 -0
  349. package/src/env/workflow/types/decorators-types.ts +198 -0
  350. package/src/env/workflow/types/event.test.ts +515 -0
  351. package/src/env/workflow/types/event.ts +193 -0
  352. package/src/env/workflow/types/index.ts +49 -0
  353. package/src/env/workflow/types/run.test.ts +437 -0
  354. package/src/env/workflow/types/run.ts +173 -0
  355. package/src/env/workflow/types/workflow-hil.ts +114 -0
  356. package/src/env/workflow/types/workflow-message.test.ts +138 -0
  357. package/src/env/workflow/types/workflow-message.ts +196 -0
  358. package/src/env/workflow/types/workflow-session.test.ts +95 -0
  359. package/src/env/workflow/types/workflow-session.ts +59 -0
  360. package/src/env/workflow/types/workflow.test.ts +495 -0
  361. package/src/env/workflow/types/workflow.ts +195 -0
  362. package/src/env/workflow/types_compat.ts +51 -0
  363. package/src/env/workflow/utils/create-workflow.ts +47 -0
  364. package/src/env/workflow/utils/execution-state.ts +245 -0
  365. package/src/env/workflow/utils/index.ts +18 -0
  366. package/src/env/workflow/utils/node-registry-helper.ts +58 -0
  367. package/src/env/workflow/utils/recovery-validator.test.ts +460 -0
  368. package/src/env/workflow/utils/recovery-validator.ts +377 -0
  369. package/src/env/workflow/utils/session-parser.test.ts +111 -0
  370. package/src/env/workflow/utils/session-parser.ts +94 -0
  371. package/src/env/workflow/utils/session-recovery.test.ts +334 -0
  372. package/src/env/workflow/utils/session-recovery.ts +188 -0
  373. package/src/env/workflow/utils/template-resolver.test.ts +258 -0
  374. package/src/env/workflow/utils/template-resolver.ts +436 -0
  375. package/src/env/workflow/utils/validation-rules.ts +149 -0
  376. package/src/env/workflow/workflow-component.ts +544 -0
  377. package/src/index.ts +422 -0
  378. package/src/utils/id.ts +21 -0
@@ -0,0 +1,598 @@
1
+ /**
2
+ * @fileoverview AgentNode - Delegate work to an AI agent
3
+ *
4
+ * AgentNode delegates work to an AI agent with task description and context.
5
+ */
6
+
7
+ import { Node, NodeDefinition, NodeExecutionContext, NodeExecutionResult } from '../types';
8
+ import type { SessionComponent } from '../../session/session-component';
9
+ import { AskUserError } from '../types/workflow-hil';
10
+ import { TracedAs } from '../../log-trace/decorator';
11
+
12
+ // ============================================================================
13
+ // Agent Types
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Agent 注册配置
18
+ */
19
+ export interface AgentRegistrationConfig {
20
+ /** Agent 类型 */
21
+ type?: string;
22
+ /** 系统提示词 */
23
+ systemPrompt?: string;
24
+ /** 模型名称 */
25
+ model?: string;
26
+ /** 最大迭代次数 */
27
+ maxIterations?: number;
28
+ /** 允许的工具列表 */
29
+ allowedTools?: string[];
30
+ /** 禁止的工具列表 */
31
+ deniedTools?: string[];
32
+ }
33
+
34
+ /**
35
+ * AgentRunner 接口
36
+ *
37
+ * 用于运行 AI Agent 的接口
38
+ */
39
+ export interface AgentRunner {
40
+ /**
41
+ * 运行 Agent
42
+ * @param config Agent 配置
43
+ * @param resumeOptions 可选的恢复选项(用于恢复暂停的 agent)
44
+ * @returns Agent 执行结果
45
+ */
46
+ run(config: AgentConfig, resumeOptions?: { userResponse?: string; agentSessionId?: string }): Promise<AgentResult>;
47
+
48
+ /**
49
+ * 恢复暂停的 Agent
50
+ *
51
+ * @param agentSessionId Agent session ID
52
+ * @param userResponse 用户的响应
53
+ * @param config 原始的 agent 配置
54
+ * @returns Agent 执行结果
55
+ */
56
+ resumeAgent?(agentSessionId: string, userResponse: string, config: AgentConfig): Promise<AgentResult>;
57
+
58
+ /**
59
+ * 获取当前 agent session ID
60
+ */
61
+ getCurrentAgentSessionId?(): string | undefined;
62
+
63
+ /**
64
+ * 设置 SessionComponent
65
+ */
66
+ setSessionComponent?(sessionComponent: SessionComponent): void;
67
+
68
+ /**
69
+ * 获取待恢复的 session 信息
70
+ */
71
+ getPendingSession?(agentSessionId: string): { agentSessionId: string; nodeId: string; runId: string; createdAt: number } | undefined;
72
+
73
+ /**
74
+ * 注册 Agent(如果不存在)
75
+ */
76
+ registerAgent?(name: string, config: AgentRegistrationConfig): void;
77
+
78
+ /**
79
+ * 检查 Agent 是否已注册
80
+ */
81
+ hasAgent?(name: string): boolean;
82
+ }
83
+
84
+ /**
85
+ * Agent 配置
86
+ */
87
+ export interface AgentConfig {
88
+ /** Agent 类型 */
89
+ type: string;
90
+ /** Prompt(任务描述) */
91
+ prompt: string;
92
+ /** 可选配置 */
93
+ options?: {
94
+ /** 超时时间(毫秒) */
95
+ timeout?: number;
96
+ /** 模型名称 */
97
+ model?: string;
98
+ /** 允许的工具列表(用于限制 agent 可用的工具) */
99
+ allowedTools?: string[];
100
+ /** 拒绝的工具列表 */
101
+ deniedTools?: string[];
102
+ };
103
+ /** Workflow 历史消息(来自前置 agent 节点) */
104
+ workflowHistory?: Array<{
105
+ role: 'user' | 'assistant' | 'tool';
106
+ content: string;
107
+ toolCallId?: string;
108
+ toolName?: string;
109
+ nodeId?: string;
110
+ }>;
111
+ /** 节点 ID(用于 history 标记) */
112
+ nodeId?: string;
113
+ /** Agent sub-session ID(用于 HiL resume) */
114
+ agentSessionId?: string;
115
+ }
116
+
117
+ /**
118
+ * Agent 执行结果
119
+ */
120
+ export interface AgentResult {
121
+ /** Agent 输出 */
122
+ output: unknown;
123
+ /** 执行元数据 */
124
+ metadata?: {
125
+ /** 消耗的 token 数量 */
126
+ tokens?: number;
127
+ /** 执行耗时(毫秒) */
128
+ duration?: number;
129
+ /** 其他元数据 */
130
+ [key: string]: unknown;
131
+ };
132
+ /** 生成的消息列表(用于 workflow history 累积) */
133
+ messages?: Array<{
134
+ role: 'user' | 'assistant' | 'tool';
135
+ content: string;
136
+ toolCallId?: string;
137
+ toolName?: string;
138
+ nodeId?: string;
139
+ }>;
140
+ }
141
+
142
+ // ============================================================================
143
+ // AgentNode Implementation
144
+ // ============================================================================
145
+
146
+ /**
147
+ * AgentNode 类
148
+ *
149
+ * 委托 AI Agent 执行任务的节点类型
150
+ */
151
+ export class AgentNode implements Node {
152
+ /** 节点类型 */
153
+ readonly type = 'agent';
154
+ /** 节点 ID */
155
+ readonly id: string;
156
+
157
+ /**
158
+ * 创建 AgentNode
159
+ * @param definition 节点定义
160
+ * @param agentRunner Agent 运行器
161
+ */
162
+ constructor(
163
+ private definition: NodeDefinition,
164
+ private agentRunner: AgentRunner
165
+ ) {
166
+ this.id = definition.id;
167
+ }
168
+
169
+ /**
170
+ * 执行 AgentNode
171
+ *
172
+ * 流程:
173
+ * 1. 从 definition.config 获取 agent 配置
174
+ * 2. 解析 prompt 模板(可能引用前置节点输出)
175
+ * 3. 通过 agentRunner 运行 agent
176
+ * 4. 返回 agent 结果
177
+ *
178
+ * @param context 执行上下文
179
+ * @returns 执行结果
180
+ */
181
+ @TracedAs("agent.node.execute", { recordParams: true, recordResult: true, log: true })
182
+ async execute(context: NodeExecutionContext): Promise<NodeExecutionResult> {
183
+ const startTime = Date.now();
184
+
185
+ try {
186
+ // 1. 从配置中获取 agent 参数
187
+ const config = this.definition.config as Record<string, unknown> || {};
188
+ const agentType = (config.agent_type as string) || 'general';
189
+ const promptTemplate = (config.prompt as string) || '';
190
+ const options = (config.options as AgentConfig['options']) || {};
191
+
192
+ // 2. 解析 prompt 模板
193
+ const resolvedPrompt = this.resolveTemplate(promptTemplate, context);
194
+
195
+ // 3. 自动注册 agent(如果不存在)
196
+ // 这样 workflow 定义中引用的 agent 类型会被自动注册,无需手动预注册
197
+ if (this.agentRunner.registerAgent && this.agentRunner.hasAgent) {
198
+ if (!this.agentRunner.hasAgent(agentType)) {
199
+ this.agentRunner.registerAgent(agentType, {
200
+ type: agentType,
201
+ systemPrompt: (config.system_prompt as string) || `You are a ${agentType} agent.`,
202
+ model: options.model,
203
+ });
204
+ }
205
+ }
206
+
207
+ // 4. 检查是否需要恢复执行(从 pause 恢复时传递了 userResponse)
208
+ // 从 context 中获取恢复选项(由 engine.resumeNode 设置)
209
+ const userResponse = (context as any).userResponse;
210
+ let agentSessionId = (context as any).agentSessionId;
211
+
212
+ // 5. 如果不是恢复模式,创建 agent sub-session
213
+ // Sub-session 用于存储 agent 的对话历史,支持 resume
214
+ if (!userResponse && context.sessionComponent && !agentSessionId) {
215
+ agentSessionId = await this.createAgentSubSession(context);
216
+
217
+ // 将 agentSessionId 设置到 context 中,供后续使用(如 AskUserError)
218
+ // 注意:NodeExecutionContext 是引用类型,直接修改会生效
219
+ (context as any).agentSessionId = agentSessionId;
220
+ }
221
+
222
+ // 6. 构建 AgentConfig
223
+ // 注意:添加 allowedTools: ['ask_user'] 确保 ask_user 工具在 workflow agent 上下文中可用
224
+ // 这样 ask_user 只在 workflow agent 中可用,不影响 default agent 和 sub agent
225
+ const agentConfig: AgentConfig = {
226
+ type: agentType,
227
+ prompt: resolvedPrompt,
228
+ options: {
229
+ timeout: options.timeout,
230
+ model: options.model,
231
+ // 添加 ask_user 到 allowedTools,确保 workflow agent 可以调用此工具
232
+ allowedTools: ['ask_user'],
233
+ },
234
+ // 传递 workflow history(来自前置 agent 节点)
235
+ workflowHistory: context.workflowHistory,
236
+ // 传递节点信息(用于 history 标记)
237
+ nodeId: this.definition.id,
238
+ // 传递 agent sub-session ID
239
+ agentSessionId,
240
+ };
241
+
242
+ // 如果有 userResponse,说明是从 pause 恢复的,应该传递给 agentRunner
243
+ const resumeOptions = userResponse
244
+ ? { userResponse, agentSessionId }
245
+ : undefined;
246
+
247
+ // 7. 运行 agent(传递 resumeOptions 如果是从 pause 恢复的)
248
+ const result = await this.agentRunner.run(agentConfig, resumeOptions);
249
+
250
+ // 8. 计算执行耗时
251
+ const duration = Date.now() - startTime;
252
+
253
+ // 9. 返回成功结果
254
+ return {
255
+ output: {
256
+ result: result.output,
257
+ metadata: result.metadata,
258
+ // 返回 workflow history 供 WorkflowEngine 累积
259
+ workflowHistory: result.messages,
260
+ },
261
+ error: undefined,
262
+ duration,
263
+ };
264
+ } catch (error) {
265
+ // 检查是否是 AskUserError - 这是用于工作流暂停的特殊错误,需要向上传播
266
+ // 以便 Executor 能够正确处理暂停逻辑
267
+ if (error instanceof AskUserError) {
268
+ // 确保 AskUserError 携带 agentSessionId(如果有的话)
269
+ const contextAgentSessionId = (context as any).agentSessionId;
270
+ if (contextAgentSessionId && !error.agentSessionId) {
271
+ // 创建一个新的 AskUserError 携带 agentSessionId
272
+ throw new AskUserError(
273
+ error.runId,
274
+ error.sessionId,
275
+ error.nodeId,
276
+ error.nodeType,
277
+ error.query,
278
+ contextAgentSessionId,
279
+ error.timestamp
280
+ );
281
+ }
282
+ throw error; // 重新抛出,让 Executor 处理
283
+ }
284
+
285
+ // 其他错误处理
286
+ const duration = Date.now() - startTime;
287
+ const errorMessage = error instanceof Error ? error.message : String(error);
288
+
289
+ return {
290
+ output: undefined,
291
+ error: errorMessage,
292
+ duration,
293
+ };
294
+ }
295
+ }
296
+
297
+ /**
298
+ * 创建 agent sub-session
299
+ *
300
+ * Sub-session 用于存储 agent 的对话历史,支持 human-in-loop resume
301
+ */
302
+ @TracedAs("agent.node.createAgentSubSession", { recordParams: true, recordResult: true, log: true })
303
+ private async createAgentSubSession(context: NodeExecutionContext): Promise<string> {
304
+ const sessionComponent = context.sessionComponent;
305
+ if (!sessionComponent) {
306
+ return '';
307
+ }
308
+
309
+ const agentSessionId = `agent_${this.definition.id}_${Date.now()}`;
310
+
311
+ // 1. 创建 agent sub-session
312
+ await sessionComponent.create({
313
+ id: agentSessionId,
314
+ title: `Agent: ${this.definition.id}`,
315
+ metadata: {
316
+ type: 'agent',
317
+ parentSessionId: context.sessionId,
318
+ nodeId: this.definition.id,
319
+ workflowName: context.workflowName,
320
+ },
321
+ });
322
+
323
+ // 2. 更新 workflow session 的 metadata,添加 agentSessions 引用
324
+ const workflowSession = await sessionComponent.get(context.sessionId);
325
+ if (workflowSession) {
326
+ const metadata = workflowSession.metadata as any || {};
327
+ const agentSessions = metadata.agentSessions || [];
328
+
329
+ // 检查是否已经存在
330
+ const existingIndex = agentSessions.findIndex(
331
+ (s: any) => s.nodeId === this.definition.id
332
+ );
333
+
334
+ if (existingIndex >= 0) {
335
+ agentSessions[existingIndex] = {
336
+ nodeId: this.definition.id,
337
+ sessionId: agentSessionId,
338
+ status: 'active',
339
+ createdAt: Date.now(),
340
+ };
341
+ } else {
342
+ agentSessions.push({
343
+ nodeId: this.definition.id,
344
+ sessionId: agentSessionId,
345
+ status: 'active',
346
+ createdAt: Date.now(),
347
+ });
348
+ }
349
+
350
+ await sessionComponent.update(context.sessionId, {
351
+ metadata: {
352
+ ...metadata,
353
+ agentSessions,
354
+ },
355
+ });
356
+ }
357
+
358
+ return agentSessionId;
359
+ }
360
+
361
+ /**
362
+ * 解析模板字符串
363
+ *
364
+ * 支持以下模板变量:
365
+ * - {{input.key}} - 从 context.input 获取值
366
+ * - {{nodes.nodeId}} - 从前置节点输出获取值
367
+ * - {{nodes.nodeId.output}} - 从前置节点输出获取值
368
+ * - {{nodes.nodeId.output.path}} - 从前置节点输出的嵌套路径获取值
369
+ *
370
+ * @param template 模板字符串
371
+ * @param context 执行上下文
372
+ * @returns 解析后的字符串
373
+ */
374
+ private resolveTemplate(template: string, context: NodeExecutionContext): string {
375
+ if (!template) {
376
+ return '';
377
+ }
378
+
379
+ let resolved = template;
380
+
381
+ // 解析 input 变量: {{input.key}} 或 {{input.key.nested}}
382
+ resolved = resolved.replace(/\{\{input\.([^}]+)\}\}/g, (match, path) => {
383
+ const value = this.getNestedValue(context.input as any, path);
384
+ return this.stringifyValue(value, match);
385
+ });
386
+
387
+ // 解析 node 输出变量: {{nodes.nodeId}} 或 {{nodes.nodeId.output}} 或 {{nodes.nodeId.output.path}}
388
+ resolved = resolved.replace(/\{\{nodes\.([^}]+)\}\}/g, (match, path) => {
389
+ // 路径格式: nodeId 或 nodeId.output 或 nodeId.output.path
390
+ const segments = path.split('.');
391
+ const nodeId = segments[0];
392
+
393
+ // 从 previousOutputs 获取原始输出(尝试规范化匹配)
394
+ let nodeOutput = context.previousOutputs.get(nodeId);
395
+
396
+ if (nodeOutput === undefined) {
397
+ // 尝试规范化版本(hyphen <-> underscore)
398
+ const normalizedUnderscore = nodeId.replace(/-/g, '_');
399
+ const normalizedHyphen = nodeId.replace(/_/g, '-');
400
+
401
+ if (normalizedUnderscore !== nodeId) {
402
+ nodeOutput = context.previousOutputs.get(normalizedUnderscore);
403
+ }
404
+ if (nodeOutput === undefined && normalizedHyphen !== nodeId) {
405
+ nodeOutput = context.previousOutputs.get(normalizedHyphen);
406
+ }
407
+ }
408
+
409
+ if (nodeOutput === undefined) {
410
+ return match;
411
+ }
412
+
413
+ // 首先从包装格式中提取实际值
414
+ // 处理 DecoratorNode 格式: { success, output, ... } 和 AgentNode 格式: { result, metadata }
415
+ nodeOutput = this.extractFromWrapper(nodeOutput);
416
+
417
+ // 然后从提取后的值中获取嵌套路径
418
+ if (segments.length === 1) {
419
+ // 只有 nodeId,直接返回提取后的值
420
+ // 如果是对象,序列化为 JSON;如果是原始值,转为字符串
421
+ return this.stringifyValue(nodeOutput, match);
422
+ }
423
+
424
+ // 从嵌套路径获取(从 segments[1] 开始)
425
+ // 如果 segments[1] 是包装属性("output"、"result"、"metadata"),跳过它因为 extractFromWrapper 已经提取了
426
+ let value: any = nodeOutput;
427
+ let startIndex = 1;
428
+ if (segments.length > 1 &&
429
+ (segments[1] === 'output' || segments[1] === 'result' || segments[1] === 'metadata')) {
430
+ startIndex = 2;
431
+ }
432
+ for (let i = startIndex; i < segments.length; i++) {
433
+ if (value === null || value === undefined) {
434
+ return match;
435
+ }
436
+ value = value[segments[i]];
437
+ }
438
+
439
+ return this.stringifyValue(value, match);
440
+ });
441
+
442
+ // 解析裸 node 输出变量: {{nodeId}} 或 {{nodeId.output}} (不带 nodes. 前缀)
443
+ // 这需要放在 {{input.}} 和 {{nodes.}} 之后,以避免与它们冲突
444
+ resolved = resolved.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
445
+ // 跳过已经是 input 或 nodes 开头的(已由前面的规则处理)
446
+ if (path.startsWith('input.') || path.startsWith('nodes.')) {
447
+ return match;
448
+ }
449
+
450
+ const segments = path.split('.');
451
+ const nodeId = segments[0];
452
+
453
+ // 从 previousOutputs 获取原始输出(尝试规范化匹配)
454
+ let nodeOutput = context.previousOutputs.get(nodeId);
455
+
456
+ if (nodeOutput === undefined) {
457
+ // 尝试规范化版本(hyphen <-> underscore)
458
+ const normalizedUnderscore = nodeId.replace(/-/g, '_');
459
+ const normalizedHyphen = nodeId.replace(/_/g, '-');
460
+
461
+ if (normalizedUnderscore !== nodeId) {
462
+ nodeOutput = context.previousOutputs.get(normalizedUnderscore);
463
+ }
464
+ if (nodeOutput === undefined && normalizedHyphen !== nodeId) {
465
+ nodeOutput = context.previousOutputs.get(normalizedHyphen);
466
+ }
467
+ }
468
+
469
+ if (nodeOutput === undefined) {
470
+ return match;
471
+ }
472
+
473
+ // 首先从包装格式中提取实际值
474
+ nodeOutput = this.extractFromWrapper(nodeOutput);
475
+
476
+ // 然后从提取后的值中获取嵌套路径
477
+ if (segments.length === 1) {
478
+ // 只有 nodeId,直接返回提取后的值
479
+ // 如果是对象,序列化为 JSON;如果是原始值,转为字符串
480
+ return this.stringifyValue(nodeOutput, match);
481
+ }
482
+
483
+ // 从嵌套路径获取(从 segments[1] 开始)
484
+ // 如果 segments[1] 是包装属性("output"、"result"、"metadata"),跳过它因为 extractFromWrapper 已经提取了
485
+ let value: any = nodeOutput;
486
+ let startIndex = 1;
487
+ if (segments.length > 1 &&
488
+ (segments[1] === 'output' || segments[1] === 'result' || segments[1] === 'metadata')) {
489
+ startIndex = 2;
490
+ }
491
+ for (let i = startIndex; i < segments.length; i++) {
492
+ if (value === null || value === undefined) {
493
+ return match;
494
+ }
495
+ value = value[segments[i]];
496
+ }
497
+
498
+ return this.stringifyValue(value, match);
499
+ });
500
+
501
+ return resolved;
502
+ }
503
+
504
+ /**
505
+ * Convert a value to string for template replacement.
506
+ * - If value is undefined, return the original template
507
+ * - If value is a string, return it directly
508
+ * - If value is null, return 'null'
509
+ * - If value is an object or array, serialize to JSON
510
+ *
511
+ * @param value - The value to stringify
512
+ * @param originalTemplate - The original template string (returned if value is undefined)
513
+ * @returns String representation of the value
514
+ */
515
+ private stringifyValue(value: unknown, originalTemplate: string): string {
516
+ if (value === undefined) {
517
+ return originalTemplate;
518
+ }
519
+ if (value === null) {
520
+ return 'null';
521
+ }
522
+ if (typeof value === 'string') {
523
+ return value;
524
+ }
525
+ // For objects and arrays, serialize to JSON
526
+ return JSON.stringify(value);
527
+ }
528
+
529
+ /**
530
+ * 从包装格式中提取实际输出值
531
+ *
532
+ * 处理以下格式:
533
+ * - AgentNode: { result, metadata } -> 返回 result
534
+ * - DecoratorNode: { success, output, ... } -> 返回 output
535
+ * - 其他格式直接返回
536
+ *
537
+ * @param value 原始值
538
+ * @returns 提取后的值
539
+ */
540
+ private extractFromWrapper(value: unknown): unknown {
541
+ if (value === null || value === undefined) {
542
+ return value;
543
+ }
544
+
545
+ if (typeof value !== 'object') {
546
+ return value;
547
+ }
548
+
549
+ // 检查 AgentNode 包装格式: { result, metadata }
550
+ // 这是 AgentNode 输出的特征签名
551
+ if ('result' in value && 'metadata' in value) {
552
+ return (value as { result: unknown }).result;
553
+ }
554
+
555
+ // 检查 DecoratorNode 包装格式: { success, output, ... }
556
+ // 有 'output' 但没有 'result'(AgentNode 的特征)
557
+ if ('output' in value) {
558
+ return (value as { output: unknown }).output;
559
+ }
560
+
561
+ return value;
562
+ }
563
+
564
+ /**
565
+ * 获取嵌套对象的值
566
+ *
567
+ * @param obj 对象
568
+ * @param path 属性路径
569
+ * @returns 值或 undefined
570
+ */
571
+ private getNestedValue(obj: Record<string, unknown> | undefined, path: string): unknown {
572
+ if (!obj) {
573
+ return undefined;
574
+ }
575
+
576
+ // Special case: If obj has an 'input' key, we might be accessing a key inside it
577
+ // This happens when createExecutionContext wraps globalInput as {input: globalInput}
578
+ if ('input' in obj && path in (obj['input'] as Record<string, unknown>)) {
579
+ // Path exists inside 'input', so look there
580
+ return (obj['input'] as Record<string, unknown>)[path];
581
+ }
582
+
583
+ // Normal case: path.split('.').reduce(...) with obj as initial value
584
+ // This allows {{input.city}} to work when path = "city" and obj = {input: {city: "..."}}
585
+ return path.split('.').reduce((current: unknown, key: string) => {
586
+ if (current && typeof current === 'object') {
587
+ return (current as Record<string, unknown>)[key];
588
+ }
589
+ return undefined;
590
+ }, obj as unknown);
591
+ }
592
+ }
593
+
594
+ // ============================================================================
595
+ // Exports
596
+ // ============================================================================
597
+
598
+ export { AgentNode as default };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @fileoverview AskUserNode - Node for requesting user input
3
+ *
4
+ * This node is used for simple pause/resume scenarios where a workflow
5
+ * needs user input without going through an Agent.
6
+ */
7
+
8
+ import type { Node, NodeExecutionResult } from '../types';
9
+ import type { NodeDefinition } from '../types';
10
+ import { AskUserError, createNodeInterruptEvent } from '../types/workflow-hil';
11
+ import type { NodeInterruptEvent } from '../types/workflow-hil';
12
+
13
+ // ==================== Types ====================
14
+
15
+ /**
16
+ * AskUserNode Configuration
17
+ */
18
+ export interface AskUserNodeConfig {
19
+ /** Question to ask the user */
20
+ query?: string;
21
+ /** Optional choices for the user */
22
+ options?: string[];
23
+ /** If true, user must explicitly confirm */
24
+ required_confirm?: boolean;
25
+ }
26
+
27
+ // ==================== AskUserNode Class ====================
28
+
29
+ /**
30
+ * AskUserNode - Standalone node for user input requests
31
+ *
32
+ * This node pauses the workflow and waits for user response.
33
+ * Used for simple human-in-loop scenarios without an Agent.
34
+ *
35
+ * @example
36
+ * ```yaml
37
+ * nodes:
38
+ * - id: confirm_step
39
+ * type: ask_user
40
+ * config:
41
+ * query: "Do you want to proceed?"
42
+ * options:
43
+ * - "Yes"
44
+ * - "No"
45
+ * - "Cancel"
46
+ * ```
47
+ */
48
+ export class AskUserNode implements Node {
49
+ readonly type = 'ask_user';
50
+ readonly id: string;
51
+
52
+ private config: AskUserNodeConfig;
53
+
54
+ /**
55
+ * Create a new AskUserNode
56
+ */
57
+ constructor(definition: NodeDefinition) {
58
+ this.id = definition.id;
59
+ this.config = (definition.config as AskUserNodeConfig) || {};
60
+ }
61
+
62
+ /**
63
+ * Execute the AskUserNode
64
+ *
65
+ * Publishes a node.interrupt event and throws AskUserError to pause workflow.
66
+ *
67
+ * @param context - Node execution context
68
+ * @throws AskUserError - Always throws to pause workflow
69
+ */
70
+ async execute(context: {
71
+ runId: string;
72
+ sessionId: string;
73
+ eventBus: {
74
+ publish(event: NodeInterruptEvent): Promise<void>;
75
+ };
76
+ nodeOutputs: Map<string, unknown>;
77
+ }): Promise<NodeExecutionResult> {
78
+ const query = this.config.query || '确认继续吗?';
79
+ const options = this.config.options;
80
+
81
+ // Build query with options if provided
82
+ const fullQuery = options
83
+ ? `${query} (选项: ${options.join(', ')})`
84
+ : query;
85
+
86
+ // Publish node.interrupt event
87
+ await context.eventBus.publish(
88
+ createNodeInterruptEvent(
89
+ context.runId,
90
+ this.id,
91
+ this.type,
92
+ fullQuery
93
+ )
94
+ );
95
+
96
+ // Throw AskUserError to pause workflow
97
+ throw new AskUserError(
98
+ context.runId,
99
+ context.sessionId,
100
+ this.id,
101
+ this.type,
102
+ query
103
+ );
104
+ }
105
+
106
+ /**
107
+ * Validate node configuration
108
+ */
109
+ validateConfig(_config: Record<string, unknown>): boolean {
110
+ // query is optional, has default value
111
+ return true;
112
+ }
113
+ }