@ai-setting/roy-agent-core 1.3.10 → 1.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.js +1647 -0
- package/dist/index.js +12579 -89691
- package/package.json +19 -56
- package/src/config/config-component.test.ts +0 -627
- package/src/config/config-component.ts +0 -906
- package/src/config/config-parser.test.ts +0 -319
- package/src/config/config-parser.ts +0 -203
- package/src/config/decentralized-config.test.ts +0 -740
- package/src/config/env-key.ts +0 -210
- package/src/config/env-source.test.ts +0 -252
- package/src/config/env-source.ts +0 -301
- package/src/config/file-source.test.ts +0 -357
- package/src/config/file-source.ts +0 -421
- package/src/config/index.ts +0 -24
- package/src/config/protocol-resolver.test.ts +0 -217
- package/src/config/protocol-resolver.ts +0 -228
- package/src/env/agent/agent-component.abort.test.ts +0 -511
- package/src/env/agent/agent-component.record-session.test.ts +0 -349
- package/src/env/agent/agent-component.test.ts +0 -1389
- package/src/env/agent/agent-component.tool-error.test.ts +0 -327
- package/src/env/agent/agent-component.ts +0 -1711
- package/src/env/agent/agent-config-registration.test.ts +0 -226
- package/src/env/agent/agent-config-registration.ts +0 -46
- package/src/env/agent/agent-reminder-plugin.integration.test.ts +0 -243
- package/src/env/agent/index.ts +0 -10
- package/src/env/agent/summary-agent.parse-hint.test.ts +0 -360
- package/src/env/agent/summary-agent.ts +0 -508
- package/src/env/agent/types.ts +0 -536
- package/src/env/commands/commands-component.test.ts +0 -364
- package/src/env/commands/commands-component.ts +0 -604
- package/src/env/commands/commands-config-registration.test.ts +0 -198
- package/src/env/commands/commands-config-registration.ts +0 -38
- package/src/env/commands/index.ts +0 -21
- package/src/env/commands/parser.test.ts +0 -203
- package/src/env/commands/parser.ts +0 -115
- package/src/env/commands/types.ts +0 -184
- package/src/env/commands-prompt-integration.test.ts +0 -243
- package/src/env/component-env.test.ts +0 -119
- package/src/env/component.ts +0 -335
- package/src/env/constants.test.ts +0 -72
- package/src/env/constants.ts +0 -123
- package/src/env/debug/debug-component.test.ts +0 -114
- package/src/env/debug/debug-component.ts +0 -547
- package/src/env/debug/formatters/index.ts +0 -9
- package/src/env/debug/formatters/repl-formatter.test.ts +0 -139
- package/src/env/debug/formatters/repl-formatter.ts +0 -358
- package/src/env/debug/formatters/trace-formatter.test.ts +0 -119
- package/src/env/debug/formatters/trace-formatter.ts +0 -191
- package/src/env/debug/formatters/tree-formatter.test.ts +0 -107
- package/src/env/debug/formatters/tree-formatter.ts +0 -325
- package/src/env/debug/index.ts +0 -38
- package/src/env/debug/parser/regex-parser.test.ts +0 -201
- package/src/env/debug/parser/regex-parser.ts +0 -196
- package/src/env/debug/parser/span-builder.test.ts +0 -241
- package/src/env/debug/parser/span-builder.ts +0 -386
- package/src/env/debug/reader/log-reader.test.ts +0 -170
- package/src/env/debug/reader/log-reader.ts +0 -186
- package/src/env/debug/reader/span-db-reader.test.ts +0 -118
- package/src/env/debug/reader/span-db-reader.ts +0 -201
- package/src/env/debug/types.test.ts +0 -187
- package/src/env/debug/types.ts +0 -171
- package/src/env/environment-init.test.ts +0 -183
- package/src/env/environment-lifecycle.test.ts +0 -516
- package/src/env/environment-service.test.ts +0 -332
- package/src/env/environment.handle-query.test.ts +0 -96
- package/src/env/environment.test.ts +0 -232
- package/src/env/environment.ts +0 -708
- package/src/env/errors.test.ts +0 -165
- package/src/env/errors.ts +0 -157
- package/src/env/event-source/event-source-agent-handler.test.ts +0 -193
- package/src/env/event-source/event-source-agent-handler.ts +0 -111
- package/src/env/event-source/event-source-component.process-cleanup.test.ts +0 -236
- package/src/env/event-source/event-source-component.stop.test.ts +0 -346
- package/src/env/event-source/event-source-component.test.ts +0 -1207
- package/src/env/event-source/event-source-component.ts +0 -1379
- package/src/env/event-source/event-source-config-registration.test.ts +0 -242
- package/src/env/event-source/event-source-config-registration.ts +0 -37
- package/src/env/event-source/event-source-integration.test.ts +0 -320
- package/src/env/event-source/event-source-platform.test.ts +0 -630
- package/src/env/event-source/types.ts +0 -298
- package/src/env/hook/global-hook-manager.ts +0 -162
- package/src/env/hook/hook-manager.test.ts +0 -374
- package/src/env/hook/hook-manager.ts +0 -309
- package/src/env/hook/index.ts +0 -38
- package/src/env/hook/types.ts +0 -138
- package/src/env/index.ts +0 -144
- package/src/env/interface.ts +0 -203
- package/src/env/llm/hooks.test.ts +0 -293
- package/src/env/llm/hooks.ts +0 -316
- package/src/env/llm/index.ts +0 -61
- package/src/env/llm/invoke-threshold-check.test.ts +0 -88
- package/src/env/llm/invoke-timeout.test.ts +0 -54
- package/src/env/llm/invoke.test.ts +0 -71
- package/src/env/llm/invoke.ts +0 -1039
- package/src/env/llm/llm-config.test.ts +0 -523
- package/src/env/llm/llm.test.ts +0 -233
- package/src/env/llm/llm.ts +0 -568
- package/src/env/llm/provider.test.ts +0 -182
- package/src/env/llm/provider.ts +0 -108
- package/src/env/llm/transform.test.ts +0 -251
- package/src/env/llm/transform.ts +0 -286
- package/src/env/llm/types.test.ts +0 -580
- package/src/env/llm/types.ts +0 -424
- package/src/env/log-trace/decorator-otel.test.ts +0 -182
- package/src/env/log-trace/decorator.ts +0 -230
- package/src/env/log-trace/index.ts +0 -79
- package/src/env/log-trace/log-trace-component.test.ts +0 -242
- package/src/env/log-trace/log-trace-component.ts +0 -497
- package/src/env/log-trace/log-trace-config-registration.test.ts +0 -348
- package/src/env/log-trace/log-trace-config-registration.ts +0 -45
- package/src/env/log-trace/logger.test.ts +0 -149
- package/src/env/log-trace/logger.ts +0 -522
- package/src/env/log-trace/opentelemetry/cli-propagation.test.ts +0 -147
- package/src/env/log-trace/opentelemetry/cli-propagation.ts +0 -194
- package/src/env/log-trace/opentelemetry/integration.test.ts +0 -668
- package/src/env/log-trace/opentelemetry/mod.ts +0 -25
- package/src/env/log-trace/opentelemetry/propagation-env.test.ts +0 -181
- package/src/env/log-trace/opentelemetry/propagation-env.ts +0 -136
- package/src/env/log-trace/opentelemetry/propagation.test.ts +0 -259
- package/src/env/log-trace/opentelemetry/propagation.ts +0 -215
- package/src/env/log-trace/opentelemetry/tracer-provider-context.test.ts +0 -166
- package/src/env/log-trace/opentelemetry/tracer-provider.test.ts +0 -379
- package/src/env/log-trace/opentelemetry/tracer-provider.ts +0 -612
- package/src/env/log-trace/span-storage.test.ts +0 -145
- package/src/env/log-trace/span-storage.ts +0 -230
- package/src/env/log-trace/trace-context.test.ts +0 -187
- package/src/env/log-trace/trace-context.ts +0 -162
- package/src/env/log-trace/types.test.ts +0 -63
- package/src/env/log-trace/types.ts +0 -172
- package/src/env/mcp/README.md +0 -244
- package/src/env/mcp/__integration__/mcp-component.integration.test.ts +0 -373
- package/src/env/mcp/config.test.ts +0 -74
- package/src/env/mcp/config.ts +0 -116
- package/src/env/mcp/index.ts +0 -41
- package/src/env/mcp/loader.test.ts +0 -161
- package/src/env/mcp/loader.ts +0 -209
- package/src/env/mcp/mcp-component.test.ts +0 -111
- package/src/env/mcp/mcp-component.ts +0 -358
- package/src/env/mcp/mcp-config-registration.test.ts +0 -304
- package/src/env/mcp/mcp-config-registration.ts +0 -50
- package/src/env/mcp/scanner.test.ts +0 -170
- package/src/env/mcp/scanner.ts +0 -246
- package/src/env/mcp/tool/adapter.test.ts +0 -520
- package/src/env/mcp/tool/adapter.ts +0 -521
- package/src/env/mcp/tool/index.ts +0 -5
- package/src/env/mcp/types.test.ts +0 -171
- package/src/env/mcp/types.ts +0 -79
- package/src/env/memory/README.md +0 -177
- package/src/env/memory/built-in/index.ts +0 -59
- package/src/env/memory/built-in/recall-memory.ts +0 -103
- package/src/env/memory/built-in/record-memory.ts +0 -148
- package/src/env/memory/index.ts +0 -20
- package/src/env/memory/memory-component.test.ts +0 -239
- package/src/env/memory/memory-component.ts +0 -503
- package/src/env/memory/memory-config-registration.test.ts +0 -67
- package/src/env/memory/memory-config-registration.ts +0 -48
- package/src/env/memory/memory-config.ts +0 -45
- package/src/env/memory/memory-file.test.ts +0 -268
- package/src/env/memory/plugin/index.ts +0 -48
- package/src/env/memory/plugin/memory-agent.test.ts +0 -249
- package/src/env/memory/plugin/memory-agent.ts +0 -365
- package/src/env/memory/plugin/memory-manager.ts +0 -198
- package/src/env/memory/plugin/memory-plugin-agent.test.ts +0 -145
- package/src/env/memory/plugin/memory-plugin.ts +0 -210
- package/src/env/memory/plugin/plugin-simplified.test.ts +0 -51
- package/src/env/memory/plugin/recall-memory.test.ts +0 -106
- package/src/env/memory/plugin/recall-memory.ts +0 -53
- package/src/env/memory/plugin/types.ts +0 -101
- package/src/env/memory/tools/memory-agent-tools.ts +0 -228
- package/src/env/memory/types.ts +0 -85
- package/src/env/paths.ts +0 -118
- package/src/env/prompt/index.ts +0 -18
- package/src/env/prompt/memory-prompts.test.ts +0 -91
- package/src/env/prompt/prompt-component.test.ts +0 -491
- package/src/env/prompt/prompt-component.ts +0 -619
- package/src/env/prompt/prompt-config-registration.test.ts +0 -213
- package/src/env/prompt/prompt-config-registration.ts +0 -39
- package/src/env/prompt/prompts-index.ts +0 -504
- package/src/env/prompt/renderer.ts +0 -67
- package/src/env/prompt/types.ts +0 -136
- package/src/env/session/hooks.ts +0 -18
- package/src/env/session/index.ts +0 -37
- package/src/env/session/search-query-parser.test.ts +0 -425
- package/src/env/session/search-query-parser.ts +0 -171
- package/src/env/session/session-checkpoint.test.ts +0 -523
- package/src/env/session/session-component.extract-recent-messages.test.ts +0 -209
- package/src/env/session/session-component.test.ts +0 -132
- package/src/env/session/session-component.ts +0 -1249
- package/src/env/session/session-config-registration.test.ts +0 -138
- package/src/env/session/session-config-registration.ts +0 -52
- package/src/env/session/session-message-converter.test.ts +0 -763
- package/src/env/session/session-message-converter.ts +0 -415
- package/src/env/session/session-message-e2e.test.ts +0 -448
- package/src/env/session/session-search.test.ts +0 -391
- package/src/env/session/session-store.test.ts +0 -362
- package/src/env/session/session-store.ts +0 -141
- package/src/env/session/storage/index.ts +0 -6
- package/src/env/session/storage/memory.ts +0 -502
- package/src/env/session/storage/sqlite.ts +0 -794
- package/src/env/session/types.ts +0 -742
- package/src/env/skill/config.ts +0 -39
- package/src/env/skill/index.ts +0 -6
- package/src/env/skill/parser.test.ts +0 -116
- package/src/env/skill/parser.ts +0 -77
- package/src/env/skill/scanner.test.ts +0 -211
- package/src/env/skill/scanner.ts +0 -119
- package/src/env/skill/skill-component.test.ts +0 -234
- package/src/env/skill/skill-component.ts +0 -352
- package/src/env/skill/skill-config-registration.test.ts +0 -60
- package/src/env/skill/skill-config-registration.ts +0 -43
- package/src/env/skill/tool/index.ts +0 -1
- package/src/env/skill/tool/skill-tool.test.ts +0 -100
- package/src/env/skill/tool/skill-tool.ts +0 -72
- package/src/env/skill/types.ts +0 -64
- package/src/env/task/delegate/delegate-tool.test.ts +0 -498
- package/src/env/task/delegate/delegate-tool.ts +0 -1014
- package/src/env/task/delegate/index.ts +0 -18
- package/src/env/task/delegate/stop-tool.test.ts +0 -140
- package/src/env/task/delegate/stop-tool.ts +0 -119
- package/src/env/task/delegate/task-events.test.ts +0 -178
- package/src/env/task/delegate/task-events.ts +0 -143
- package/src/env/task/hooks/contexts.test.ts +0 -92
- package/src/env/task/hooks/contexts.ts +0 -192
- package/src/env/task/hooks/index.ts +0 -23
- package/src/env/task/hooks/task-hook-points.test.ts +0 -32
- package/src/env/task/hooks/task-hook-points.ts +0 -54
- package/src/env/task/index.ts +0 -7
- package/src/env/task/plugins/index.ts +0 -13
- package/src/env/task/plugins/task-plugin.test.ts +0 -74
- package/src/env/task/plugins/task-plugin.ts +0 -89
- package/src/env/task/plugins/task-tag-plugin.test.ts +0 -377
- package/src/env/task/plugins/task-tag-plugin.ts +0 -319
- package/src/env/task/plugins/task-workflow-extractor.integration.test.ts +0 -226
- package/src/env/task/plugins/workflow-extractor-agent.test.ts +0 -107
- package/src/env/task/plugins/workflow-extractor-agent.ts +0 -225
- package/src/env/task/storage/index.ts +0 -6
- package/src/env/task/storage/sqlite-task-store.test.ts +0 -283
- package/src/env/task/storage/sqlite-task-store.ts +0 -903
- package/src/env/task/storage/task-search.test.ts +0 -291
- package/src/env/task/tag-service.test.ts +0 -198
- package/src/env/task/tag-service.ts +0 -264
- package/src/env/task/task-component.test.ts +0 -193
- package/src/env/task/task-component.ts +0 -658
- package/src/env/task/task-config-registration.test.ts +0 -57
- package/src/env/task/task-config-registration.ts +0 -37
- package/src/env/task/task-types.test.ts +0 -137
- package/src/env/task/tools/complete-tool.ts +0 -44
- package/src/env/task/tools/create-tool.ts +0 -49
- package/src/env/task/tools/delete-tool.ts +0 -43
- package/src/env/task/tools/get-tool.ts +0 -59
- package/src/env/task/tools/index.ts +0 -10
- package/src/env/task/tools/list-tool.ts +0 -40
- package/src/env/task/tools/operation/create-tool.ts +0 -48
- package/src/env/task/tools/operation/delete-tool.ts +0 -43
- package/src/env/task/tools/operation/get-tool.ts +0 -43
- package/src/env/task/tools/operation/index.ts +0 -9
- package/src/env/task/tools/operation/list-tool.ts +0 -40
- package/src/env/task/tools/operation/operation-tools.test.ts +0 -274
- package/src/env/task/tools/operation/operation-types.ts +0 -75
- package/src/env/task/tools/operation/update-tool.ts +0 -47
- package/src/env/task/tools/task-tools.test.ts +0 -203
- package/src/env/task/tools/task-types.test.ts +0 -75
- package/src/env/task/tools/task-types.ts +0 -68
- package/src/env/task/tools/update-tool.ts +0 -70
- package/src/env/task/types.ts +0 -160
- package/src/env/tool/built-in/bash.ts +0 -201
- package/src/env/tool/built-in/echo.ts +0 -29
- package/src/env/tool/built-in/edit-file.test.ts +0 -136
- package/src/env/tool/built-in/edit-file.ts +0 -92
- package/src/env/tool/built-in/glob.test.ts +0 -94
- package/src/env/tool/built-in/glob.ts +0 -65
- package/src/env/tool/built-in/grep.test.ts +0 -122
- package/src/env/tool/built-in/grep.ts +0 -108
- package/src/env/tool/built-in/index.ts +0 -44
- package/src/env/tool/built-in/read-file.test.ts +0 -84
- package/src/env/tool/built-in/read-file.ts +0 -75
- package/src/env/tool/built-in/write-file.test.ts +0 -119
- package/src/env/tool/built-in/write-file.ts +0 -68
- package/src/env/tool/index.ts +0 -24
- package/src/env/tool/registry.test.ts +0 -257
- package/src/env/tool/registry.ts +0 -167
- package/src/env/tool/tool-component.test.ts +0 -559
- package/src/env/tool/tool-component.ts +0 -563
- package/src/env/tool/tool-config-registration.test.ts +0 -249
- package/src/env/tool/tool-config-registration.ts +0 -46
- package/src/env/tool/types.ts +0 -267
- package/src/env/tool/validator.test.ts +0 -143
- package/src/env/tool/validator.ts +0 -44
- package/src/env/types.ts +0 -180
- package/src/env/workflow/ask-user-tool-registration.test.ts +0 -216
- package/src/env/workflow/complex-workflow.integration.test.ts +0 -1900
- package/src/env/workflow/decorators/decorator-node.ts +0 -229
- package/src/env/workflow/decorators/decorator.test.ts +0 -196
- package/src/env/workflow/decorators/edge.ts +0 -82
- package/src/env/workflow/decorators/index.ts +0 -31
- package/src/env/workflow/decorators/node-as.ts +0 -98
- package/src/env/workflow/decorators/workflow.ts +0 -54
- package/src/env/workflow/engine/dag-manager.test.ts +0 -570
- package/src/env/workflow/engine/dag-manager.ts +0 -594
- package/src/env/workflow/engine/engine.ts +0 -1422
- package/src/env/workflow/engine/event-bus.test.ts +0 -359
- package/src/env/workflow/engine/event-bus.ts +0 -156
- package/src/env/workflow/engine/executor-agent-session.test.ts +0 -84
- package/src/env/workflow/engine/executor.test.ts +0 -619
- package/src/env/workflow/engine/executor.ts +0 -593
- package/src/env/workflow/engine/index.ts +0 -24
- package/src/env/workflow/engine/node-registry.test.ts +0 -560
- package/src/env/workflow/engine/node-registry.ts +0 -289
- package/src/env/workflow/engine/resume-removed.test.ts +0 -22
- package/src/env/workflow/engine/scheduler.test.ts +0 -715
- package/src/env/workflow/engine/scheduler.ts +0 -318
- package/src/env/workflow/engine/workflow-engine.test.ts +0 -815
- package/src/env/workflow/extractor/workflow-converter.ts +0 -306
- package/src/env/workflow/fixtures.ts +0 -380
- package/src/env/workflow/index.ts +0 -38
- package/src/env/workflow/integration/run-resume-unified.test.ts +0 -186
- package/src/env/workflow/integration/service-integration.test.ts +0 -267
- package/src/env/workflow/metadata/keys.ts +0 -12
- package/src/env/workflow/nodes/agent-component-adapter.test.ts +0 -318
- package/src/env/workflow/nodes/agent-component-adapter.ts +0 -448
- package/src/env/workflow/nodes/agent-node.test.ts +0 -371
- package/src/env/workflow/nodes/agent-node.ts +0 -598
- package/src/env/workflow/nodes/ask-user-node.ts +0 -113
- package/src/env/workflow/nodes/condition-node.ts +0 -200
- package/src/env/workflow/nodes/index.ts +0 -9
- package/src/env/workflow/nodes/merge-node.ts +0 -141
- package/src/env/workflow/nodes/skill-node.test.ts +0 -253
- package/src/env/workflow/nodes/skill-node.ts +0 -393
- package/src/env/workflow/nodes/tool-node.test.ts +0 -251
- package/src/env/workflow/nodes/tool-node.ts +0 -493
- package/src/env/workflow/nodes/workflow-llm-history.test.ts +0 -455
- package/src/env/workflow/nodes/workflow-node.test.ts +0 -315
- package/src/env/workflow/nodes/workflow-node.ts +0 -311
- package/src/env/workflow/service/index.ts +0 -27
- package/src/env/workflow/service/registry.test.ts +0 -133
- package/src/env/workflow/service/registry.ts +0 -71
- package/src/env/workflow/service/workflow-service.test.ts +0 -310
- package/src/env/workflow/service/workflow-service.ts +0 -393
- package/src/env/workflow/storage/index.ts +0 -28
- package/src/env/workflow/storage/mock-repositories.ts +0 -385
- package/src/env/workflow/storage/sqlite.test.ts +0 -179
- package/src/env/workflow/storage/sqlite.ts +0 -163
- package/src/env/workflow/storage/workflow-repo.test.ts +0 -780
- package/src/env/workflow/storage/workflow-repo.ts +0 -342
- package/src/env/workflow/tools/ask-user-tool.ts +0 -82
- package/src/env/workflow/tools/index.ts +0 -26
- package/src/env/workflow/tools/run-workflow.test.ts +0 -352
- package/src/env/workflow/tools/run-workflow.ts +0 -214
- package/src/env/workflow/types/context.ts +0 -18
- package/src/env/workflow/types/decorators-types.ts +0 -198
- package/src/env/workflow/types/event.test.ts +0 -515
- package/src/env/workflow/types/event.ts +0 -193
- package/src/env/workflow/types/index.ts +0 -49
- package/src/env/workflow/types/run.test.ts +0 -437
- package/src/env/workflow/types/run.ts +0 -173
- package/src/env/workflow/types/workflow-hil.ts +0 -114
- package/src/env/workflow/types/workflow-message.test.ts +0 -138
- package/src/env/workflow/types/workflow-message.ts +0 -196
- package/src/env/workflow/types/workflow-session.test.ts +0 -95
- package/src/env/workflow/types/workflow-session.ts +0 -59
- package/src/env/workflow/types/workflow.test.ts +0 -495
- package/src/env/workflow/types/workflow.ts +0 -195
- package/src/env/workflow/types_compat.ts +0 -51
- package/src/env/workflow/utils/create-workflow.ts +0 -47
- package/src/env/workflow/utils/execution-state.ts +0 -245
- package/src/env/workflow/utils/index.ts +0 -18
- package/src/env/workflow/utils/node-registry-helper.ts +0 -58
- package/src/env/workflow/utils/recovery-validator.test.ts +0 -460
- package/src/env/workflow/utils/recovery-validator.ts +0 -377
- package/src/env/workflow/utils/session-parser.test.ts +0 -111
- package/src/env/workflow/utils/session-parser.ts +0 -94
- package/src/env/workflow/utils/session-recovery.test.ts +0 -334
- package/src/env/workflow/utils/session-recovery.ts +0 -188
- package/src/env/workflow/utils/template-resolver.test.ts +0 -258
- package/src/env/workflow/utils/template-resolver.ts +0 -436
- package/src/env/workflow/utils/validation-rules.ts +0 -149
- package/src/env/workflow/workflow-component.ts +0 -544
- package/src/index.ts +0 -422
- package/src/utils/id.ts +0 -21
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
import { Node, NodeDefinition, NodeExecutionContext, NodeExecutionResult } from '../types';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Skill Registry interface for looking up skills
|
|
5
|
-
*/
|
|
6
|
-
export interface SkillRegistry {
|
|
7
|
-
getSkill(name: string): Skill | undefined;
|
|
8
|
-
hasSkill(name: string): boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Skill interface for invocation
|
|
13
|
-
*/
|
|
14
|
-
export interface Skill {
|
|
15
|
-
name: string;
|
|
16
|
-
invoke(input: any, context: any): Promise<any>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* SkillNode invokes a registered Skill with the provided input.
|
|
21
|
-
*
|
|
22
|
-
* Input can contain template strings like {{nodeId.output.path}} that will be
|
|
23
|
-
* resolved from previousOutputs.
|
|
24
|
-
*/
|
|
25
|
-
export class SkillNode implements Node {
|
|
26
|
-
readonly type = 'skill';
|
|
27
|
-
readonly id: string;
|
|
28
|
-
|
|
29
|
-
constructor(
|
|
30
|
-
private definition: NodeDefinition,
|
|
31
|
-
private skillRegistry: SkillRegistry
|
|
32
|
-
) {
|
|
33
|
-
this.id = definition.id;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async execute(context: NodeExecutionContext): Promise<NodeExecutionResult> {
|
|
37
|
-
const startTime = Date.now();
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
// 1. Get skill name from definition.config.skill
|
|
41
|
-
const skillName = this.definition.config?.skill as string | undefined;
|
|
42
|
-
|
|
43
|
-
if (!skillName) {
|
|
44
|
-
throw new Error('Skill name is required. Please specify config.skill in the node definition.');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 2. Look up skill from registry
|
|
48
|
-
const skill = this.skillRegistry.getSkill(skillName);
|
|
49
|
-
|
|
50
|
-
if (!skill) {
|
|
51
|
-
throw new Error(`Skill not found: ${skillName}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 3. Prepare input (may reference previous outputs and workflow input)
|
|
55
|
-
const input = this.definition.config?.input ?? {};
|
|
56
|
-
const resolvedInput = this.resolveTemplateReferences(input, context.previousOutputs, context.input as any);
|
|
57
|
-
|
|
58
|
-
// 4. Prepare context for skill invocation
|
|
59
|
-
const skillContext = {
|
|
60
|
-
runId: context.runId,
|
|
61
|
-
workflowName: context.workflowName,
|
|
62
|
-
nodeId: context.nodeId,
|
|
63
|
-
debug: context.debug,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// 5. Invoke skill
|
|
67
|
-
const output = await skill.invoke(resolvedInput, skillContext);
|
|
68
|
-
|
|
69
|
-
// 6. Return result
|
|
70
|
-
return {
|
|
71
|
-
output,
|
|
72
|
-
error: undefined,
|
|
73
|
-
durationMs: Date.now() - startTime,
|
|
74
|
-
};
|
|
75
|
-
} catch (error) {
|
|
76
|
-
return {
|
|
77
|
-
output: undefined,
|
|
78
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
79
|
-
durationMs: Date.now() - startTime,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Resolve template references in input values.
|
|
86
|
-
* Templates can be in the format:
|
|
87
|
-
* - {{input.key}} - from workflow input
|
|
88
|
-
* - {{nodeId}} - entire previous node output
|
|
89
|
-
* - {{nodeId.path}} - nested path within previous node output
|
|
90
|
-
* - {{nodes.nodeId}} - entire previous node output (alias)
|
|
91
|
-
* - {{nodes.nodeId.path}} - nested path within previous node output
|
|
92
|
-
*
|
|
93
|
-
* Key behavior for template strings:
|
|
94
|
-
* - If the string is a pure template reference (e.g., "{{nodes.fetch}}"),
|
|
95
|
-
* the resolved value is returned as-is (preserving objects/arrays)
|
|
96
|
-
* - If the string contains text + templates (e.g., "Hello {{name}}"),
|
|
97
|
-
* returns a string with interpolated values
|
|
98
|
-
*
|
|
99
|
-
* @param input - The input object that may contain template strings
|
|
100
|
-
* @param previousOutputs - Map of previous node outputs
|
|
101
|
-
* @param workflowInput - The workflow input parameters
|
|
102
|
-
* @returns The input with template references resolved
|
|
103
|
-
*/
|
|
104
|
-
private resolveTemplateReferences(
|
|
105
|
-
input: any,
|
|
106
|
-
previousOutputs: Map<string, any>,
|
|
107
|
-
workflowInput?: Record<string, unknown>
|
|
108
|
-
): any {
|
|
109
|
-
if (typeof input === 'string') {
|
|
110
|
-
// Check if this is a pure template reference (no surrounding text)
|
|
111
|
-
const pureTemplate = this.extractPureTemplate(input);
|
|
112
|
-
if (pureTemplate) {
|
|
113
|
-
// Pure template reference - resolve and return value as-is (preserve objects/arrays)
|
|
114
|
-
return this.resolvePureTemplate(pureTemplate, previousOutputs, workflowInput);
|
|
115
|
-
}
|
|
116
|
-
// Mixed text + templates - return resolved string
|
|
117
|
-
return this.resolveStringTemplate(input, previousOutputs, workflowInput);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (Array.isArray(input)) {
|
|
121
|
-
return input.map(item => this.resolveTemplateReferences(item, previousOutputs, workflowInput));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (input !== null && typeof input === 'object') {
|
|
125
|
-
const resolved: Record<string, any> = {};
|
|
126
|
-
for (const [key, value] of Object.entries(input)) {
|
|
127
|
-
resolved[key] = this.resolveTemplateReferences(value, previousOutputs, workflowInput);
|
|
128
|
-
}
|
|
129
|
-
return resolved;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Return primitive values as-is
|
|
133
|
-
return input;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Check if a string is a pure template reference (starts with {{ and ends with }})
|
|
138
|
-
* @param str - String to check
|
|
139
|
-
* @returns The template content if pure, null otherwise
|
|
140
|
-
*/
|
|
141
|
-
private extractPureTemplate(str: string): string | null {
|
|
142
|
-
const trimmed = str.trim();
|
|
143
|
-
if (trimmed.startsWith('{{') && trimmed.endsWith('}}') && trimmed.length > 4) {
|
|
144
|
-
// Extract content between {{ and }}
|
|
145
|
-
const content = trimmed.slice(2, -2).trim();
|
|
146
|
-
// Check there's no other text (except the template patterns themselves)
|
|
147
|
-
if (!content.includes('{{') && !content.includes('}}')) {
|
|
148
|
-
return content;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Resolve a pure template reference and return the value as-is.
|
|
156
|
-
* @param templateContent - Content between {{ and }}
|
|
157
|
-
* @param previousOutputs - Map of previous node outputs
|
|
158
|
-
* @param workflowInput - The workflow input parameters
|
|
159
|
-
* @returns The resolved value (may be object, array, or primitive),
|
|
160
|
-
* or the original template string if unresolved
|
|
161
|
-
*/
|
|
162
|
-
private resolvePureTemplate(
|
|
163
|
-
templateContent: string,
|
|
164
|
-
previousOutputs: Map<string, any>,
|
|
165
|
-
workflowInput?: Record<string, unknown>
|
|
166
|
-
): any {
|
|
167
|
-
const trimmed = templateContent.trim();
|
|
168
|
-
const originalTemplate = `{{${templateContent}}}`;
|
|
169
|
-
|
|
170
|
-
// Pattern 1: {{input.key}} or {{input.key.nested}}
|
|
171
|
-
if (trimmed.startsWith('input.')) {
|
|
172
|
-
const value = this.getNestedValue(workflowInput, trimmed.slice(6));
|
|
173
|
-
return value !== undefined ? value : originalTemplate;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Pattern 2: {{nodes.nodeId}} or {{nodes.nodeId.output}}
|
|
177
|
-
if (trimmed.startsWith('nodes.')) {
|
|
178
|
-
const path = trimmed.slice(6);
|
|
179
|
-
const segments = path.split('.');
|
|
180
|
-
const nodeId = segments[0];
|
|
181
|
-
const restPath = segments.slice(1).join('.') || undefined;
|
|
182
|
-
const value = this.resolvePath(nodeId, restPath, previousOutputs);
|
|
183
|
-
return value !== undefined ? value : originalTemplate;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Pattern 3: Bare nodeId (backward compatibility)
|
|
187
|
-
if (trimmed !== 'input' && trimmed !== 'nodes') {
|
|
188
|
-
const segments = trimmed.split('.');
|
|
189
|
-
const nodeId = segments[0];
|
|
190
|
-
const restPath = segments.slice(1).join('.') || undefined;
|
|
191
|
-
const value = this.resolvePath(nodeId, restPath, previousOutputs);
|
|
192
|
-
return value !== undefined ? value : originalTemplate;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return originalTemplate;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Resolve template references in a single string (for mixed text + templates).
|
|
200
|
-
* Supports formats:
|
|
201
|
-
* - {{input.key}} - from workflow input
|
|
202
|
-
* - {{nodeId}} - entire previous node output
|
|
203
|
-
* - {{nodeId.path}} - nested path within previous node output
|
|
204
|
-
* - {{nodes.nodeId}} - entire previous node output (alias)
|
|
205
|
-
* - {{nodes.nodeId.path}} - nested path within previous node output
|
|
206
|
-
*
|
|
207
|
-
* Note: Returns string because the input contains text around the template.
|
|
208
|
-
* For pure template references, use resolvePureTemplate instead.
|
|
209
|
-
*/
|
|
210
|
-
private resolveStringTemplate(
|
|
211
|
-
template: string,
|
|
212
|
-
previousOutputs: Map<string, any>,
|
|
213
|
-
workflowInput?: Record<string, unknown>
|
|
214
|
-
): string {
|
|
215
|
-
let resolved = template;
|
|
216
|
-
|
|
217
|
-
// Pattern 1: {{input.key}} or {{input.key.path}}
|
|
218
|
-
resolved = resolved.replace(/\{\{input\.([^}]+)\}\}/g, (match, path) => {
|
|
219
|
-
const value = this.getNestedValue(workflowInput, path.trim());
|
|
220
|
-
return this.stringifyValue(value, match);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// Pattern 2: {{nodeId}} or {{nodeId.path}} (bare nodeId without prefix)
|
|
224
|
-
resolved = resolved.replace(/\{\{([^:.{}][^}]*?)\}\}/g, (match, path) => {
|
|
225
|
-
// Skip if it starts with 'nodes.' or 'input.'
|
|
226
|
-
if (path.trim().startsWith('nodes.') || path.trim().startsWith('input.')) {
|
|
227
|
-
return match;
|
|
228
|
-
}
|
|
229
|
-
// Split into nodeId and optional path
|
|
230
|
-
const segments = path.trim().split('.');
|
|
231
|
-
const nodeId = segments[0];
|
|
232
|
-
const restPath = segments.slice(1).join('.') || undefined;
|
|
233
|
-
const value = this.resolvePath(nodeId, restPath, previousOutputs);
|
|
234
|
-
return this.stringifyValue(value, match);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// Pattern 3: {{nodes.nodeId}} or {{nodes.nodeId.path}}
|
|
238
|
-
resolved = resolved.replace(/\{\{nodes\.([^}]+)\}\}/g, (match, path) => {
|
|
239
|
-
const segments = path.trim().split('.');
|
|
240
|
-
const nodeId = segments[0];
|
|
241
|
-
const restPath = segments.slice(1).join('.') || undefined;
|
|
242
|
-
const value = this.resolvePath(nodeId, restPath, previousOutputs);
|
|
243
|
-
return this.stringifyValue(value, match);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
return resolved;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Resolve a value from previous outputs.
|
|
251
|
-
* @param nodeId - The node ID to look up
|
|
252
|
-
* @param path - Optional dot-notation path within the node output
|
|
253
|
-
* @param previousOutputs - Map of previous node outputs
|
|
254
|
-
*/
|
|
255
|
-
private resolvePath(nodeId: string, path: string | undefined, previousOutputs: Map<string, any>): any {
|
|
256
|
-
// Try exact match first, then try normalized versions (hyphen <-> underscore)
|
|
257
|
-
let nodeOutput = previousOutputs.get(nodeId);
|
|
258
|
-
|
|
259
|
-
if (nodeOutput === undefined) {
|
|
260
|
-
// Try normalized versions for flexibility
|
|
261
|
-
const normalizedUnderscore = nodeId.replace(/-/g, '_');
|
|
262
|
-
const normalizedHyphen = nodeId.replace(/_/g, '-');
|
|
263
|
-
|
|
264
|
-
if (normalizedUnderscore !== nodeId) {
|
|
265
|
-
nodeOutput = previousOutputs.get(normalizedUnderscore);
|
|
266
|
-
}
|
|
267
|
-
if (nodeOutput === undefined && normalizedHyphen !== nodeId) {
|
|
268
|
-
nodeOutput = previousOutputs.get(normalizedHyphen);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (nodeOutput === undefined) {
|
|
273
|
-
return undefined;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Extract from wrapper formats (DecoratorNode: {success, output} and AgentNode: {result, metadata})
|
|
277
|
-
let value = this.extractFromWrapper(nodeOutput);
|
|
278
|
-
|
|
279
|
-
// If no path specified, return the extracted value
|
|
280
|
-
if (!path) {
|
|
281
|
-
return value;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Navigate the path within the node's output
|
|
285
|
-
// Skip first segment if it's a wrapper property since extractFromWrapper already extracted it
|
|
286
|
-
// (e.g., template {{node-0.output.value}} -> path="output.value" -> skip "output")
|
|
287
|
-
let segments = path.split('.');
|
|
288
|
-
if (segments.length > 1 &&
|
|
289
|
-
(segments[0] === 'output' || segments[0] === 'result' || segments[0] === 'metadata')) {
|
|
290
|
-
segments = segments.slice(1);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
let current: any = value;
|
|
294
|
-
|
|
295
|
-
for (const segment of segments) {
|
|
296
|
-
if (current === null || current === undefined) {
|
|
297
|
-
return undefined;
|
|
298
|
-
}
|
|
299
|
-
current = current[segment];
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return current;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Extract the actual output value from wrapper formats.
|
|
307
|
-
*
|
|
308
|
-
* Handles:
|
|
309
|
-
* - AgentNode format: { result, metadata } -> returns result
|
|
310
|
-
* - DecoratorNode format: { success, output, ... } -> returns output
|
|
311
|
-
* - Other formats: returns value as-is
|
|
312
|
-
*
|
|
313
|
-
* @param value - The value to extract from
|
|
314
|
-
* @returns The extracted value
|
|
315
|
-
*/
|
|
316
|
-
private extractFromWrapper(value: unknown): unknown {
|
|
317
|
-
if (value === null || value === undefined) {
|
|
318
|
-
return value;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (typeof value !== 'object') {
|
|
322
|
-
return value;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Check for AgentNode wrapper: { result, metadata }
|
|
326
|
-
// This is the signature of AgentNode output
|
|
327
|
-
if ('result' in value && 'metadata' in value) {
|
|
328
|
-
return (value as { result: unknown }).result;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Check for DecoratorNode wrapper: { success, output, ... }
|
|
332
|
-
// This has 'output' but NOT 'result' (that's AgentNode's signature)
|
|
333
|
-
if ('output' in value) {
|
|
334
|
-
return (value as { output: unknown }).output;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return value;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Get nested value from an object using dot-notation path.
|
|
342
|
-
*
|
|
343
|
-
* Special handling: If the object has an 'input' key and we're looking for a path that doesn't
|
|
344
|
-
* exist at the top level, check inside the 'input' key.
|
|
345
|
-
* This allows {{input.city}} to work when workflowInput is wrapped as {input: {city: "..."}}
|
|
346
|
-
*/
|
|
347
|
-
private getNestedValue(obj: Record<string, unknown> | undefined, path: string): unknown {
|
|
348
|
-
if (!obj) {
|
|
349
|
-
return undefined;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Special case: If obj has an 'input' key, we might be accessing a key inside it
|
|
353
|
-
// This happens when createExecutionContext wraps globalInput as {input: globalInput}
|
|
354
|
-
if ('input' in obj && path in (obj['input'] as Record<string, unknown>)) {
|
|
355
|
-
// Path exists inside 'input', so look there
|
|
356
|
-
return (obj['input'] as Record<string, unknown>)[path];
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Normal case: path.split('.').reduce(...) with obj as initial value
|
|
360
|
-
// This allows {{input.city}} to work when path = "city" and obj = {input: {city: "..."}}
|
|
361
|
-
return path.split('.').reduce((current: unknown, key: string) => {
|
|
362
|
-
if (current && typeof current === 'object') {
|
|
363
|
-
return (current as Record<string, unknown>)[key];
|
|
364
|
-
}
|
|
365
|
-
return undefined;
|
|
366
|
-
}, obj as unknown);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Convert a value to string for template replacement.
|
|
371
|
-
* - If value is undefined, return the original template
|
|
372
|
-
* - If value is a string, return it directly
|
|
373
|
-
* - If value is null, return 'null'
|
|
374
|
-
* - If value is an object or array, serialize to JSON
|
|
375
|
-
*
|
|
376
|
-
* @param value - The value to stringify
|
|
377
|
-
* @param originalTemplate - The original template string (returned if value is undefined)
|
|
378
|
-
* @returns String representation of the value
|
|
379
|
-
*/
|
|
380
|
-
private stringifyValue(value: unknown, originalTemplate: string): string {
|
|
381
|
-
if (value === undefined) {
|
|
382
|
-
return originalTemplate;
|
|
383
|
-
}
|
|
384
|
-
if (value === null) {
|
|
385
|
-
return 'null';
|
|
386
|
-
}
|
|
387
|
-
if (typeof value === 'string') {
|
|
388
|
-
return value;
|
|
389
|
-
}
|
|
390
|
-
// For objects and arrays, serialize to JSON
|
|
391
|
-
return JSON.stringify(value);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, jest } from 'vitest';
|
|
2
|
-
import { ToolNode, ToolRegistry, Tool } from './tool-node';
|
|
3
|
-
import type { NodeDefinition, NodeExecutionContext } from '../types';
|
|
4
|
-
import type { EventBus } from '../engine/event-bus';
|
|
5
|
-
|
|
6
|
-
// Mock EventBus
|
|
7
|
-
const mockEventBus = {
|
|
8
|
-
emit: jest.fn(),
|
|
9
|
-
on: jest.fn(),
|
|
10
|
-
off: jest.fn(),
|
|
11
|
-
} as Partial<EventBus> as EventBus;
|
|
12
|
-
|
|
13
|
-
// Mock ToolRegistry
|
|
14
|
-
const mockRegistry = {
|
|
15
|
-
getTool: jest.fn<(name: string) => Tool | undefined>(),
|
|
16
|
-
hasTool: jest.fn<(name: string) => boolean>(),
|
|
17
|
-
} as {
|
|
18
|
-
getTool: ReturnType<typeof jest.fn>;
|
|
19
|
-
hasTool: ReturnType<typeof jest.fn>;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// Mock Tool
|
|
23
|
-
const mockTool = {
|
|
24
|
-
name: 'test-tool',
|
|
25
|
-
execute: jest.fn<(input: any) => Promise<any>>(),
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
describe('ToolNode', () => {
|
|
29
|
-
let toolNode: ToolNode;
|
|
30
|
-
let nodeDefinition: NodeDefinition;
|
|
31
|
-
let executionContext: NodeExecutionContext;
|
|
32
|
-
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
jest.clearAllMocks();
|
|
35
|
-
|
|
36
|
-
nodeDefinition = {
|
|
37
|
-
id: 'node-1',
|
|
38
|
-
type: 'tool',
|
|
39
|
-
name: 'Test Tool Node',
|
|
40
|
-
config: {
|
|
41
|
-
tool: 'test-tool',
|
|
42
|
-
input: { message: 'hello' },
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
executionContext = {
|
|
47
|
-
runId: 'run-1',
|
|
48
|
-
workflowName: 'test-workflow',
|
|
49
|
-
nodeId: 'node-1',
|
|
50
|
-
input: {},
|
|
51
|
-
previousOutputs: new Map(),
|
|
52
|
-
config: nodeDefinition.config,
|
|
53
|
-
debug: false,
|
|
54
|
-
eventBus: mockEventBus,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
mockRegistry.getTool = jest.fn().mockReturnValue(mockTool);
|
|
58
|
-
mockRegistry.hasTool = jest.fn().mockReturnValue(true);
|
|
59
|
-
mockTool.execute = jest.fn().mockResolvedValue({ result: 'success' });
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe('constructor', () => {
|
|
63
|
-
it('should create a ToolNode with correct type', () => {
|
|
64
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
65
|
-
expect(toolNode.type).toBe('tool');
|
|
66
|
-
expect(toolNode.id).toBe('node-1');
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('execute', () => {
|
|
71
|
-
it('should execute tool successfully', async () => {
|
|
72
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
73
|
-
|
|
74
|
-
const result = await toolNode.execute(executionContext);
|
|
75
|
-
|
|
76
|
-
expect(result.output).toEqual({ result: 'success' });
|
|
77
|
-
expect(result.error).toBeUndefined();
|
|
78
|
-
expect(result.durationMs).toBeGreaterThanOrEqual(0);
|
|
79
|
-
expect(mockRegistry.getTool).toHaveBeenCalledWith('test-tool');
|
|
80
|
-
// Tool is called with (input, context)
|
|
81
|
-
expect(mockTool.execute).toHaveBeenCalledWith(
|
|
82
|
-
{ message: 'hello' },
|
|
83
|
-
expect.objectContaining({
|
|
84
|
-
session_id: expect.any(String),
|
|
85
|
-
})
|
|
86
|
-
);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should return error when tool not found', async () => {
|
|
90
|
-
mockRegistry.getTool = jest.fn().mockReturnValue(undefined);
|
|
91
|
-
mockRegistry.hasTool = jest.fn().mockReturnValue(false);
|
|
92
|
-
|
|
93
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
94
|
-
|
|
95
|
-
const result = await toolNode.execute(executionContext);
|
|
96
|
-
|
|
97
|
-
expect(result.output).toBeUndefined();
|
|
98
|
-
expect(result.error).toBeDefined();
|
|
99
|
-
expect(result.error.message).toContain('Tool not found: test-tool');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should pass input to tool', async () => {
|
|
103
|
-
const customInput = { param1: 'value1', param2: 123 };
|
|
104
|
-
nodeDefinition.config.input = customInput;
|
|
105
|
-
executionContext.config = nodeDefinition.config;
|
|
106
|
-
|
|
107
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
108
|
-
|
|
109
|
-
await toolNode.execute(executionContext);
|
|
110
|
-
|
|
111
|
-
expect(mockTool.execute).toHaveBeenCalledWith(
|
|
112
|
-
customInput,
|
|
113
|
-
expect.objectContaining({
|
|
114
|
-
session_id: expect.any(String),
|
|
115
|
-
})
|
|
116
|
-
);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should handle tool execution errors', async () => {
|
|
120
|
-
const error = new Error('Tool execution failed');
|
|
121
|
-
mockTool.execute = jest.fn().mockRejectedValue(error);
|
|
122
|
-
|
|
123
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
124
|
-
|
|
125
|
-
const result = await toolNode.execute(executionContext);
|
|
126
|
-
|
|
127
|
-
expect(result.output).toBeUndefined();
|
|
128
|
-
expect(result.error).toBeDefined();
|
|
129
|
-
expect(result.error.message).toBe('Tool execution failed');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should resolve template references from previous outputs', async () => {
|
|
133
|
-
// Set up previous outputs
|
|
134
|
-
executionContext.previousOutputs.set('node-0', {
|
|
135
|
-
value: 'from-node-0'
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Input contains template reference with nodes. prefix
|
|
139
|
-
nodeDefinition.config.input = {
|
|
140
|
-
message: '{{nodes.node-0.value}}',
|
|
141
|
-
};
|
|
142
|
-
executionContext.config = nodeDefinition.config;
|
|
143
|
-
|
|
144
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
145
|
-
|
|
146
|
-
const result = await toolNode.execute(executionContext);
|
|
147
|
-
|
|
148
|
-
expect(result.output).toEqual({ result: 'success' });
|
|
149
|
-
expect(mockTool.execute).toHaveBeenCalledWith({
|
|
150
|
-
message: 'from-node-0',
|
|
151
|
-
}, expect.any(Object));
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('should resolve nested template references', async () => {
|
|
155
|
-
executionContext.previousOutputs.set('data-node', {
|
|
156
|
-
nested: {
|
|
157
|
-
deep: {
|
|
158
|
-
value: 'nested-value',
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
nodeDefinition.config.input = {
|
|
164
|
-
message: '{{nodes.data-node.nested.deep.value}}',
|
|
165
|
-
};
|
|
166
|
-
executionContext.config = nodeDefinition.config;
|
|
167
|
-
|
|
168
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
169
|
-
|
|
170
|
-
await toolNode.execute(executionContext);
|
|
171
|
-
|
|
172
|
-
expect(mockTool.execute).toHaveBeenCalledWith(
|
|
173
|
-
{ message: 'nested-value' },
|
|
174
|
-
expect.objectContaining({
|
|
175
|
-
session_id: expect.any(String),
|
|
176
|
-
})
|
|
177
|
-
);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should handle unresolved template references gracefully', async () => {
|
|
181
|
-
// Template references non-existent node
|
|
182
|
-
nodeDefinition.config.input = {
|
|
183
|
-
message: '{{non-existent.output.value}}',
|
|
184
|
-
};
|
|
185
|
-
executionContext.config = nodeDefinition.config;
|
|
186
|
-
|
|
187
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
188
|
-
|
|
189
|
-
const result = await toolNode.execute(executionContext);
|
|
190
|
-
|
|
191
|
-
// Should still execute with unresolved template
|
|
192
|
-
expect(mockTool.execute).toHaveBeenCalledWith(
|
|
193
|
-
{ message: '{{non-existent.output.value}}' },
|
|
194
|
-
expect.any(Object)
|
|
195
|
-
);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('should record execution duration', async () => {
|
|
199
|
-
mockTool.execute = jest.fn().mockImplementation(async () => {
|
|
200
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
201
|
-
return { result: 'success' };
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
205
|
-
|
|
206
|
-
const result = await toolNode.execute(executionContext);
|
|
207
|
-
|
|
208
|
-
expect(result.durationMs).toBeGreaterThanOrEqual(0);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it('should use tool name from config.tool', async () => {
|
|
212
|
-
nodeDefinition.config.tool = 'custom-tool';
|
|
213
|
-
executionContext.config = nodeDefinition.config;
|
|
214
|
-
|
|
215
|
-
mockRegistry.getTool = jest.fn().mockReturnValue({
|
|
216
|
-
name: 'custom-tool',
|
|
217
|
-
execute: jest.fn().mockResolvedValue({ custom: true }),
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
221
|
-
|
|
222
|
-
await toolNode.execute(executionContext);
|
|
223
|
-
|
|
224
|
-
expect(mockRegistry.getTool).toHaveBeenCalledWith('custom-tool');
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
it('should throw error when tool config is missing', async () => {
|
|
228
|
-
nodeDefinition.config = {}; // No tool specified
|
|
229
|
-
executionContext.config = nodeDefinition.config;
|
|
230
|
-
|
|
231
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
232
|
-
|
|
233
|
-
const result = await toolNode.execute(executionContext);
|
|
234
|
-
|
|
235
|
-
expect(result.error).toBeDefined();
|
|
236
|
-
expect(result.error.message).toContain('Tool name is required');
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
describe('ToolRegistry interface', () => {
|
|
241
|
-
it('should check tool existence with hasTool', async () => {
|
|
242
|
-
mockRegistry.hasTool = jest.fn().mockReturnValue(true);
|
|
243
|
-
|
|
244
|
-
toolNode = new ToolNode(nodeDefinition, mockRegistry);
|
|
245
|
-
|
|
246
|
-
// This is tested implicitly - if hasTool returns false,
|
|
247
|
-
// getTool should also return undefined
|
|
248
|
-
expect(mockRegistry.hasTool('test-tool')).toBe(true);
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
});
|