foliko 1.0.75 → 1.0.76

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 (88) hide show
  1. package/.claude/settings.local.json +159 -157
  2. package/cli/bin/foliko.js +12 -12
  3. package/cli/src/commands/chat.js +143 -143
  4. package/cli/src/commands/list.js +93 -93
  5. package/cli/src/index.js +75 -75
  6. package/cli/src/ui/chat-ui.js +201 -201
  7. package/cli/src/utils/ansi.js +40 -40
  8. package/cli/src/utils/markdown.js +292 -292
  9. package/examples/ambient-example.js +194 -194
  10. package/examples/basic.js +115 -115
  11. package/examples/bootstrap.js +121 -121
  12. package/examples/mcp-example.js +56 -56
  13. package/examples/skill-example.js +49 -49
  14. package/examples/test-chat.js +137 -137
  15. package/examples/test-mcp.js +85 -85
  16. package/examples/test-reload.js +59 -59
  17. package/examples/test-telegram.js +50 -50
  18. package/examples/test-tg-bot.js +45 -45
  19. package/examples/test-tg-simple.js +47 -47
  20. package/examples/test-tg.js +62 -62
  21. package/examples/test-think.js +43 -43
  22. package/examples/test-web-plugin.js +103 -103
  23. package/examples/test-weixin-feishu.js +103 -103
  24. package/examples/workflow.js +158 -158
  25. package/package.json +1 -1
  26. package/plugins/ai-plugin.js +102 -102
  27. package/plugins/ambient-agent/EventWatcher.js +113 -113
  28. package/plugins/ambient-agent/ExplorerLoop.js +640 -640
  29. package/plugins/ambient-agent/GoalManager.js +197 -197
  30. package/plugins/ambient-agent/Reflector.js +95 -95
  31. package/plugins/ambient-agent/StateStore.js +90 -90
  32. package/plugins/ambient-agent/constants.js +101 -101
  33. package/plugins/ambient-agent/index.js +579 -579
  34. package/plugins/audit-plugin.js +187 -187
  35. package/plugins/default-plugins.js +662 -662
  36. package/plugins/email/constants.js +64 -64
  37. package/plugins/email/handlers.js +461 -461
  38. package/plugins/email/index.js +278 -278
  39. package/plugins/email/monitor.js +269 -269
  40. package/plugins/email/parser.js +138 -138
  41. package/plugins/email/reply.js +151 -151
  42. package/plugins/email/utils.js +124 -124
  43. package/plugins/feishu-plugin.js +481 -481
  44. package/plugins/file-system-plugin.js +826 -826
  45. package/plugins/install-plugin.js +199 -199
  46. package/plugins/python-executor-plugin.js +367 -367
  47. package/plugins/python-plugin-loader.js +481 -481
  48. package/plugins/rules-plugin.js +294 -294
  49. package/plugins/scheduler-plugin.js +691 -691
  50. package/plugins/session-plugin.js +369 -369
  51. package/plugins/shell-executor-plugin.js +197 -197
  52. package/plugins/storage-plugin.js +240 -240
  53. package/plugins/subagent-plugin.js +845 -845
  54. package/plugins/telegram-plugin.js +482 -482
  55. package/plugins/think-plugin.js +345 -345
  56. package/plugins/tools-plugin.js +196 -196
  57. package/plugins/web-plugin.js +606 -606
  58. package/plugins/weixin-plugin.js +545 -545
  59. package/src/capabilities/index.js +11 -11
  60. package/src/capabilities/skill-manager.js +609 -609
  61. package/src/capabilities/workflow-engine.js +1109 -1109
  62. package/src/core/agent-chat.js +882 -882
  63. package/src/core/agent.js +892 -892
  64. package/src/core/framework.js +465 -465
  65. package/src/core/index.js +19 -19
  66. package/src/core/plugin-base.js +219 -219
  67. package/src/core/plugin-manager.js +863 -863
  68. package/src/core/provider.js +114 -114
  69. package/src/core/sub-agent-config.js +264 -264
  70. package/src/core/system-prompt-builder.js +120 -120
  71. package/src/core/tool-registry.js +517 -517
  72. package/src/core/tool-router.js +297 -297
  73. package/src/executors/executor-base.js +58 -58
  74. package/src/executors/mcp-executor.js +741 -741
  75. package/src/index.js +25 -25
  76. package/src/utils/circuit-breaker.js +301 -301
  77. package/src/utils/error-boundary.js +363 -363
  78. package/src/utils/error.js +374 -374
  79. package/src/utils/event-emitter.js +97 -97
  80. package/src/utils/id.js +133 -133
  81. package/src/utils/index.js +217 -217
  82. package/src/utils/logger.js +181 -181
  83. package/src/utils/plugin-helpers.js +90 -90
  84. package/src/utils/retry.js +122 -122
  85. package/src/utils/sandbox.js +292 -292
  86. package/test/tool-registry-validation.test.js +218 -218
  87. package/website/script.js +136 -136
  88. package/foliko-1.0.75.tgz +0 -0
@@ -1,1109 +1,1109 @@
1
- /**
2
- * WorkflowEngine 工作流引擎
3
- * 支持结构化工作流定义和执行
4
- */
5
- const { EventEmitter } = require('../utils/event-emitter');
6
- const { Plugin } = require('../core/plugin-base');
7
- const { logger } = require('../utils/logger');
8
- const log = logger.child('Workflow');
9
- const { z } = require('zod');
10
- const { runScriptSafely, evaluateInSandbox, runWorkflowFileSafely, runWorkflowSafely } = require('../utils/sandbox');
11
- const fs = require('fs');
12
- const crypto = require('crypto');
13
- const path = require('path');
14
- /**
15
- * 工作流步骤类型
16
- */
17
- const StepType = {
18
- AGENT: 'agent',
19
- SCRIPT: 'script',
20
- CONDITION: 'condition',
21
- SWITCH: 'switch',
22
- TRY: 'try',
23
- PARALLEL: 'parallel',
24
- SEQUENTIAL: 'sequential',
25
- LOOP: 'loop',
26
- DELAY: 'delay',
27
- TOOL: 'tool',
28
- INPUT: 'input',
29
- OUTPUT: 'output',
30
- MESSAGE: 'message',
31
- THINK: 'think',
32
- WORKFLOW: 'workflow', // 嵌套工作流
33
- };
34
- /**
35
- * StepExecutor - 统一的步骤执行器
36
- * 同时被 WorkflowEngine 和 ExplorerLoop 使用
37
- */
38
- class StepExecutor {
39
- constructor(framework) {
40
- this.framework = framework;
41
- this._log = logger.child('StepExecutor');
42
- }
43
- /**
44
- * 执行单个步骤
45
- */
46
- async executeStep(step, context) {
47
- const handler = this._getStepHandler(step.type);
48
- if (!handler) {
49
- throw new Error(`Unknown step type: ${step.type}`);
50
- }
51
- return await handler.call(this, step, context);
52
- }
53
- /**
54
- * 执行多个步骤(顺序)
55
- */
56
- async executeSteps(steps, context) {
57
- const results = [];
58
- for (const step of steps) {
59
- const result = await this.executeStep(step, context);
60
- results.push(result);
61
- }
62
- return results;
63
- }
64
- /**
65
- * 获取步骤类型处理器
66
- */
67
- _getStepHandler(type) {
68
- const handlers = {
69
- [StepType.TOOL]: this.executeTool,
70
- [StepType.SCRIPT]: this.executeScript,
71
- [StepType.CONDITION]: this.executeCondition,
72
- [StepType.SWITCH]: this.executeSwitch,
73
- [StepType.TRY]: this.executeTry,
74
- [StepType.PARALLEL]: this.executeParallel,
75
- [StepType.LOOP]: this.executeLoop,
76
- [StepType.DELAY]: this.executeDelay,
77
- [StepType.SEQUENTIAL]: this.executeSequential,
78
- [StepType.MESSAGE]: this.executeMessage,
79
- [StepType.THINK]: this.executeThink,
80
- [StepType.WORKFLOW]: this.executeWorkflowStep,
81
- };
82
- return handlers[type];
83
- }
84
- /**
85
- * 执行 switch 步骤
86
- */
87
- async executeSwitch(step, context) {
88
- this._log.debug(` Executing switch: ${step.name || step.id}`);
89
- const value = this._resolveValue(step.value, context);
90
- const branches = step.branches || [];
91
- for (const branch of branches) {
92
- const caseValue = this._resolveValue(branch.case, context);
93
- if (value === caseValue) {
94
- this._log.debug(` Switch matched case: ${caseValue}`);
95
- if (branch.steps && branch.steps.length > 0) {
96
- return await this.executeSteps(branch.steps, context);
97
- }
98
- return { matched: caseValue };
99
- }
100
- }
101
- // 默认分支
102
- if (step.default && step.default.steps) {
103
- this._log.debug(` Executing switch default branch`);
104
- return await this.executeSteps(step.default.steps, context);
105
- }
106
- return { matched: null, value };
107
- }
108
- /**
109
- * 执行 try-catch 步骤
110
- */
111
- async executeTry(step, context) {
112
- this._log.debug(` Executing try-catch: ${step.name || step.id}`);
113
- try {
114
- if (step.try && step.try.steps) {
115
- const result = await this.executeSteps(step.try.steps, context);
116
- return { success: true, result };
117
- }
118
- return { success: true };
119
- } catch (err) {
120
- this._log.debug(` Try block failed, executing catch: ${err.message}`);
121
- if (step.catch && step.catch.steps) {
122
- const catchResult = await this.executeSteps(step.catch.steps, context);
123
- return { success: false, error: err.message, caught: catchResult };
124
- }
125
- return { success: false, error: err.message };
126
- }
127
- }
128
- /**
129
- * 执行嵌套工作流步骤
130
- */
131
- async executeWorkflowStep(step, context) {
132
- this._log.debug(` Executing nested workflow: ${step.name || step.id}`);
133
- if (!step.workflow) {
134
- throw new Error('Workflow step requires a workflow definition');
135
- }
136
- // 执行嵌套工作流
137
- const result = await this.framework.pluginManager
138
- .get('workflow')
139
- .executeWorkflow(step.workflow, step.input || {}, context.variables._sessionId);
140
- // 将嵌套工作流的输出合并到当前上下文
141
- if (result.output) {
142
- for (const [key, value] of Object.entries(result.output)) {
143
- context.variables[key] = value;
144
- }
145
- }
146
- return result;
147
- }
148
- /**
149
- * 执行并行步骤
150
- */
151
- async executeParallel(step, context) {
152
- this._log.debug(` Executing parallel: ${step.name || step.id}`);
153
- const steps = step.steps || [];
154
- if (steps.length === 0) {
155
- return [];
156
- }
157
- // 并行执行所有步骤
158
- const promises = steps.map((stepConfig) => {
159
- return this.executeStep(stepConfig, context);
160
- });
161
- const results = await Promise.all(promises);
162
- return results;
163
- }
164
- /**
165
- * 执行工具步骤
166
- */
167
- async executeTool(step, context) {
168
- if (!step.tool) {
169
- throw new Error('Tool step requires a tool name');
170
- }
171
- this._log.debug(` Executing tool: ${step.tool}`);
172
- // 解析工具参数,支持变量引用
173
- let resolvedArgs = this._resolveArgs(step.args || {}, context);
174
- // 兼容旧格式:处理 step.input 字段(${stepId.output} 引用)
175
- // 将 input 注入到 args.input_data,供 python-execute 使用
176
- if (step.input) {
177
- const resolvedInput = this._resolveArgs(step.input, context);
178
- resolvedArgs.input_data = resolvedInput;
179
- }
180
- // 获取当前 sessionId
181
- let sessionId = context.variables?._sessionId;
182
- if (!sessionId && this.framework.getExecutionContext) {
183
- const ctx = this.framework.getExecutionContext();
184
- sessionId = ctx?.sessionId;
185
- }
186
- // 执行工具
187
- let result;
188
- if (sessionId && this.framework.runWithContext) {
189
- result = await this.framework.runWithContext(
190
- { sessionId },
191
- async () => await this.framework.executeTool(step.tool, resolvedArgs)
192
- );
193
- } else {
194
- result = await this.framework.executeTool(step.tool, resolvedArgs);
195
- }
196
- // 保存结果到变量
197
- // 支持 output 作为 outputVariable 的别名
198
- const outputVar = step.outputVariable || step.output;
199
- if (outputVar) {
200
- context.variables[outputVar] = result;
201
- }
202
- context.lastResult = result;
203
- if (result && result.error) {
204
- this._log.warn(` Tool ${step.tool} returned error: ${result.error}`);
205
- }
206
- return result;
207
- }
208
- /**
209
- * 执行脚本步骤
210
- */
211
- async executeScript(step, context) {
212
- this._log.debug(` Executing script step: ${step.name || step.id}`);
213
- // 安全警告:使用 new Function() 执行用户脚本存在风险
214
- if (typeof step.script === 'string') {
215
- this._log.warn(
216
- ` [SECURITY WARNING] Executing user script via new Function() - ensure script is from trusted source`
217
- );
218
- }
219
- try {
220
- // 兼容旧格式:处理 step.input 字段(${stepId.output} 引用)
221
- let resolvedInput = context.input || {};
222
- if (step.input && typeof step.input === 'object') {
223
- resolvedInput = this._resolveArgs(step.input, context);
224
- }
225
- const scriptContext = {
226
- input: resolvedInput,
227
- input_data: resolvedInput, // 兼容 input_data 变量名
228
- variables: context.variables,
229
- previousResult: context.lastResult,
230
- console: {
231
- log: (...args) => {
232
- const s = logger.child('Script');
233
- s.debug(`[\${step.id}]`, ...args);
234
- },
235
- },
236
- };
237
- let result;
238
- if (typeof step.script === 'function') {
239
- result = await step.script(scriptContext);
240
- } else if (typeof step.script === 'string') {
241
- // 使用沙箱执行用户脚本,防止恶意代码执行
242
- result = await runScriptSafely(step.script, scriptContext, { timeout: 10000 });
243
- }
244
- // 支持 output 作为 outputVariable 的别名
245
- const outputVar = step.outputVariable || step.output;
246
- if (outputVar) {
247
- context.variables[outputVar] = result;
248
- }
249
- context.lastResult = result;
250
- return result;
251
- } catch (err) {
252
- throw new Error(`Script execution failed: ${err.message}`);
253
- }
254
- }
255
- /**
256
- * 执行条件步骤
257
- */
258
- async executeCondition(step, context) {
259
- this._log.debug(` Evaluating condition: ${step.name || step.id}`);
260
- for (const branch of step.branches || []) {
261
- try {
262
- let conditionValue;
263
- if (typeof branch.condition === 'function') {
264
- conditionValue = branch.condition(context);
265
- } else if (typeof branch.condition === 'string') {
266
- // 使用沙箱评估条件,防止恶意代码执行
267
- conditionValue = await evaluateInSandbox(branch.condition, context, { timeout: 5000 });
268
- }
269
- if (conditionValue) {
270
- this._log.debug(` Condition matched: ${branch.name || branch.stepId}`);
271
- if (branch.steps && branch.steps.length > 0) {
272
- await this.executeSteps(branch.steps, context);
273
- }
274
- return branch.stepId || branch.name;
275
- }
276
- } catch (err) {
277
- this._log.warn(` Branch condition error:`, err.message);
278
- }
279
- }
280
- // 执行默认分支
281
- if (step.defaultBranch && step.defaultBranch.steps) {
282
- this._log.debug(` Executing default branch`);
283
- await this.executeSteps(step.defaultBranch.steps, context);
284
- }
285
- return null;
286
- }
287
- /**
288
- * 执行循环步骤
289
- */
290
- async executeLoop(step, context) {
291
- this._log.debug(` Executing loop: ${step.name || step.id}`);
292
- const results = [];
293
- const maxIterations = step.maxIterations || 10;
294
- const loopVariable = step.loopVariable || 'loopIndex';
295
- for (let i = 0; i < maxIterations; i++) {
296
- context.variables[loopVariable] = i;
297
- this._log.debug(` Loop iteration ${i}`);
298
- const iterationResults = [];
299
- for (const stepConfig of step.steps || []) {
300
- const result = await this.executeStep(stepConfig, context);
301
- iterationResults.push(result);
302
- }
303
- results.push(iterationResults);
304
- // 检查终止条件
305
- if (step.until) {
306
- let shouldStop = false;
307
- if (typeof step.until === 'function') {
308
- shouldStop = step.until(context);
309
- } else if (typeof step.until === 'string') {
310
- // 使用沙箱评估循环条件,防止恶意代码执行
311
- shouldStop = await evaluateInSandbox(step.until, context, { timeout: 5000 });
312
- }
313
- if (shouldStop) {
314
- this._log.debug(` Loop terminated at iteration ${i}`);
315
- break;
316
- }
317
- }
318
- }
319
- return results;
320
- }
321
- /**
322
- * 执行延迟步骤
323
- */
324
- async executeDelay(step, context) {
325
- const delayMs = step.delayMs || 1000;
326
- this._log.debug(` Delaying ${delayMs}ms`);
327
- await new Promise((resolve) => setTimeout(resolve, delayMs));
328
- return null;
329
- }
330
- /**
331
- * 执行顺序步骤
332
- */
333
- async executeSequential(step, context) {
334
- this._log.debug(` Executing sequential: ${step.name || step.id}`);
335
- return await this.executeSteps(step.steps || [], context);
336
- }
337
- /**
338
- * 执行消息步骤(ExplorerLoop 专用)
339
- */
340
- async executeMessage(step, context) {
341
- this._log.debug(` Executing message step`);
342
- // 使用子Agent处理消息,避免阻塞主agent
343
- const messageAgent = this.framework.createSubAgent({
344
- name: 'workflow_message',
345
- role: '工作流任务执行助手,专注于处理工作流中的消息任务',
346
- });
347
- let content = step.content || '';
348
- // 支持变量引用
349
- content = this._resolveValue(content, context);
350
- // 如果有事件上下文,附加到消息
351
- if (context.variables?._event) {
352
- content = `${content}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`;
353
- }
354
- const result = await messageAgent.chat(content);
355
- return { success: true, result };
356
- }
357
- /**
358
- * 执行思考步骤(ExplorerLoop 专用)
359
- */
360
- async executeThink(step, context) {
361
- this._log.debug(` Executing think step`);
362
- const thinkPlugin = this.framework.pluginManager?.get('think');
363
- if (!thinkPlugin) {
364
- return { success: false, error: '思考插件不可用' };
365
- }
366
- let topic = step.topic || 'Ambient代理反思';
367
- topic = this._resolveValue(topic, context);
368
- if (context.variables?._event) {
369
- topic = `${topic}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`;
370
- }
371
- const result = await thinkPlugin._triggerThinking({
372
- topic,
373
- mode: step.mode || 'reflect',
374
- depth: step.depth || 2,
375
- });
376
- return result;
377
- }
378
- /**
379
- * 获取活跃代理
380
- */
381
- _getActiveAgent() {
382
- if (this.framework._mainAgent) {
383
- return this.framework._mainAgent;
384
- }
385
- const agents = this.framework._agents || [];
386
- return agents.length > 0 ? agents[agents.length - 1] : null;
387
- }
388
- /**
389
- * 解析参数中的变量引用
390
- */
391
- _resolveArgs(args, context) {
392
- const resolved = {};
393
- for (const [key, value] of Object.entries(args)) {
394
- resolved[key] = this._resolveValue(value, context);
395
- }
396
- return resolved;
397
- }
398
- _resolveValue(value, context) {
399
- if (typeof value === 'string') {
400
- // 支持多种引用格式:
401
- // {{result}} - 上一步结果 (lastResult)
402
- // {{result.body.ip}} - 从上一步结果提取嵌套字段
403
- // {{variables.xxx}} - 显式引用 context.variables
404
- // {{context.xxx}} - 显式引用 context 根属性
405
- // {{lastResult}} - 上一步结果(result 的别名)
406
- // ${stepId.output} - 引用指定 id 步骤的输出(兼容旧格式)
407
- // ${stepId.output.path} - 从指定步骤输出提取字段
408
- let result = value;
409
- // 先处理 ${stepId.output} 格式(兼容旧格式)
410
- result = result.replace(/\$\{([^}]+)\}/g, (match, path) => {
411
- const stepOutputs = context.variables._stepOutputs || {};
412
- if (path === 'output') {
413
- // ${output} 等同于 ${stepId.output} 引用上一步
414
- return context.lastResult ?? match;
415
- }
416
- if (path.includes('.')) {
417
- // ${stepId.output.field} 格式
418
- const [stepId, ...fieldParts] = path.split('.');
419
- const stepOutput = stepOutputs[stepId];
420
- if (stepOutput !== undefined) {
421
- const val = this._getNestedValue(stepOutput, fieldParts.join('.'));
422
- if (val !== undefined) return val;
423
- }
424
- } else {
425
- // ${stepId.output} 格式
426
- const stepOutput = stepOutputs[path];
427
- if (stepOutput !== undefined) return stepOutput;
428
- }
429
- return match;
430
- });
431
- // 再处理 {{...}} 格式(支持 _event.email.from 等嵌套属性)
432
- result = result.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
433
- // result 和 lastResult 都指向 context.lastResult
434
- if (path === 'result' || path === 'lastResult') {
435
- return context.lastResult ?? match;
436
- }
437
- if (path.startsWith('result.') || path.startsWith('lastResult.')) {
438
- const nestedPath = path.replace(/^(result|lastResult)\./, '');
439
- const val = this._getNestedValue(context.lastResult, nestedPath);
440
- if (val !== undefined) return val;
441
- return match;
442
- }
443
- if (path.startsWith('variables.')) {
444
- return this._getNestedValue(context, path) ?? match;
445
- } else if (path.startsWith('context.')) {
446
- return this._getNestedValue(context, path) ?? match;
447
- } else {
448
- // 先从 variables 查找,再从 context 根属性查找
449
- const val = this._getNestedValue(context.variables, path);
450
- if (val !== undefined) return val;
451
- return this._getNestedValue(context, path) ?? match;
452
- }
453
- });
454
- return result;
455
- } else if (Array.isArray(value)) {
456
- return value.map((v) => this._resolveValue(v, context));
457
- } else if (typeof value === 'object' && value !== null) {
458
- const resolved = {};
459
- for (const [k, v] of Object.entries(value)) {
460
- resolved[k] = this._resolveValue(v, context);
461
- }
462
- return resolved;
463
- }
464
- return value;
465
- }
466
- _getNestedValue(obj, path) {
467
- if (!obj) return undefined;
468
- const parts = path.split('.');
469
- let current = obj;
470
- for (const part of parts) {
471
- if (current === null || current === undefined) return undefined;
472
- // 如果当前值是字符串,尝试解析为 JSON
473
- if (typeof current === 'string' && part !== '0' && part !== 'length') {
474
- try {
475
- current = JSON.parse(current);
476
- } catch {
477
- // 解析失败,继续用原值
478
- }
479
- }
480
- current = current[part];
481
- }
482
- return current;
483
- }
484
- }
485
- /**
486
- * 工作流步骤基类
487
- */
488
- class WorkflowStep {
489
- constructor(config) {
490
- this.id = config.id || `step_${Date.now()}_${crypto.randomBytes(6).toString('base64url')}`;
491
- this.type = config.type;
492
- this.name = config.name || '';
493
- this.description = config.description || '';
494
- this.condition = config.condition || null;
495
- this.timeout = config.timeout || 30000;
496
- this.retry = config.retry || { enabled: false, maxAttempts: 3, delay: 1000 };
497
- this.onSuccess = config.onSuccess || null;
498
- this.onFailure = config.onFailure || null;
499
- }
500
- async execute(context, engine) {
501
- throw new Error('execute() must be implemented by subclass');
502
- }
503
- validate() {
504
- if (!this.type) {
505
- throw new Error('Step must have a type');
506
- }
507
- return true;
508
- }
509
- }
510
- /**
511
- * 脚本步骤
512
- */
513
- class ScriptStep extends WorkflowStep {
514
- constructor(config) {
515
- super({ ...config, type: StepType.SCRIPT });
516
- this.script = config.script;
517
- this.outputVariable = config.outputVariable || null;
518
- }
519
- async execute(context, engine) {
520
- // 使用 StepExecutor 执行脚本逻辑
521
- const executor = new StepExecutor(engine.framework);
522
- const stepConfig = {
523
- id: this.id,
524
- name: this.name,
525
- type: StepType.SCRIPT,
526
- script: this.script,
527
- outputVariable: this.outputVariable,
528
- };
529
- return await executor.executeScript(stepConfig, context);
530
- }
531
- }
532
- /**
533
- * 条件步骤
534
- */
535
- class ConditionStep extends WorkflowStep {
536
- constructor(config) {
537
- super({ ...config, type: StepType.CONDITION });
538
- this.branches = config.branches || [];
539
- this.defaultBranch = config.defaultBranch || null;
540
- }
541
- async execute(context, engine) {
542
- const executor = new StepExecutor(engine.framework);
543
- const stepConfig = {
544
- id: this.id,
545
- name: this.name,
546
- type: StepType.CONDITION,
547
- branches: this.branches,
548
- defaultBranch: this.defaultBranch,
549
- };
550
- return await executor.executeCondition(stepConfig, context);
551
- }
552
- }
553
- /**
554
- * 并行步骤
555
- */
556
- class ParallelStep extends WorkflowStep {
557
- constructor(config) {
558
- super({ ...config, type: StepType.PARALLEL });
559
- this.steps = config.steps || [];
560
- }
561
- async execute(context, engine) {
562
- const executor = new StepExecutor(engine.framework);
563
- const stepConfig = {
564
- id: this.id,
565
- name: this.name,
566
- type: StepType.PARALLEL,
567
- steps: this.steps,
568
- };
569
- return await executor.executeParallel(stepConfig, context);
570
- }
571
- }
572
- /**
573
- * Switch 步骤
574
- */
575
- class SwitchStep extends WorkflowStep {
576
- constructor(config) {
577
- super({ ...config, type: StepType.SWITCH });
578
- this.value = config.value;
579
- this.branches = config.branches || [];
580
- this.default = config.default || null;
581
- }
582
- async execute(context, engine) {
583
- const executor = new StepExecutor(engine.framework);
584
- const stepConfig = {
585
- id: this.id,
586
- name: this.name,
587
- type: StepType.SWITCH,
588
- value: this.value,
589
- branches: this.branches,
590
- default: this.default,
591
- };
592
- return await executor.executeSwitch(stepConfig, context);
593
- }
594
- }
595
- /**
596
- * Try-Catch 步骤
597
- */
598
- class TryStep extends WorkflowStep {
599
- constructor(config) {
600
- super({ ...config, type: StepType.TRY });
601
- this.try = config.try || {};
602
- this.catch = config.catch || {};
603
- }
604
- async execute(context, engine) {
605
- const executor = new StepExecutor(engine.framework);
606
- const stepConfig = {
607
- id: this.id,
608
- name: this.name,
609
- type: StepType.TRY,
610
- try: this.try,
611
- catch: this.catch,
612
- };
613
- return await executor.executeTry(stepConfig, context);
614
- }
615
- }
616
- /**
617
- * 嵌套工作流步骤
618
- */
619
- class NestedWorkflowStep extends WorkflowStep {
620
- constructor(config) {
621
- super({ ...config, type: StepType.WORKFLOW });
622
- this.workflow = config.workflow;
623
- this.input = config.input || {};
624
- }
625
- async execute(context, engine) {
626
- const executor = new StepExecutor(engine.framework);
627
- const stepConfig = {
628
- id: this.id,
629
- name: this.name,
630
- type: StepType.WORKFLOW,
631
- workflow: this.workflow,
632
- input: this.input,
633
- };
634
- return await executor.executeWorkflowStep(stepConfig, context);
635
- }
636
- }
637
- /**
638
- * 顺序步骤
639
- */
640
- class SequentialStep extends WorkflowStep {
641
- constructor(config) {
642
- super({ ...config, type: StepType.SEQUENTIAL });
643
- this.steps = config.steps || [];
644
- }
645
- async execute(context, engine) {
646
- const executor = new StepExecutor(engine.framework);
647
- const stepConfig = {
648
- id: this.id,
649
- name: this.name,
650
- type: StepType.SEQUENTIAL,
651
- steps: this.steps,
652
- };
653
- return await executor.executeSequential(stepConfig, context);
654
- }
655
- }
656
- /**
657
- * 循环步骤
658
- */
659
- class LoopStep extends WorkflowStep {
660
- constructor(config) {
661
- super({ ...config, type: StepType.LOOP });
662
- this.maxIterations = config.maxIterations || 10;
663
- this.loopVariable = config.loopVariable || 'loopIndex';
664
- this.steps = config.steps || [];
665
- this.until = config.until || null;
666
- }
667
- async execute(context, engine) {
668
- const executor = new StepExecutor(engine.framework);
669
- const stepConfig = {
670
- id: this.id,
671
- name: this.name,
672
- type: StepType.LOOP,
673
- maxIterations: this.maxIterations,
674
- loopVariable: this.loopVariable,
675
- steps: this.steps,
676
- until: this.until,
677
- };
678
- return await executor.executeLoop(stepConfig, context);
679
- }
680
- }
681
- /**
682
- * 延迟步骤
683
- */
684
- class DelayStep extends WorkflowStep {
685
- constructor(config) {
686
- super({ ...config, type: StepType.DELAY });
687
- this.delayMs = config.delayMs || 1000;
688
- }
689
- async execute(context, engine) {
690
- const executor = new StepExecutor(engine.framework);
691
- const stepConfig = {
692
- id: this.id,
693
- name: this.name,
694
- type: StepType.DELAY,
695
- delayMs: this.delayMs,
696
- };
697
- return await executor.executeDelay(stepConfig, context);
698
- }
699
- }
700
- /**
701
- * 工具步骤
702
- */
703
- class ToolStep extends WorkflowStep {
704
- constructor(config) {
705
- super({ ...config, type: StepType.TOOL });
706
- this.tool = config.tool;
707
- this.args = config.args || {};
708
- this.outputVariable = config.outputVariable || null;
709
- this.output = config.output || null; // 兼容旧格式
710
- this.input = config.input || null; // 兼容旧格式:input 字段
711
- }
712
- async execute(context, engine) {
713
- const executor = new StepExecutor(engine.framework);
714
- const stepConfig = {
715
- id: this.id,
716
- name: this.name,
717
- type: StepType.TOOL,
718
- tool: this.tool,
719
- args: this.args,
720
- outputVariable: this.outputVariable,
721
- output: this.output, // 传递 output
722
- input: this.input, // 传递 input
723
- };
724
- return await executor.executeTool(stepConfig, context);
725
- }
726
- }
727
- /**
728
- * 工作流引擎
729
- */
730
- class WorkflowEngine extends EventEmitter {
731
- constructor(framework) {
732
- super();
733
- this.framework = framework;
734
- this._steps = new Map();
735
- }
736
- /**
737
- * 注册自定义步骤类型
738
- */
739
- registerStepType(type, StepClass) {
740
- this._steps.set(type, StepClass);
741
- }
742
- /**
743
- * 创建步骤实例
744
- * 自动推断步骤类型:
745
- * - 有 tool 字段 -> tool 类型
746
- * - 有 code/script 字段 -> script 类型
747
- * - 有 branches 字段 -> condition 类型
748
- * - 有 value + branches -> switch 类型
749
- * - 有 try/catch 字段 -> try 类型
750
- * - 有 parallel 属性 -> parallel 类型
751
- * - 有 steps 字段 + loopVariable/maxIterations -> loop 类型
752
- * - 有 steps 字段 -> sequential 类型
753
- * - 有 delayMs 字段 -> delay 类型
754
- * - 有 workflow 字段 -> workflow 类型
755
- */
756
- createStep(config) {
757
- let type = config.type;
758
- // 自动推断类型
759
- if (!type) {
760
- if (config.tool) {
761
- type = StepType.TOOL;
762
- } else if (config.code || config.script) {
763
- type = StepType.SCRIPT;
764
- } else if (config.try || config.catch) {
765
- type = StepType.TRY;
766
- } else if (config.value !== undefined && config.branches) {
767
- type = StepType.SWITCH;
768
- } else if (config.parallel === true || config.mode === 'parallel') {
769
- type = StepType.PARALLEL;
770
- } else if (config.workflow) {
771
- type = StepType.WORKFLOW;
772
- } else if (config.branches) {
773
- type = StepType.CONDITION;
774
- } else if (config.steps && (config.loopVariable || config.maxIterations)) {
775
- type = StepType.LOOP;
776
- } else if (config.steps) {
777
- type = StepType.SEQUENTIAL;
778
- } else if (config.delayMs !== undefined) {
779
- type = StepType.DELAY;
780
- } else if (config.message) {
781
- type = StepType.MESSAGE;
782
- } else if (config.topic) {
783
- type = StepType.THINK;
784
- }
785
- }
786
- if (!type) {
787
- throw new Error(
788
- `Cannot infer step type for step: ${JSON.stringify(config).substring(0, 100)}`
789
- );
790
- }
791
- const StepClass = this._steps.get(type);
792
- if (!StepClass) {
793
- throw new Error(`Unknown step type: ${type}`);
794
- }
795
- // 确保配置有 type 字段
796
- return new StepClass({ ...config, type });
797
- }
798
- /**
799
- * 创建工作流上下文
800
- */
801
- createContext(input = {}, variables = {}) {
802
- return {
803
- input,
804
- variables,
805
- lastResult: null,
806
- results: [],
807
- };
808
- }
809
- }
810
- /**
811
- * WorkflowManagerPlugin
812
- */
813
- class WorkflowPlugin extends Plugin {
814
- constructor(config = {}) {
815
- super();
816
- this.name = 'workflow';
817
- this.version = '1.0.0';
818
- this.description = '工作流引擎,支持结构化工作流定义和执行';
819
- this.priority = 6;
820
- this.system = true;
821
- this._framework = null;
822
- this._engine = null;
823
- this._workflowsDir = config.workflowsDir || '.agent/workflows';
824
- this._workflows = new Map();
825
- this._workflowTools = new Map();
826
- }
827
- install(framework) {
828
- this._framework = framework;
829
- this._engine = new WorkflowEngine(framework);
830
- // 注册内置步骤类型
831
- this._engine.registerStepType(StepType.SCRIPT, ScriptStep);
832
- this._engine.registerStepType(StepType.CONDITION, ConditionStep);
833
- this._engine.registerStepType(StepType.SWITCH, SwitchStep);
834
- this._engine.registerStepType(StepType.TRY, TryStep);
835
- this._engine.registerStepType(StepType.PARALLEL, ParallelStep);
836
- this._engine.registerStepType(StepType.SEQUENTIAL, SequentialStep);
837
- this._engine.registerStepType(StepType.LOOP, LoopStep);
838
- this._engine.registerStepType(StepType.DELAY, DelayStep);
839
- this._engine.registerStepType(StepType.TOOL, ToolStep);
840
- this._engine.registerStepType(StepType.WORKFLOW, NestedWorkflowStep);
841
- // 注册工作流执行工具
842
- framework.registerTool({
843
- name: 'execute_workflow',
844
- description: '执行指定的工作流',
845
- inputSchema: z.object({
846
- workflow: z.string().describe('工作流定义(JSON 或 JavaScript 代码)'),
847
- input: z.object({}).optional().describe('工作流输入参数'),
848
- }),
849
- execute: async (args, framework) => {
850
- // 获取当前 sessionId
851
- let sessionId = null;
852
- const ctx = framework.getExecutionContext();
853
- if (ctx?.sessionId) {
854
- sessionId = ctx.sessionId;
855
- }
856
- return await this.executeWorkflow(args.workflow, args.input || {}, sessionId);
857
- },
858
- });
859
- return this;
860
- }
861
- start(framework) {
862
- this._loadWorkflows();
863
- this._registerWorkflowTools();
864
- // 注册 reloadWorkflows 工具
865
- framework.registerTool({
866
- name: 'reloadWorkflows',
867
- description: '重载所有工作流,当用户添加或修改工作流后调用此工具',
868
- inputSchema: z.object({}),
869
- execute: async () => {
870
- this.reload(this._framework);
871
- return {
872
- success: true,
873
- message: `Workflows reloaded. Total: ${this._workflows.size}`,
874
- workflows: Array.from(this._workflows.keys()),
875
- };
876
- },
877
- });
878
- return this;
879
- }
880
- /**
881
- * 加载目录中的工作流定义
882
- */
883
- _loadWorkflows() {
884
- const dir = path.resolve(process.cwd(), this._workflowsDir);
885
- if (!fs.existsSync(dir)) {
886
- fs.mkdirSync(dir, { recursive: true });
887
- log.info(` Created workflows directory: ${dir}`);
888
- return;
889
- }
890
- try {
891
- const files = fs.readdirSync(dir);
892
- for (const file of files) {
893
- if (!file.endsWith('.json') && !file.endsWith('.js')) continue;
894
- const filePath = path.join(dir, file);
895
- const workflowName = path.basename(file, path.extname(file));
896
- // 跳过已存在的工作流
897
- if (this._workflows.has(workflowName)) continue;
898
- try {
899
- const content = fs.readFileSync(filePath, 'utf-8');
900
- let workflowDef;
901
- if (file.endsWith('.js')) {
902
- // 执行 JS 文件获取工作流定义
903
- // 使用沙箱加载工作流文件,防止恶意代码执行
904
- workflowDef = runWorkflowFileSafely(content, { timeout: 10000 });
905
- if (typeof workflowDef === 'function') {
906
- workflowDef = workflowDef(this._engine || {});
907
- }
908
- workflowDef = workflowDef.default || workflowDef;
909
- } else {
910
- workflowDef = JSON.parse(content);
911
- }
912
- if (workflowDef && workflowDef.steps) {
913
- this._workflows.set(workflowName, workflowDef);
914
- //log.info(` Loaded: ${workflowName}`)
915
- }
916
- } catch (err) {
917
- log.error(` Failed to load ${file}:`, err.message);
918
- }
919
- }
920
- } catch (err) {
921
- log.error(' Failed to read workflows directory:', err.message);
922
- }
923
- }
924
- /**
925
- * 注册工作流为工具
926
- */
927
- _registerWorkflowTools() {
928
- for (const [name, workflow] of this._workflows) {
929
- if (this._workflowTools.has(name)) continue;
930
- const toolName = `workflow_${name}`;
931
- const description = workflow.description || `执行工作流: ${name}`;
932
- this._framework.registerTool({
933
- name: toolName,
934
- description,
935
- inputSchema: z.object({
936
- input: z.object({}).optional().describe('工作流输入参数'),
937
- }),
938
- execute: async (args, framework) => {
939
- // Get current sessionId to ensure notifications reach the correct session
940
- let sessionId = null;
941
- const ctx = framework?.getExecutionContext?.();
942
- if (ctx?.sessionId) {
943
- sessionId = ctx.sessionId;
944
- }
945
- return await this.executeWorkflow(workflow, args.input || {}, sessionId);
946
- },
947
- });
948
- this._workflowTools.set(name, toolName);
949
- log.info(` Registered tool: ${toolName}`);
950
- }
951
- }
952
- /**
953
- * 重载工作流
954
- */
955
- reload(framework) {
956
- // 清除模块缓存,确保重新加载最新代码
957
- const modulePath = require.resolve('./src/capabilities/workflow-engine');
958
- if (require.cache[modulePath]) {
959
- delete require.cache[modulePath];
960
- }
961
- log.info(' Reloading...');
962
- this._framework = framework;
963
- // 重新创建 engine(确保使用最新代码)
964
- this._engine = new (require('./src/capabilities/workflow-engine').WorkflowEngine)(framework);
965
- // 注册内置步骤类型
966
- this._engine.registerStepType(
967
- require('./src/capabilities/workflow-engine').StepType.SCRIPT,
968
- require('./src/capabilities/workflow-engine').ScriptStep
969
- );
970
- this._engine.registerStepType(
971
- require('./src/capabilities/workflow-engine').StepType.TOOL,
972
- require('./src/capabilities/workflow-engine').ToolStep
973
- );
974
- this._engine.registerStepType(
975
- require('./src/capabilities/workflow-engine').StepType.CONDITION,
976
- require('./src/capabilities/workflow-engine').ConditionStep
977
- );
978
- this._engine.registerStepType(
979
- require('./src/capabilities/workflow-engine').StepType.SWITCH,
980
- require('./src/capabilities/workflow-engine').SwitchStep
981
- );
982
- this._engine.registerStepType(
983
- require('./src/capabilities/workflow-engine').StepType.TRY,
984
- require('./src/capabilities/workflow-engine').TryStep
985
- );
986
- this._engine.registerStepType(
987
- require('./src/capabilities/workflow-engine').StepType.PARALLEL,
988
- require('./src/capabilities/workflow-engine').ParallelStep
989
- );
990
- this._engine.registerStepType(
991
- require('./src/capabilities/workflow-engine').StepType.SEQUENTIAL,
992
- require('./src/capabilities/workflow-engine').SequentialStep
993
- );
994
- this._engine.registerStepType(
995
- require('./src/capabilities/workflow-engine').StepType.LOOP,
996
- require('./src/capabilities/workflow-engine').LoopStep
997
- );
998
- this._engine.registerStepType(
999
- require('./src/capabilities/workflow-engine').StepType.DELAY,
1000
- require('./src/capabilities/workflow-engine').DelayStep
1001
- );
1002
- this._engine.registerStepType(
1003
- require('./src/capabilities/workflow-engine').StepType.WORKFLOW,
1004
- require('./src/capabilities/workflow-engine').NestedWorkflowStep
1005
- );
1006
- // 清除已注册的工具
1007
- for (const toolName of this._workflowTools.values()) {
1008
- // 工具注销需要框架支持,这里只清理内部状态
1009
- }
1010
- this._workflowTools.clear();
1011
- this._workflows.clear();
1012
- // 重新加载
1013
- this._loadWorkflows();
1014
- this._registerWorkflowTools();
1015
- log.info(` Reloaded. Total workflows: ${this._workflows.size}`);
1016
- }
1017
- /**
1018
- * 执行工作流
1019
- */
1020
- async executeWorkflow(workflowDef, input = {}, sessionId = null) {
1021
- try {
1022
- let workflow;
1023
- if (typeof workflowDef === 'string') {
1024
- // 尝试作为工作流名称加载(先检查是否已加载的工作流)
1025
- const workflowName = workflowDef.trim();
1026
- if (this._workflows.has(workflowName)) {
1027
- workflow = this._workflows.get(workflowName);
1028
- } else {
1029
- // 尝试解析 JSON 或执行代码
1030
- try {
1031
- workflow = JSON.parse(workflowDef);
1032
- } catch {
1033
- // 使用沙箱解析工作流定义,防止恶意代码执行
1034
- workflow = runWorkflowSafely(workflowDef, this._engine, { timeout: 5000 });
1035
- }
1036
- }
1037
- } else {
1038
- workflow = workflowDef;
1039
- }
1040
- const context = this._engine.createContext(input, {});
1041
- // 将 sessionId 存储到上下文变量,供工具步骤使用
1042
- if (sessionId) {
1043
- context.variables._sessionId = sessionId;
1044
- }
1045
- // 执行工作流步骤
1046
- if (workflow.steps && Array.isArray(workflow.steps)) {
1047
- const results = [];
1048
- // 按 id 存储步骤输出,供 ${id.output} 引用
1049
- context.variables._stepOutputs = {};
1050
- for (const stepConfig of workflow.steps) {
1051
- const step = this._engine.createStep(stepConfig);
1052
- const result = await step.execute(context, this._engine);
1053
- results.push(result);
1054
- // 如果步骤有 id,存储其输出
1055
- if (stepConfig.id) {
1056
- context.variables._stepOutputs[stepConfig.id] = result;
1057
- }
1058
- }
1059
- // 只返回最后一步的结果(包含最终输出)和成功状态
1060
- // 不返回所有中间结果,避免上下文超限
1061
- const lastResult = results.length > 0 ? results[results.length - 1] : null;
1062
- // 从 context.variables 中提取非下划线开头的用户变量
1063
- // 下划线开头的是内部变量(如 _stepOutputs),不需要返回
1064
- const userVariables = {};
1065
- for (const [key, value] of Object.entries(context.variables)) {
1066
- if (!key.startsWith('_')) {
1067
- userVariables[key] = value;
1068
- }
1069
- }
1070
- return {
1071
- success: true,
1072
- stepCount: workflow.steps.length,
1073
- result: lastResult,
1074
- output: userVariables,
1075
- };
1076
- }
1077
- return { success: true, output: {} };
1078
- } catch (err) {
1079
- return { success: false, error: err.message };
1080
- }
1081
- }
1082
- /**
1083
- * 获取引擎
1084
- */
1085
- getEngine() {
1086
- return this._engine;
1087
- }
1088
- uninstall(framework) {
1089
- this._engine = null;
1090
- this._framework = null;
1091
- }
1092
- }
1093
- module.exports = {
1094
- WorkflowPlugin,
1095
- WorkflowEngine,
1096
- WorkflowStep,
1097
- StepType,
1098
- StepExecutor,
1099
- ScriptStep,
1100
- ConditionStep,
1101
- ParallelStep,
1102
- SwitchStep,
1103
- TryStep,
1104
- NestedWorkflowStep,
1105
- SequentialStep,
1106
- LoopStep,
1107
- DelayStep,
1108
- ToolStep,
1109
- };
1
+ /**
2
+ * WorkflowEngine 工作流引擎
3
+ * 支持结构化工作流定义和执行
4
+ */
5
+ const { EventEmitter } = require('../utils/event-emitter');
6
+ const { Plugin } = require('../core/plugin-base');
7
+ const { logger } = require('../utils/logger');
8
+ const log = logger.child('Workflow');
9
+ const { z } = require('zod');
10
+ const { runScriptSafely, evaluateInSandbox, runWorkflowFileSafely, runWorkflowSafely } = require('../utils/sandbox');
11
+ const fs = require('fs');
12
+ const crypto = require('crypto');
13
+ const path = require('path');
14
+ /**
15
+ * 工作流步骤类型
16
+ */
17
+ const StepType = {
18
+ AGENT: 'agent',
19
+ SCRIPT: 'script',
20
+ CONDITION: 'condition',
21
+ SWITCH: 'switch',
22
+ TRY: 'try',
23
+ PARALLEL: 'parallel',
24
+ SEQUENTIAL: 'sequential',
25
+ LOOP: 'loop',
26
+ DELAY: 'delay',
27
+ TOOL: 'tool',
28
+ INPUT: 'input',
29
+ OUTPUT: 'output',
30
+ MESSAGE: 'message',
31
+ THINK: 'think',
32
+ WORKFLOW: 'workflow', // 嵌套工作流
33
+ };
34
+ /**
35
+ * StepExecutor - 统一的步骤执行器
36
+ * 同时被 WorkflowEngine 和 ExplorerLoop 使用
37
+ */
38
+ class StepExecutor {
39
+ constructor(framework) {
40
+ this.framework = framework;
41
+ this._log = logger.child('StepExecutor');
42
+ }
43
+ /**
44
+ * 执行单个步骤
45
+ */
46
+ async executeStep(step, context) {
47
+ const handler = this._getStepHandler(step.type);
48
+ if (!handler) {
49
+ throw new Error(`Unknown step type: ${step.type}`);
50
+ }
51
+ return await handler.call(this, step, context);
52
+ }
53
+ /**
54
+ * 执行多个步骤(顺序)
55
+ */
56
+ async executeSteps(steps, context) {
57
+ const results = [];
58
+ for (const step of steps) {
59
+ const result = await this.executeStep(step, context);
60
+ results.push(result);
61
+ }
62
+ return results;
63
+ }
64
+ /**
65
+ * 获取步骤类型处理器
66
+ */
67
+ _getStepHandler(type) {
68
+ const handlers = {
69
+ [StepType.TOOL]: this.executeTool,
70
+ [StepType.SCRIPT]: this.executeScript,
71
+ [StepType.CONDITION]: this.executeCondition,
72
+ [StepType.SWITCH]: this.executeSwitch,
73
+ [StepType.TRY]: this.executeTry,
74
+ [StepType.PARALLEL]: this.executeParallel,
75
+ [StepType.LOOP]: this.executeLoop,
76
+ [StepType.DELAY]: this.executeDelay,
77
+ [StepType.SEQUENTIAL]: this.executeSequential,
78
+ [StepType.MESSAGE]: this.executeMessage,
79
+ [StepType.THINK]: this.executeThink,
80
+ [StepType.WORKFLOW]: this.executeWorkflowStep,
81
+ };
82
+ return handlers[type];
83
+ }
84
+ /**
85
+ * 执行 switch 步骤
86
+ */
87
+ async executeSwitch(step, context) {
88
+ this._log.debug(` Executing switch: ${step.name || step.id}`);
89
+ const value = this._resolveValue(step.value, context);
90
+ const branches = step.branches || [];
91
+ for (const branch of branches) {
92
+ const caseValue = this._resolveValue(branch.case, context);
93
+ if (value === caseValue) {
94
+ this._log.debug(` Switch matched case: ${caseValue}`);
95
+ if (branch.steps && branch.steps.length > 0) {
96
+ return await this.executeSteps(branch.steps, context);
97
+ }
98
+ return { matched: caseValue };
99
+ }
100
+ }
101
+ // 默认分支
102
+ if (step.default && step.default.steps) {
103
+ this._log.debug(` Executing switch default branch`);
104
+ return await this.executeSteps(step.default.steps, context);
105
+ }
106
+ return { matched: null, value };
107
+ }
108
+ /**
109
+ * 执行 try-catch 步骤
110
+ */
111
+ async executeTry(step, context) {
112
+ this._log.debug(` Executing try-catch: ${step.name || step.id}`);
113
+ try {
114
+ if (step.try && step.try.steps) {
115
+ const result = await this.executeSteps(step.try.steps, context);
116
+ return { success: true, result };
117
+ }
118
+ return { success: true };
119
+ } catch (err) {
120
+ this._log.debug(` Try block failed, executing catch: ${err.message}`);
121
+ if (step.catch && step.catch.steps) {
122
+ const catchResult = await this.executeSteps(step.catch.steps, context);
123
+ return { success: false, error: err.message, caught: catchResult };
124
+ }
125
+ return { success: false, error: err.message };
126
+ }
127
+ }
128
+ /**
129
+ * 执行嵌套工作流步骤
130
+ */
131
+ async executeWorkflowStep(step, context) {
132
+ this._log.debug(` Executing nested workflow: ${step.name || step.id}`);
133
+ if (!step.workflow) {
134
+ throw new Error('Workflow step requires a workflow definition');
135
+ }
136
+ // 执行嵌套工作流
137
+ const result = await this.framework.pluginManager
138
+ .get('workflow')
139
+ .executeWorkflow(step.workflow, step.input || {}, context.variables._sessionId);
140
+ // 将嵌套工作流的输出合并到当前上下文
141
+ if (result.output) {
142
+ for (const [key, value] of Object.entries(result.output)) {
143
+ context.variables[key] = value;
144
+ }
145
+ }
146
+ return result;
147
+ }
148
+ /**
149
+ * 执行并行步骤
150
+ */
151
+ async executeParallel(step, context) {
152
+ this._log.debug(` Executing parallel: ${step.name || step.id}`);
153
+ const steps = step.steps || [];
154
+ if (steps.length === 0) {
155
+ return [];
156
+ }
157
+ // 并行执行所有步骤
158
+ const promises = steps.map((stepConfig) => {
159
+ return this.executeStep(stepConfig, context);
160
+ });
161
+ const results = await Promise.all(promises);
162
+ return results;
163
+ }
164
+ /**
165
+ * 执行工具步骤
166
+ */
167
+ async executeTool(step, context) {
168
+ if (!step.tool) {
169
+ throw new Error('Tool step requires a tool name');
170
+ }
171
+ this._log.debug(` Executing tool: ${step.tool}`);
172
+ // 解析工具参数,支持变量引用
173
+ let resolvedArgs = this._resolveArgs(step.args || {}, context);
174
+ // 兼容旧格式:处理 step.input 字段(${stepId.output} 引用)
175
+ // 将 input 注入到 args.input_data,供 python-execute 使用
176
+ if (step.input) {
177
+ const resolvedInput = this._resolveArgs(step.input, context);
178
+ resolvedArgs.input_data = resolvedInput;
179
+ }
180
+ // 获取当前 sessionId
181
+ let sessionId = context.variables?._sessionId;
182
+ if (!sessionId && this.framework.getExecutionContext) {
183
+ const ctx = this.framework.getExecutionContext();
184
+ sessionId = ctx?.sessionId;
185
+ }
186
+ // 执行工具
187
+ let result;
188
+ if (sessionId && this.framework.runWithContext) {
189
+ result = await this.framework.runWithContext(
190
+ { sessionId },
191
+ async () => await this.framework.executeTool(step.tool, resolvedArgs)
192
+ );
193
+ } else {
194
+ result = await this.framework.executeTool(step.tool, resolvedArgs);
195
+ }
196
+ // 保存结果到变量
197
+ // 支持 output 作为 outputVariable 的别名
198
+ const outputVar = step.outputVariable || step.output;
199
+ if (outputVar) {
200
+ context.variables[outputVar] = result;
201
+ }
202
+ context.lastResult = result;
203
+ if (result && result.error) {
204
+ this._log.warn(` Tool ${step.tool} returned error: ${result.error}`);
205
+ }
206
+ return result;
207
+ }
208
+ /**
209
+ * 执行脚本步骤
210
+ */
211
+ async executeScript(step, context) {
212
+ this._log.debug(` Executing script step: ${step.name || step.id}`);
213
+ // 安全警告:使用 new Function() 执行用户脚本存在风险
214
+ if (typeof step.script === 'string') {
215
+ this._log.warn(
216
+ ` [SECURITY WARNING] Executing user script via new Function() - ensure script is from trusted source`
217
+ );
218
+ }
219
+ try {
220
+ // 兼容旧格式:处理 step.input 字段(${stepId.output} 引用)
221
+ let resolvedInput = context.input || {};
222
+ if (step.input && typeof step.input === 'object') {
223
+ resolvedInput = this._resolveArgs(step.input, context);
224
+ }
225
+ const scriptContext = {
226
+ input: resolvedInput,
227
+ input_data: resolvedInput, // 兼容 input_data 变量名
228
+ variables: context.variables,
229
+ previousResult: context.lastResult,
230
+ console: {
231
+ log: (...args) => {
232
+ const s = logger.child('Script');
233
+ s.debug(`[\${step.id}]`, ...args);
234
+ },
235
+ },
236
+ };
237
+ let result;
238
+ if (typeof step.script === 'function') {
239
+ result = await step.script(scriptContext);
240
+ } else if (typeof step.script === 'string') {
241
+ // 使用沙箱执行用户脚本,防止恶意代码执行
242
+ result = await runScriptSafely(step.script, scriptContext, { timeout: 10000 });
243
+ }
244
+ // 支持 output 作为 outputVariable 的别名
245
+ const outputVar = step.outputVariable || step.output;
246
+ if (outputVar) {
247
+ context.variables[outputVar] = result;
248
+ }
249
+ context.lastResult = result;
250
+ return result;
251
+ } catch (err) {
252
+ throw new Error(`Script execution failed: ${err.message}`);
253
+ }
254
+ }
255
+ /**
256
+ * 执行条件步骤
257
+ */
258
+ async executeCondition(step, context) {
259
+ this._log.debug(` Evaluating condition: ${step.name || step.id}`);
260
+ for (const branch of step.branches || []) {
261
+ try {
262
+ let conditionValue;
263
+ if (typeof branch.condition === 'function') {
264
+ conditionValue = branch.condition(context);
265
+ } else if (typeof branch.condition === 'string') {
266
+ // 使用沙箱评估条件,防止恶意代码执行
267
+ conditionValue = await evaluateInSandbox(branch.condition, context, { timeout: 5000 });
268
+ }
269
+ if (conditionValue) {
270
+ this._log.debug(` Condition matched: ${branch.name || branch.stepId}`);
271
+ if (branch.steps && branch.steps.length > 0) {
272
+ await this.executeSteps(branch.steps, context);
273
+ }
274
+ return branch.stepId || branch.name;
275
+ }
276
+ } catch (err) {
277
+ this._log.warn(` Branch condition error:`, err.message);
278
+ }
279
+ }
280
+ // 执行默认分支
281
+ if (step.defaultBranch && step.defaultBranch.steps) {
282
+ this._log.debug(` Executing default branch`);
283
+ await this.executeSteps(step.defaultBranch.steps, context);
284
+ }
285
+ return null;
286
+ }
287
+ /**
288
+ * 执行循环步骤
289
+ */
290
+ async executeLoop(step, context) {
291
+ this._log.debug(` Executing loop: ${step.name || step.id}`);
292
+ const results = [];
293
+ const maxIterations = step.maxIterations || 10;
294
+ const loopVariable = step.loopVariable || 'loopIndex';
295
+ for (let i = 0; i < maxIterations; i++) {
296
+ context.variables[loopVariable] = i;
297
+ this._log.debug(` Loop iteration ${i}`);
298
+ const iterationResults = [];
299
+ for (const stepConfig of step.steps || []) {
300
+ const result = await this.executeStep(stepConfig, context);
301
+ iterationResults.push(result);
302
+ }
303
+ results.push(iterationResults);
304
+ // 检查终止条件
305
+ if (step.until) {
306
+ let shouldStop = false;
307
+ if (typeof step.until === 'function') {
308
+ shouldStop = step.until(context);
309
+ } else if (typeof step.until === 'string') {
310
+ // 使用沙箱评估循环条件,防止恶意代码执行
311
+ shouldStop = await evaluateInSandbox(step.until, context, { timeout: 5000 });
312
+ }
313
+ if (shouldStop) {
314
+ this._log.debug(` Loop terminated at iteration ${i}`);
315
+ break;
316
+ }
317
+ }
318
+ }
319
+ return results;
320
+ }
321
+ /**
322
+ * 执行延迟步骤
323
+ */
324
+ async executeDelay(step, context) {
325
+ const delayMs = step.delayMs || 1000;
326
+ this._log.debug(` Delaying ${delayMs}ms`);
327
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
328
+ return null;
329
+ }
330
+ /**
331
+ * 执行顺序步骤
332
+ */
333
+ async executeSequential(step, context) {
334
+ this._log.debug(` Executing sequential: ${step.name || step.id}`);
335
+ return await this.executeSteps(step.steps || [], context);
336
+ }
337
+ /**
338
+ * 执行消息步骤(ExplorerLoop 专用)
339
+ */
340
+ async executeMessage(step, context) {
341
+ this._log.debug(` Executing message step`);
342
+ // 使用子Agent处理消息,避免阻塞主agent
343
+ const messageAgent = this.framework.createSubAgent({
344
+ name: 'workflow_message',
345
+ role: '工作流任务执行助手,专注于处理工作流中的消息任务',
346
+ });
347
+ let content = step.content || '';
348
+ // 支持变量引用
349
+ content = this._resolveValue(content, context);
350
+ // 如果有事件上下文,附加到消息
351
+ if (context.variables?._event) {
352
+ content = `${content}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`;
353
+ }
354
+ const result = await messageAgent.chat(content);
355
+ return { success: true, result };
356
+ }
357
+ /**
358
+ * 执行思考步骤(ExplorerLoop 专用)
359
+ */
360
+ async executeThink(step, context) {
361
+ this._log.debug(` Executing think step`);
362
+ const thinkPlugin = this.framework.pluginManager?.get('think');
363
+ if (!thinkPlugin) {
364
+ return { success: false, error: '思考插件不可用' };
365
+ }
366
+ let topic = step.topic || 'Ambient代理反思';
367
+ topic = this._resolveValue(topic, context);
368
+ if (context.variables?._event) {
369
+ topic = `${topic}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`;
370
+ }
371
+ const result = await thinkPlugin._triggerThinking({
372
+ topic,
373
+ mode: step.mode || 'reflect',
374
+ depth: step.depth || 2,
375
+ });
376
+ return result;
377
+ }
378
+ /**
379
+ * 获取活跃代理
380
+ */
381
+ _getActiveAgent() {
382
+ if (this.framework._mainAgent) {
383
+ return this.framework._mainAgent;
384
+ }
385
+ const agents = this.framework._agents || [];
386
+ return agents.length > 0 ? agents[agents.length - 1] : null;
387
+ }
388
+ /**
389
+ * 解析参数中的变量引用
390
+ */
391
+ _resolveArgs(args, context) {
392
+ const resolved = {};
393
+ for (const [key, value] of Object.entries(args)) {
394
+ resolved[key] = this._resolveValue(value, context);
395
+ }
396
+ return resolved;
397
+ }
398
+ _resolveValue(value, context) {
399
+ if (typeof value === 'string') {
400
+ // 支持多种引用格式:
401
+ // {{result}} - 上一步结果 (lastResult)
402
+ // {{result.body.ip}} - 从上一步结果提取嵌套字段
403
+ // {{variables.xxx}} - 显式引用 context.variables
404
+ // {{context.xxx}} - 显式引用 context 根属性
405
+ // {{lastResult}} - 上一步结果(result 的别名)
406
+ // ${stepId.output} - 引用指定 id 步骤的输出(兼容旧格式)
407
+ // ${stepId.output.path} - 从指定步骤输出提取字段
408
+ let result = value;
409
+ // 先处理 ${stepId.output} 格式(兼容旧格式)
410
+ result = result.replace(/\$\{([^}]+)\}/g, (match, path) => {
411
+ const stepOutputs = context.variables._stepOutputs || {};
412
+ if (path === 'output') {
413
+ // ${output} 等同于 ${stepId.output} 引用上一步
414
+ return context.lastResult ?? match;
415
+ }
416
+ if (path.includes('.')) {
417
+ // ${stepId.output.field} 格式
418
+ const [stepId, ...fieldParts] = path.split('.');
419
+ const stepOutput = stepOutputs[stepId];
420
+ if (stepOutput !== undefined) {
421
+ const val = this._getNestedValue(stepOutput, fieldParts.join('.'));
422
+ if (val !== undefined) return val;
423
+ }
424
+ } else {
425
+ // ${stepId.output} 格式
426
+ const stepOutput = stepOutputs[path];
427
+ if (stepOutput !== undefined) return stepOutput;
428
+ }
429
+ return match;
430
+ });
431
+ // 再处理 {{...}} 格式(支持 _event.email.from 等嵌套属性)
432
+ result = result.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
433
+ // result 和 lastResult 都指向 context.lastResult
434
+ if (path === 'result' || path === 'lastResult') {
435
+ return context.lastResult ?? match;
436
+ }
437
+ if (path.startsWith('result.') || path.startsWith('lastResult.')) {
438
+ const nestedPath = path.replace(/^(result|lastResult)\./, '');
439
+ const val = this._getNestedValue(context.lastResult, nestedPath);
440
+ if (val !== undefined) return val;
441
+ return match;
442
+ }
443
+ if (path.startsWith('variables.')) {
444
+ return this._getNestedValue(context, path) ?? match;
445
+ } else if (path.startsWith('context.')) {
446
+ return this._getNestedValue(context, path) ?? match;
447
+ } else {
448
+ // 先从 variables 查找,再从 context 根属性查找
449
+ const val = this._getNestedValue(context.variables, path);
450
+ if (val !== undefined) return val;
451
+ return this._getNestedValue(context, path) ?? match;
452
+ }
453
+ });
454
+ return result;
455
+ } else if (Array.isArray(value)) {
456
+ return value.map((v) => this._resolveValue(v, context));
457
+ } else if (typeof value === 'object' && value !== null) {
458
+ const resolved = {};
459
+ for (const [k, v] of Object.entries(value)) {
460
+ resolved[k] = this._resolveValue(v, context);
461
+ }
462
+ return resolved;
463
+ }
464
+ return value;
465
+ }
466
+ _getNestedValue(obj, path) {
467
+ if (!obj) return undefined;
468
+ const parts = path.split('.');
469
+ let current = obj;
470
+ for (const part of parts) {
471
+ if (current === null || current === undefined) return undefined;
472
+ // 如果当前值是字符串,尝试解析为 JSON
473
+ if (typeof current === 'string' && part !== '0' && part !== 'length') {
474
+ try {
475
+ current = JSON.parse(current);
476
+ } catch {
477
+ // 解析失败,继续用原值
478
+ }
479
+ }
480
+ current = current[part];
481
+ }
482
+ return current;
483
+ }
484
+ }
485
+ /**
486
+ * 工作流步骤基类
487
+ */
488
+ class WorkflowStep {
489
+ constructor(config) {
490
+ this.id = config.id || `step_${Date.now()}_${crypto.randomBytes(6).toString('base64url')}`;
491
+ this.type = config.type;
492
+ this.name = config.name || '';
493
+ this.description = config.description || '';
494
+ this.condition = config.condition || null;
495
+ this.timeout = config.timeout || 30000;
496
+ this.retry = config.retry || { enabled: false, maxAttempts: 3, delay: 1000 };
497
+ this.onSuccess = config.onSuccess || null;
498
+ this.onFailure = config.onFailure || null;
499
+ }
500
+ async execute(context, engine) {
501
+ throw new Error('execute() must be implemented by subclass');
502
+ }
503
+ validate() {
504
+ if (!this.type) {
505
+ throw new Error('Step must have a type');
506
+ }
507
+ return true;
508
+ }
509
+ }
510
+ /**
511
+ * 脚本步骤
512
+ */
513
+ class ScriptStep extends WorkflowStep {
514
+ constructor(config) {
515
+ super({ ...config, type: StepType.SCRIPT });
516
+ this.script = config.script;
517
+ this.outputVariable = config.outputVariable || null;
518
+ }
519
+ async execute(context, engine) {
520
+ // 使用 StepExecutor 执行脚本逻辑
521
+ const executor = new StepExecutor(engine.framework);
522
+ const stepConfig = {
523
+ id: this.id,
524
+ name: this.name,
525
+ type: StepType.SCRIPT,
526
+ script: this.script,
527
+ outputVariable: this.outputVariable,
528
+ };
529
+ return await executor.executeScript(stepConfig, context);
530
+ }
531
+ }
532
+ /**
533
+ * 条件步骤
534
+ */
535
+ class ConditionStep extends WorkflowStep {
536
+ constructor(config) {
537
+ super({ ...config, type: StepType.CONDITION });
538
+ this.branches = config.branches || [];
539
+ this.defaultBranch = config.defaultBranch || null;
540
+ }
541
+ async execute(context, engine) {
542
+ const executor = new StepExecutor(engine.framework);
543
+ const stepConfig = {
544
+ id: this.id,
545
+ name: this.name,
546
+ type: StepType.CONDITION,
547
+ branches: this.branches,
548
+ defaultBranch: this.defaultBranch,
549
+ };
550
+ return await executor.executeCondition(stepConfig, context);
551
+ }
552
+ }
553
+ /**
554
+ * 并行步骤
555
+ */
556
+ class ParallelStep extends WorkflowStep {
557
+ constructor(config) {
558
+ super({ ...config, type: StepType.PARALLEL });
559
+ this.steps = config.steps || [];
560
+ }
561
+ async execute(context, engine) {
562
+ const executor = new StepExecutor(engine.framework);
563
+ const stepConfig = {
564
+ id: this.id,
565
+ name: this.name,
566
+ type: StepType.PARALLEL,
567
+ steps: this.steps,
568
+ };
569
+ return await executor.executeParallel(stepConfig, context);
570
+ }
571
+ }
572
+ /**
573
+ * Switch 步骤
574
+ */
575
+ class SwitchStep extends WorkflowStep {
576
+ constructor(config) {
577
+ super({ ...config, type: StepType.SWITCH });
578
+ this.value = config.value;
579
+ this.branches = config.branches || [];
580
+ this.default = config.default || null;
581
+ }
582
+ async execute(context, engine) {
583
+ const executor = new StepExecutor(engine.framework);
584
+ const stepConfig = {
585
+ id: this.id,
586
+ name: this.name,
587
+ type: StepType.SWITCH,
588
+ value: this.value,
589
+ branches: this.branches,
590
+ default: this.default,
591
+ };
592
+ return await executor.executeSwitch(stepConfig, context);
593
+ }
594
+ }
595
+ /**
596
+ * Try-Catch 步骤
597
+ */
598
+ class TryStep extends WorkflowStep {
599
+ constructor(config) {
600
+ super({ ...config, type: StepType.TRY });
601
+ this.try = config.try || {};
602
+ this.catch = config.catch || {};
603
+ }
604
+ async execute(context, engine) {
605
+ const executor = new StepExecutor(engine.framework);
606
+ const stepConfig = {
607
+ id: this.id,
608
+ name: this.name,
609
+ type: StepType.TRY,
610
+ try: this.try,
611
+ catch: this.catch,
612
+ };
613
+ return await executor.executeTry(stepConfig, context);
614
+ }
615
+ }
616
+ /**
617
+ * 嵌套工作流步骤
618
+ */
619
+ class NestedWorkflowStep extends WorkflowStep {
620
+ constructor(config) {
621
+ super({ ...config, type: StepType.WORKFLOW });
622
+ this.workflow = config.workflow;
623
+ this.input = config.input || {};
624
+ }
625
+ async execute(context, engine) {
626
+ const executor = new StepExecutor(engine.framework);
627
+ const stepConfig = {
628
+ id: this.id,
629
+ name: this.name,
630
+ type: StepType.WORKFLOW,
631
+ workflow: this.workflow,
632
+ input: this.input,
633
+ };
634
+ return await executor.executeWorkflowStep(stepConfig, context);
635
+ }
636
+ }
637
+ /**
638
+ * 顺序步骤
639
+ */
640
+ class SequentialStep extends WorkflowStep {
641
+ constructor(config) {
642
+ super({ ...config, type: StepType.SEQUENTIAL });
643
+ this.steps = config.steps || [];
644
+ }
645
+ async execute(context, engine) {
646
+ const executor = new StepExecutor(engine.framework);
647
+ const stepConfig = {
648
+ id: this.id,
649
+ name: this.name,
650
+ type: StepType.SEQUENTIAL,
651
+ steps: this.steps,
652
+ };
653
+ return await executor.executeSequential(stepConfig, context);
654
+ }
655
+ }
656
+ /**
657
+ * 循环步骤
658
+ */
659
+ class LoopStep extends WorkflowStep {
660
+ constructor(config) {
661
+ super({ ...config, type: StepType.LOOP });
662
+ this.maxIterations = config.maxIterations || 10;
663
+ this.loopVariable = config.loopVariable || 'loopIndex';
664
+ this.steps = config.steps || [];
665
+ this.until = config.until || null;
666
+ }
667
+ async execute(context, engine) {
668
+ const executor = new StepExecutor(engine.framework);
669
+ const stepConfig = {
670
+ id: this.id,
671
+ name: this.name,
672
+ type: StepType.LOOP,
673
+ maxIterations: this.maxIterations,
674
+ loopVariable: this.loopVariable,
675
+ steps: this.steps,
676
+ until: this.until,
677
+ };
678
+ return await executor.executeLoop(stepConfig, context);
679
+ }
680
+ }
681
+ /**
682
+ * 延迟步骤
683
+ */
684
+ class DelayStep extends WorkflowStep {
685
+ constructor(config) {
686
+ super({ ...config, type: StepType.DELAY });
687
+ this.delayMs = config.delayMs || 1000;
688
+ }
689
+ async execute(context, engine) {
690
+ const executor = new StepExecutor(engine.framework);
691
+ const stepConfig = {
692
+ id: this.id,
693
+ name: this.name,
694
+ type: StepType.DELAY,
695
+ delayMs: this.delayMs,
696
+ };
697
+ return await executor.executeDelay(stepConfig, context);
698
+ }
699
+ }
700
+ /**
701
+ * 工具步骤
702
+ */
703
+ class ToolStep extends WorkflowStep {
704
+ constructor(config) {
705
+ super({ ...config, type: StepType.TOOL });
706
+ this.tool = config.tool;
707
+ this.args = config.args || {};
708
+ this.outputVariable = config.outputVariable || null;
709
+ this.output = config.output || null; // 兼容旧格式
710
+ this.input = config.input || null; // 兼容旧格式:input 字段
711
+ }
712
+ async execute(context, engine) {
713
+ const executor = new StepExecutor(engine.framework);
714
+ const stepConfig = {
715
+ id: this.id,
716
+ name: this.name,
717
+ type: StepType.TOOL,
718
+ tool: this.tool,
719
+ args: this.args,
720
+ outputVariable: this.outputVariable,
721
+ output: this.output, // 传递 output
722
+ input: this.input, // 传递 input
723
+ };
724
+ return await executor.executeTool(stepConfig, context);
725
+ }
726
+ }
727
+ /**
728
+ * 工作流引擎
729
+ */
730
+ class WorkflowEngine extends EventEmitter {
731
+ constructor(framework) {
732
+ super();
733
+ this.framework = framework;
734
+ this._steps = new Map();
735
+ }
736
+ /**
737
+ * 注册自定义步骤类型
738
+ */
739
+ registerStepType(type, StepClass) {
740
+ this._steps.set(type, StepClass);
741
+ }
742
+ /**
743
+ * 创建步骤实例
744
+ * 自动推断步骤类型:
745
+ * - 有 tool 字段 -> tool 类型
746
+ * - 有 code/script 字段 -> script 类型
747
+ * - 有 branches 字段 -> condition 类型
748
+ * - 有 value + branches -> switch 类型
749
+ * - 有 try/catch 字段 -> try 类型
750
+ * - 有 parallel 属性 -> parallel 类型
751
+ * - 有 steps 字段 + loopVariable/maxIterations -> loop 类型
752
+ * - 有 steps 字段 -> sequential 类型
753
+ * - 有 delayMs 字段 -> delay 类型
754
+ * - 有 workflow 字段 -> workflow 类型
755
+ */
756
+ createStep(config) {
757
+ let type = config.type;
758
+ // 自动推断类型
759
+ if (!type) {
760
+ if (config.tool) {
761
+ type = StepType.TOOL;
762
+ } else if (config.code || config.script) {
763
+ type = StepType.SCRIPT;
764
+ } else if (config.try || config.catch) {
765
+ type = StepType.TRY;
766
+ } else if (config.value !== undefined && config.branches) {
767
+ type = StepType.SWITCH;
768
+ } else if (config.parallel === true || config.mode === 'parallel') {
769
+ type = StepType.PARALLEL;
770
+ } else if (config.workflow) {
771
+ type = StepType.WORKFLOW;
772
+ } else if (config.branches) {
773
+ type = StepType.CONDITION;
774
+ } else if (config.steps && (config.loopVariable || config.maxIterations)) {
775
+ type = StepType.LOOP;
776
+ } else if (config.steps) {
777
+ type = StepType.SEQUENTIAL;
778
+ } else if (config.delayMs !== undefined) {
779
+ type = StepType.DELAY;
780
+ } else if (config.message) {
781
+ type = StepType.MESSAGE;
782
+ } else if (config.topic) {
783
+ type = StepType.THINK;
784
+ }
785
+ }
786
+ if (!type) {
787
+ throw new Error(
788
+ `Cannot infer step type for step: ${JSON.stringify(config).substring(0, 100)}`
789
+ );
790
+ }
791
+ const StepClass = this._steps.get(type);
792
+ if (!StepClass) {
793
+ throw new Error(`Unknown step type: ${type}`);
794
+ }
795
+ // 确保配置有 type 字段
796
+ return new StepClass({ ...config, type });
797
+ }
798
+ /**
799
+ * 创建工作流上下文
800
+ */
801
+ createContext(input = {}, variables = {}) {
802
+ return {
803
+ input,
804
+ variables,
805
+ lastResult: null,
806
+ results: [],
807
+ };
808
+ }
809
+ }
810
+ /**
811
+ * WorkflowManagerPlugin
812
+ */
813
+ class WorkflowPlugin extends Plugin {
814
+ constructor(config = {}) {
815
+ super();
816
+ this.name = 'workflow';
817
+ this.version = '1.0.0';
818
+ this.description = '工作流引擎,支持结构化工作流定义和执行';
819
+ this.priority = 6;
820
+ this.system = true;
821
+ this._framework = null;
822
+ this._engine = null;
823
+ this._workflowsDir = config.workflowsDir || '.agent/workflows';
824
+ this._workflows = new Map();
825
+ this._workflowTools = new Map();
826
+ }
827
+ install(framework) {
828
+ this._framework = framework;
829
+ this._engine = new WorkflowEngine(framework);
830
+ // 注册内置步骤类型
831
+ this._engine.registerStepType(StepType.SCRIPT, ScriptStep);
832
+ this._engine.registerStepType(StepType.CONDITION, ConditionStep);
833
+ this._engine.registerStepType(StepType.SWITCH, SwitchStep);
834
+ this._engine.registerStepType(StepType.TRY, TryStep);
835
+ this._engine.registerStepType(StepType.PARALLEL, ParallelStep);
836
+ this._engine.registerStepType(StepType.SEQUENTIAL, SequentialStep);
837
+ this._engine.registerStepType(StepType.LOOP, LoopStep);
838
+ this._engine.registerStepType(StepType.DELAY, DelayStep);
839
+ this._engine.registerStepType(StepType.TOOL, ToolStep);
840
+ this._engine.registerStepType(StepType.WORKFLOW, NestedWorkflowStep);
841
+ // 注册工作流执行工具
842
+ framework.registerTool({
843
+ name: 'execute_workflow',
844
+ description: '执行指定的工作流',
845
+ inputSchema: z.object({
846
+ workflow: z.string().describe('工作流定义(JSON 或 JavaScript 代码)'),
847
+ input: z.object({}).optional().describe('工作流输入参数'),
848
+ }),
849
+ execute: async (args, framework) => {
850
+ // 获取当前 sessionId
851
+ let sessionId = null;
852
+ const ctx = framework.getExecutionContext();
853
+ if (ctx?.sessionId) {
854
+ sessionId = ctx.sessionId;
855
+ }
856
+ return await this.executeWorkflow(args.workflow, args.input || {}, sessionId);
857
+ },
858
+ });
859
+ return this;
860
+ }
861
+ start(framework) {
862
+ this._loadWorkflows();
863
+ this._registerWorkflowTools();
864
+ // 注册 reloadWorkflows 工具
865
+ framework.registerTool({
866
+ name: 'reloadWorkflows',
867
+ description: '重载所有工作流,当用户添加或修改工作流后调用此工具',
868
+ inputSchema: z.object({}),
869
+ execute: async () => {
870
+ this.reload(this._framework);
871
+ return {
872
+ success: true,
873
+ message: `Workflows reloaded. Total: ${this._workflows.size}`,
874
+ workflows: Array.from(this._workflows.keys()),
875
+ };
876
+ },
877
+ });
878
+ return this;
879
+ }
880
+ /**
881
+ * 加载目录中的工作流定义
882
+ */
883
+ _loadWorkflows() {
884
+ const dir = path.resolve(process.cwd(), this._workflowsDir);
885
+ if (!fs.existsSync(dir)) {
886
+ fs.mkdirSync(dir, { recursive: true });
887
+ log.info(` Created workflows directory: ${dir}`);
888
+ return;
889
+ }
890
+ try {
891
+ const files = fs.readdirSync(dir);
892
+ for (const file of files) {
893
+ if (!file.endsWith('.json') && !file.endsWith('.js')) continue;
894
+ const filePath = path.join(dir, file);
895
+ const workflowName = path.basename(file, path.extname(file));
896
+ // 跳过已存在的工作流
897
+ if (this._workflows.has(workflowName)) continue;
898
+ try {
899
+ const content = fs.readFileSync(filePath, 'utf-8');
900
+ let workflowDef;
901
+ if (file.endsWith('.js')) {
902
+ // 执行 JS 文件获取工作流定义
903
+ // 使用沙箱加载工作流文件,防止恶意代码执行
904
+ workflowDef = runWorkflowFileSafely(content, { timeout: 10000 });
905
+ if (typeof workflowDef === 'function') {
906
+ workflowDef = workflowDef(this._engine || {});
907
+ }
908
+ workflowDef = workflowDef.default || workflowDef;
909
+ } else {
910
+ workflowDef = JSON.parse(content);
911
+ }
912
+ if (workflowDef && workflowDef.steps) {
913
+ this._workflows.set(workflowName, workflowDef);
914
+ //log.info(` Loaded: ${workflowName}`)
915
+ }
916
+ } catch (err) {
917
+ log.error(` Failed to load ${file}:`, err.message);
918
+ }
919
+ }
920
+ } catch (err) {
921
+ log.error(' Failed to read workflows directory:', err.message);
922
+ }
923
+ }
924
+ /**
925
+ * 注册工作流为工具
926
+ */
927
+ _registerWorkflowTools() {
928
+ for (const [name, workflow] of this._workflows) {
929
+ if (this._workflowTools.has(name)) continue;
930
+ const toolName = `workflow_${name}`;
931
+ const description = workflow.description || `执行工作流: ${name}`;
932
+ this._framework.registerTool({
933
+ name: toolName,
934
+ description,
935
+ inputSchema: z.object({
936
+ input: z.object({}).optional().describe('工作流输入参数'),
937
+ }),
938
+ execute: async (args, framework) => {
939
+ // Get current sessionId to ensure notifications reach the correct session
940
+ let sessionId = null;
941
+ const ctx = framework?.getExecutionContext?.();
942
+ if (ctx?.sessionId) {
943
+ sessionId = ctx.sessionId;
944
+ }
945
+ return await this.executeWorkflow(workflow, args.input || {}, sessionId);
946
+ },
947
+ });
948
+ this._workflowTools.set(name, toolName);
949
+ log.info(` Registered tool: ${toolName}`);
950
+ }
951
+ }
952
+ /**
953
+ * 重载工作流
954
+ */
955
+ reload(framework) {
956
+ // 清除模块缓存,确保重新加载最新代码
957
+ const modulePath = require.resolve('./src/capabilities/workflow-engine');
958
+ if (require.cache[modulePath]) {
959
+ delete require.cache[modulePath];
960
+ }
961
+ log.info(' Reloading...');
962
+ this._framework = framework;
963
+ // 重新创建 engine(确保使用最新代码)
964
+ this._engine = new (require('./src/capabilities/workflow-engine').WorkflowEngine)(framework);
965
+ // 注册内置步骤类型
966
+ this._engine.registerStepType(
967
+ require('./src/capabilities/workflow-engine').StepType.SCRIPT,
968
+ require('./src/capabilities/workflow-engine').ScriptStep
969
+ );
970
+ this._engine.registerStepType(
971
+ require('./src/capabilities/workflow-engine').StepType.TOOL,
972
+ require('./src/capabilities/workflow-engine').ToolStep
973
+ );
974
+ this._engine.registerStepType(
975
+ require('./src/capabilities/workflow-engine').StepType.CONDITION,
976
+ require('./src/capabilities/workflow-engine').ConditionStep
977
+ );
978
+ this._engine.registerStepType(
979
+ require('./src/capabilities/workflow-engine').StepType.SWITCH,
980
+ require('./src/capabilities/workflow-engine').SwitchStep
981
+ );
982
+ this._engine.registerStepType(
983
+ require('./src/capabilities/workflow-engine').StepType.TRY,
984
+ require('./src/capabilities/workflow-engine').TryStep
985
+ );
986
+ this._engine.registerStepType(
987
+ require('./src/capabilities/workflow-engine').StepType.PARALLEL,
988
+ require('./src/capabilities/workflow-engine').ParallelStep
989
+ );
990
+ this._engine.registerStepType(
991
+ require('./src/capabilities/workflow-engine').StepType.SEQUENTIAL,
992
+ require('./src/capabilities/workflow-engine').SequentialStep
993
+ );
994
+ this._engine.registerStepType(
995
+ require('./src/capabilities/workflow-engine').StepType.LOOP,
996
+ require('./src/capabilities/workflow-engine').LoopStep
997
+ );
998
+ this._engine.registerStepType(
999
+ require('./src/capabilities/workflow-engine').StepType.DELAY,
1000
+ require('./src/capabilities/workflow-engine').DelayStep
1001
+ );
1002
+ this._engine.registerStepType(
1003
+ require('./src/capabilities/workflow-engine').StepType.WORKFLOW,
1004
+ require('./src/capabilities/workflow-engine').NestedWorkflowStep
1005
+ );
1006
+ // 清除已注册的工具
1007
+ for (const toolName of this._workflowTools.values()) {
1008
+ // 工具注销需要框架支持,这里只清理内部状态
1009
+ }
1010
+ this._workflowTools.clear();
1011
+ this._workflows.clear();
1012
+ // 重新加载
1013
+ this._loadWorkflows();
1014
+ this._registerWorkflowTools();
1015
+ log.info(` Reloaded. Total workflows: ${this._workflows.size}`);
1016
+ }
1017
+ /**
1018
+ * 执行工作流
1019
+ */
1020
+ async executeWorkflow(workflowDef, input = {}, sessionId = null) {
1021
+ try {
1022
+ let workflow;
1023
+ if (typeof workflowDef === 'string') {
1024
+ // 尝试作为工作流名称加载(先检查是否已加载的工作流)
1025
+ const workflowName = workflowDef.trim();
1026
+ if (this._workflows.has(workflowName)) {
1027
+ workflow = this._workflows.get(workflowName);
1028
+ } else {
1029
+ // 尝试解析 JSON 或执行代码
1030
+ try {
1031
+ workflow = JSON.parse(workflowDef);
1032
+ } catch {
1033
+ // 使用沙箱解析工作流定义,防止恶意代码执行
1034
+ workflow = runWorkflowSafely(workflowDef, this._engine, { timeout: 5000 });
1035
+ }
1036
+ }
1037
+ } else {
1038
+ workflow = workflowDef;
1039
+ }
1040
+ const context = this._engine.createContext(input, {});
1041
+ // 将 sessionId 存储到上下文变量,供工具步骤使用
1042
+ if (sessionId) {
1043
+ context.variables._sessionId = sessionId;
1044
+ }
1045
+ // 执行工作流步骤
1046
+ if (workflow.steps && Array.isArray(workflow.steps)) {
1047
+ const results = [];
1048
+ // 按 id 存储步骤输出,供 ${id.output} 引用
1049
+ context.variables._stepOutputs = {};
1050
+ for (const stepConfig of workflow.steps) {
1051
+ const step = this._engine.createStep(stepConfig);
1052
+ const result = await step.execute(context, this._engine);
1053
+ results.push(result);
1054
+ // 如果步骤有 id,存储其输出
1055
+ if (stepConfig.id) {
1056
+ context.variables._stepOutputs[stepConfig.id] = result;
1057
+ }
1058
+ }
1059
+ // 只返回最后一步的结果(包含最终输出)和成功状态
1060
+ // 不返回所有中间结果,避免上下文超限
1061
+ const lastResult = results.length > 0 ? results[results.length - 1] : null;
1062
+ // 从 context.variables 中提取非下划线开头的用户变量
1063
+ // 下划线开头的是内部变量(如 _stepOutputs),不需要返回
1064
+ const userVariables = {};
1065
+ for (const [key, value] of Object.entries(context.variables)) {
1066
+ if (!key.startsWith('_')) {
1067
+ userVariables[key] = value;
1068
+ }
1069
+ }
1070
+ return {
1071
+ success: true,
1072
+ stepCount: workflow.steps.length,
1073
+ result: lastResult,
1074
+ output: userVariables,
1075
+ };
1076
+ }
1077
+ return { success: true, output: {} };
1078
+ } catch (err) {
1079
+ return { success: false, error: err.message };
1080
+ }
1081
+ }
1082
+ /**
1083
+ * 获取引擎
1084
+ */
1085
+ getEngine() {
1086
+ return this._engine;
1087
+ }
1088
+ uninstall(framework) {
1089
+ this._engine = null;
1090
+ this._framework = null;
1091
+ }
1092
+ }
1093
+ module.exports = {
1094
+ WorkflowPlugin,
1095
+ WorkflowEngine,
1096
+ WorkflowStep,
1097
+ StepType,
1098
+ StepExecutor,
1099
+ ScriptStep,
1100
+ ConditionStep,
1101
+ ParallelStep,
1102
+ SwitchStep,
1103
+ TryStep,
1104
+ NestedWorkflowStep,
1105
+ SequentialStep,
1106
+ LoopStep,
1107
+ DelayStep,
1108
+ ToolStep,
1109
+ };