@ai-setting/roy-agent-core 1.5.43 → 1.5.44

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 (24) hide show
  1. package/dist/env/index.js +7 -6
  2. package/dist/env/tool/built-in/index.js +1 -1
  3. package/dist/env/tool/index.js +2 -2
  4. package/dist/env/workflow/decorators/index.js +1 -1
  5. package/dist/env/workflow/engine/index.js +4 -3
  6. package/dist/env/workflow/index.js +34 -19
  7. package/dist/env/workflow/nodes/index.js +5 -1
  8. package/dist/env/workflow/types/index.js +16 -2
  9. package/dist/env/workflow/utils/index.js +15 -196
  10. package/dist/index.js +9 -8
  11. package/dist/shared/@ai-setting/{roy-agent-core-ffb9fq4v.js → roy-agent-core-23gw9c4s.js} +2 -2
  12. package/dist/shared/@ai-setting/{roy-agent-core-bmr6bdfb.js → roy-agent-core-2ms7296b.js} +4 -4
  13. package/dist/shared/@ai-setting/{roy-agent-core-2jnzv9at.js → roy-agent-core-38dkek2y.js} +319 -189
  14. package/dist/shared/@ai-setting/roy-agent-core-6vxg2gmr.js +321 -0
  15. package/dist/shared/@ai-setting/{roy-agent-core-0rtxwr28.js → roy-agent-core-9bmtxmp6.js} +77 -120
  16. package/dist/shared/@ai-setting/{roy-agent-core-7fgf85wc.js → roy-agent-core-h0x19xgn.js} +6 -7
  17. package/dist/shared/@ai-setting/roy-agent-core-qnrf2aw6.js +441 -0
  18. package/dist/shared/@ai-setting/roy-agent-core-v002ynpa.js +435 -0
  19. package/dist/shared/@ai-setting/{roy-agent-core-rsybkb38.js → roy-agent-core-ysvh8er9.js} +36 -39
  20. package/dist/shared/@ai-setting/{roy-agent-core-9yxb3ty9.js → roy-agent-core-z5sxe4p7.js} +5 -1
  21. package/package.json +1 -1
  22. package/dist/shared/@ai-setting/roy-agent-core-5x94xmt6.js +0 -350
  23. package/dist/shared/@ai-setting/roy-agent-core-jvatggbb.js +0 -603
  24. /package/dist/shared/@ai-setting/{roy-agent-core-e130w7mv.js → roy-agent-core-ryw3ckfy.js} +0 -0
@@ -9,12 +9,18 @@ import {
9
9
  init_skill_node,
10
10
  init_tool_node,
11
11
  init_workflow_node
12
- } from "./roy-agent-core-jvatggbb.js";
12
+ } from "./roy-agent-core-6vxg2gmr.js";
13
13
  import {
14
+ TemplateResolver,
15
+ init_template_resolver
16
+ } from "./roy-agent-core-v002ynpa.js";
17
+ import {
18
+ WorkflowError,
14
19
  createWorkflowEvent,
15
20
  init_event,
16
- init_types
17
- } from "./roy-agent-core-5x94xmt6.js";
21
+ init_types,
22
+ init_workflow_error
23
+ } from "./roy-agent-core-qnrf2aw6.js";
18
24
  import {
19
25
  AskUserError,
20
26
  createNodeInterruptEvent,
@@ -129,12 +135,14 @@ class DAGManager {
129
135
  nodeMap;
130
136
  extendedDefinition = null;
131
137
  cachedAnalysis = null;
132
- constructor(workflow) {
138
+ allowCycles;
139
+ constructor(workflow, options) {
133
140
  if (!workflow.nodes || workflow.nodes.length === 0) {
134
141
  throw new Error("At least one node is required");
135
142
  }
136
143
  this.workflow = workflow;
137
144
  this.nodeMap = new Map;
145
+ this.allowCycles = options?.allowCycles ?? false;
138
146
  for (const node of workflow.nodes) {
139
147
  if (node.id === "") {
140
148
  throw new Error("Node ID cannot be empty");
@@ -386,7 +394,7 @@ class DAGManager {
386
394
  }
387
395
  return result;
388
396
  }
389
- validate() {
397
+ validate(options) {
390
398
  const errors = [];
391
399
  const nodeIds = new Set;
392
400
  for (const node of this.workflow.nodes) {
@@ -407,7 +415,7 @@ class DAGManager {
407
415
  }
408
416
  }
409
417
  }
410
- if (this.isCyclic()) {
418
+ if (!this.allowCycles && !options?.skipCycleCheck && this.isCyclic()) {
411
419
  errors.push("Workflow contains a cycle");
412
420
  }
413
421
  return {
@@ -455,6 +463,27 @@ class DAGManager {
455
463
  const analysis = this.analyze();
456
464
  return analysis.dependents.get(nodeId) || [];
457
465
  }
466
+ getDownstreamUntil(fromNodeId, untilNodeId) {
467
+ const result = [];
468
+ const visited = new Set;
469
+ const queue = [...this.getDependents(fromNodeId)];
470
+ while (queue.length > 0) {
471
+ const nodeId = queue.shift();
472
+ if (visited.has(nodeId))
473
+ continue;
474
+ visited.add(nodeId);
475
+ result.push(nodeId);
476
+ if (nodeId === untilNodeId) {
477
+ continue;
478
+ }
479
+ for (const dependent of this.getDependents(nodeId)) {
480
+ if (!visited.has(dependent)) {
481
+ queue.push(dependent);
482
+ }
483
+ }
484
+ }
485
+ return result;
486
+ }
458
487
  getDefinition() {
459
488
  return this.workflow;
460
489
  }
@@ -467,6 +496,20 @@ class DAGManager {
467
496
  getNodeCount() {
468
497
  return this.nodeMap.size;
469
498
  }
499
+ buildEdges(explicitEdges = []) {
500
+ const derivedEdges = [];
501
+ const explicitEdgeSet = new Set(explicitEdges.map((e) => `${e.from}->${e.to}`));
502
+ for (const node of this.workflow.nodes) {
503
+ const deps = node.depends_on || [];
504
+ for (const dep of deps) {
505
+ const edgeKey = `${dep}->${node.id}`;
506
+ if (!explicitEdgeSet.has(edgeKey)) {
507
+ derivedEdges.push({ from: dep, to: node.id });
508
+ }
509
+ }
510
+ }
511
+ return [...explicitEdges, ...derivedEdges];
512
+ }
470
513
  }
471
514
  var init_dag_manager = () => {};
472
515
 
@@ -542,6 +585,10 @@ class Scheduler {
542
585
  return this.ready.has(nodeId);
543
586
  }
544
587
  markCompleted(nodeId) {
588
+ if (nodeId === "__end__" || nodeId === "__END__") {
589
+ logger.debug(`[Scheduler] Workflow marked as completed via __end__`);
590
+ return;
591
+ }
545
592
  this.running.delete(nodeId);
546
593
  this.ready.delete(nodeId);
547
594
  this.pending.delete(nodeId);
@@ -569,6 +616,21 @@ class Scheduler {
569
616
  }
570
617
  this.skipped.add(nodeId);
571
618
  }
619
+ unmarkSkipped(nodeId) {
620
+ if (!this.skipped.has(nodeId)) {
621
+ return;
622
+ }
623
+ this.skipped.delete(nodeId);
624
+ this.pending.add(nodeId);
625
+ }
626
+ resetNodeForReEntry(nodeId) {
627
+ this.running.delete(nodeId);
628
+ this.completed.delete(nodeId);
629
+ this.failed.delete(nodeId);
630
+ this.skipped.delete(nodeId);
631
+ this.ready.delete(nodeId);
632
+ this.pending.add(nodeId);
633
+ }
572
634
  reset() {
573
635
  this.running.clear();
574
636
  this.completed.clear();
@@ -593,6 +655,11 @@ class Scheduler {
593
655
  skipped: Array.from(this.skipped).sort()
594
656
  };
595
657
  }
658
+ markCompletedFromRestore(nodeIds) {
659
+ for (const nodeId of nodeIds) {
660
+ this.markCompleted(nodeId);
661
+ }
662
+ }
596
663
  updateReadyNodes(completedNodes) {
597
664
  const allCompleted = new Set(completedNodes);
598
665
  for (const nodeId of this.completed) {
@@ -616,8 +683,70 @@ class Scheduler {
616
683
  setParallelLimit(limit) {
617
684
  this.options.parallelLimit = limit;
618
685
  }
686
+ getOutgoingEdges(nodeId, edges) {
687
+ return edges.filter((edge) => edge.from === nodeId);
688
+ }
689
+ processEdgesAfterCompletion(completedNodeId, edges, templateResolver, loopEnabled) {
690
+ const result = {
691
+ toStart: [],
692
+ reEntries: [],
693
+ skipped: [],
694
+ resetNodes: []
695
+ };
696
+ const completedNode = this.dagManager.getNode(completedNodeId);
697
+ if (completedNode?.condition) {
698
+ const conditionMet = templateResolver.evaluateCondition(completedNode.condition);
699
+ if (!conditionMet) {
700
+ for (const edge of this.getOutgoingEdges(completedNodeId, edges)) {
701
+ this.markSkipped(edge.to);
702
+ result.skipped.push({
703
+ nodeId: edge.to,
704
+ reason: `Inline condition false on node "${completedNodeId}"`
705
+ });
706
+ }
707
+ return result;
708
+ }
709
+ }
710
+ const outgoingEdges = this.getOutgoingEdges(completedNodeId, edges);
711
+ for (const edge of outgoingEdges) {
712
+ const conditionMet = !edge.when || templateResolver.evaluateCondition(edge.when);
713
+ if (conditionMet) {
714
+ if (this.skipped.has(edge.to)) {
715
+ this.unmarkSkipped(edge.to);
716
+ }
717
+ const isReEntry = loopEnabled && this.completed.has(edge.to);
718
+ const canStart = loopEnabled ? !this.running.has(edge.to) && !this.failed.has(edge.to) : !this.completed.has(edge.to) && !this.running.has(edge.to) && !this.failed.has(edge.to);
719
+ if (canStart) {
720
+ if (isReEntry) {
721
+ const downstream = this.dagManager.getDownstreamUntil(edge.to, completedNodeId);
722
+ for (const nodeId of downstream) {
723
+ this.resetNodeForReEntry(nodeId);
724
+ result.resetNodes.push(nodeId);
725
+ }
726
+ this.resetNodeForReEntry(edge.to);
727
+ result.resetNodes.push(edge.to);
728
+ result.reEntries.push(edge.to);
729
+ }
730
+ result.toStart.push(edge.to);
731
+ }
732
+ } else {
733
+ if (!this.skipped.has(edge.to)) {
734
+ result.skipped.push({
735
+ nodeId: edge.to,
736
+ reason: edge.when ? `Edge condition false: ${edge.when}` : "Edge condition false"
737
+ });
738
+ }
739
+ this.markSkipped(edge.to);
740
+ }
741
+ }
742
+ return result;
743
+ }
619
744
  }
620
- var init_scheduler = () => {};
745
+ var logger;
746
+ var init_scheduler = __esm(() => {
747
+ init_logger();
748
+ logger = createLogger("workflow.scheduler");
749
+ });
621
750
 
622
751
  // src/env/workflow/engine/executor.ts
623
752
  var Executor;
@@ -972,11 +1101,16 @@ var init_agent_node = __esm(() => {
972
1101
  async execute(context) {
973
1102
  const startTime = Date.now();
974
1103
  try {
975
- const config = this.definition.config || {};
1104
+ const config = context.resolvedConfig ?? this.definition.config ?? {};
976
1105
  const agentType = config.agent_type || "general";
977
- const promptTemplate = config.prompt || "";
978
1106
  const options = config.options || {};
979
- const resolvedPrompt = this.resolveTemplate(promptTemplate, context);
1107
+ let resolvedPrompt;
1108
+ if (context.resolvedConfig && context.templateResolver) {
1109
+ resolvedPrompt = config.prompt || "";
1110
+ } else {
1111
+ const promptTemplate = config.prompt || "";
1112
+ resolvedPrompt = context.templateResolver?.resolveValue(promptTemplate) ?? promptTemplate;
1113
+ }
980
1114
  if (this.agentRunner.registerAgent && this.agentRunner.hasAgent) {
981
1115
  if (!this.agentRunner.hasAgent(agentType)) {
982
1116
  this.agentRunner.registerAgent(agentType, {
@@ -1082,142 +1216,9 @@ var init_agent_node = __esm(() => {
1082
1216
  }
1083
1217
  return agentSessionId;
1084
1218
  }
1085
- resolveTemplate(template, context) {
1086
- if (!template) {
1087
- return "";
1088
- }
1089
- let resolved = template;
1090
- resolved = resolved.replace(/\{\{input\.([^}]+)\}\}/g, (match, path) => {
1091
- let value = this.getNestedValue(context.input, path);
1092
- if (value === undefined && context.workflowInput) {
1093
- value = this.getNestedValue(context.workflowInput, path);
1094
- }
1095
- return this.stringifyValue(value, match);
1096
- });
1097
- resolved = resolved.replace(/\{\{inputs\.([^}]+)\}\}/g, (match, path) => {
1098
- let value = this.getNestedValue(context.input, path);
1099
- if (value === undefined && context.workflowInput) {
1100
- value = this.getNestedValue(context.workflowInput, path);
1101
- }
1102
- return this.stringifyValue(value, match);
1103
- });
1104
- resolved = resolved.replace(/\{\{nodes\.([^}]+)\}\}/g, (match, path) => {
1105
- const segments = path.split(".");
1106
- const nodeId = segments[0];
1107
- let nodeOutput = context.previousOutputs.get(nodeId);
1108
- if (nodeOutput === undefined) {
1109
- const normalizedUnderscore = nodeId.replace(/-/g, "_");
1110
- const normalizedHyphen = nodeId.replace(/_/g, "-");
1111
- if (normalizedUnderscore !== nodeId) {
1112
- nodeOutput = context.previousOutputs.get(normalizedUnderscore);
1113
- }
1114
- if (nodeOutput === undefined && normalizedHyphen !== nodeId) {
1115
- nodeOutput = context.previousOutputs.get(normalizedHyphen);
1116
- }
1117
- }
1118
- if (nodeOutput === undefined) {
1119
- return match;
1120
- }
1121
- nodeOutput = this.extractFromWrapper(nodeOutput);
1122
- if (segments.length === 1) {
1123
- return this.stringifyValue(nodeOutput, match);
1124
- }
1125
- let value = nodeOutput;
1126
- let startIndex = 1;
1127
- if (segments.length > 1 && (segments[1] === "output" || segments[1] === "result" || segments[1] === "metadata")) {
1128
- startIndex = 2;
1129
- }
1130
- for (let i = startIndex;i < segments.length; i++) {
1131
- if (value === null || value === undefined) {
1132
- return match;
1133
- }
1134
- value = value[segments[i]];
1135
- }
1136
- return this.stringifyValue(value, match);
1137
- });
1138
- resolved = resolved.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
1139
- if (path.startsWith("input.") || path.startsWith("nodes.")) {
1140
- return match;
1141
- }
1142
- const segments = path.split(".");
1143
- const nodeId = segments[0];
1144
- let nodeOutput = context.previousOutputs.get(nodeId);
1145
- if (nodeOutput === undefined) {
1146
- const normalizedUnderscore = nodeId.replace(/-/g, "_");
1147
- const normalizedHyphen = nodeId.replace(/_/g, "-");
1148
- if (normalizedUnderscore !== nodeId) {
1149
- nodeOutput = context.previousOutputs.get(normalizedUnderscore);
1150
- }
1151
- if (nodeOutput === undefined && normalizedHyphen !== nodeId) {
1152
- nodeOutput = context.previousOutputs.get(normalizedHyphen);
1153
- }
1154
- }
1155
- if (nodeOutput === undefined) {
1156
- return match;
1157
- }
1158
- nodeOutput = this.extractFromWrapper(nodeOutput);
1159
- if (segments.length === 1) {
1160
- return this.stringifyValue(nodeOutput, match);
1161
- }
1162
- let value = nodeOutput;
1163
- let startIndex = 1;
1164
- if (segments.length > 1 && (segments[1] === "output" || segments[1] === "result" || segments[1] === "metadata")) {
1165
- startIndex = 2;
1166
- }
1167
- for (let i = startIndex;i < segments.length; i++) {
1168
- if (value === null || value === undefined) {
1169
- return match;
1170
- }
1171
- value = value[segments[i]];
1172
- }
1173
- return this.stringifyValue(value, match);
1174
- });
1175
- return resolved;
1176
- }
1177
- stringifyValue(value, originalTemplate) {
1178
- if (value === undefined) {
1179
- return originalTemplate;
1180
- }
1181
- if (value === null) {
1182
- return "null";
1183
- }
1184
- if (typeof value === "string") {
1185
- return value;
1186
- }
1187
- return JSON.stringify(value);
1188
- }
1189
- extractFromWrapper(value) {
1190
- if (value === null || value === undefined) {
1191
- return value;
1192
- }
1193
- if (typeof value !== "object") {
1194
- return value;
1195
- }
1196
- if ("result" in value && "metadata" in value) {
1197
- return value.result;
1198
- }
1199
- if ("output" in value) {
1200
- return value.output;
1201
- }
1202
- return value;
1203
- }
1204
- getNestedValue(obj, path) {
1205
- if (!obj) {
1206
- return;
1207
- }
1208
- if ("input" in obj && path in obj["input"]) {
1209
- return obj["input"][path];
1210
- }
1211
- return path.split(".").reduce((current, key) => {
1212
- if (current && typeof current === "object") {
1213
- return current[key];
1214
- }
1215
- return;
1216
- }, obj);
1217
- }
1218
1219
  };
1219
1220
  __legacyDecorateClassTS([
1220
- TracedAs("agent.node.execute", { recordParams: true, recordResult: true, log: true })
1221
+ TracedAs("workflow.agent.execute", { log: true })
1221
1222
  ], AgentNode.prototype, "execute", null);
1222
1223
  __legacyDecorateClassTS([
1223
1224
  TracedAs("agent.node.createAgentSubSession", { recordParams: true, recordResult: true, log: true })
@@ -1226,16 +1227,17 @@ var init_agent_node = __esm(() => {
1226
1227
 
1227
1228
  // src/env/workflow/nodes/ask-user-node.ts
1228
1229
  class AskUserNode {
1230
+ definition;
1229
1231
  type = "ask_user";
1230
1232
  id;
1231
- config;
1232
1233
  constructor(definition) {
1234
+ this.definition = definition;
1233
1235
  this.id = definition.id;
1234
- this.config = definition.config || {};
1235
1236
  }
1236
1237
  async execute(context) {
1237
- const query = this.config.query || "确认继续吗?";
1238
- const options = this.config.options;
1238
+ const config = context.resolvedConfig ?? this.definition.config ?? {};
1239
+ const query = config.query || "确认继续吗?";
1240
+ const options = config.options;
1239
1241
  const fullQuery = options ? `${query} (选项: ${options.join(", ")})` : query;
1240
1242
  await context.eventBus.publish(createNodeInterruptEvent(context.runId, this.id, this.type, fullQuery));
1241
1243
  throw new AskUserError(context.runId, context.sessionId, this.id, this.type, query);
@@ -1348,7 +1350,7 @@ var init_node_registry = __esm(() => {
1348
1350
 
1349
1351
  // src/env/workflow/engine/engine.ts
1350
1352
  import { EventEmitter } from "events";
1351
- var logger, WorkflowEngine;
1353
+ var logger2, WorkflowEngine;
1352
1354
  var init_engine = __esm(() => {
1353
1355
  init_event();
1354
1356
  init_workflow_hil();
@@ -1359,7 +1361,9 @@ var init_engine = __esm(() => {
1359
1361
  init_node_registry();
1360
1362
  init_logger();
1361
1363
  init_decorator();
1362
- logger = createLogger("workflow:engine");
1364
+ init_template_resolver();
1365
+ init_workflow_error();
1366
+ logger2 = createLogger("workflow:engine");
1363
1367
  WorkflowEngine = class WorkflowEngine extends EventEmitter {
1364
1368
  activeSessions = new Map;
1365
1369
  sessionIdCounter = 0;
@@ -1416,7 +1420,7 @@ var init_engine = __esm(() => {
1416
1420
  metadata
1417
1421
  });
1418
1422
  }
1419
- logger.info(`[WorkflowEngine] Created session: ${sessionId}`);
1423
+ logger2.info(`[WorkflowEngine] Created session: ${sessionId}`);
1420
1424
  return sessionId;
1421
1425
  }
1422
1426
  async run(sessionId, options) {
@@ -1486,24 +1490,26 @@ var init_engine = __esm(() => {
1486
1490
  }
1487
1491
  });
1488
1492
  metadata.input = { ...existingInput, ...newInput };
1489
- logger.info(`[WorkflowEngine] Updated session ${sessionId} input with resume user input`);
1493
+ logger2.info(`[WorkflowEngine] Updated session ${sessionId} input with resume user input`);
1490
1494
  }
1491
1495
  }
1492
1496
  const messages = this.sessionComponent ? await this.sessionComponent.getMessages(sessionId) : [];
1493
- const { inferNextNode } = await import("./roy-agent-core-ffb9fq4v.js");
1497
+ const { inferNextNode, extractResumeInfo } = await import("./roy-agent-core-23gw9c4s.js");
1494
1498
  const entry = workflowDef.entry;
1495
1499
  const entryNode = Array.isArray(entry) ? entry[0] : entry;
1500
+ const resumeInfo = extractResumeInfo(messages);
1496
1501
  const resumePoint = inferNextNode(messages, {
1497
1502
  entryNode: entryNode !== "__default_entry__" ? entryNode : undefined,
1498
1503
  edges: undefined
1499
1504
  });
1500
1505
  const sessionState = await this.initializeSessionState(sessionId, workflowDef, workflowId, workflowName, options);
1501
1506
  this.activeSessions.set(sessionId, sessionState);
1502
- return this.runWithResume(sessionId, resumePoint, options);
1507
+ return this.runWithResume(sessionId, resumePoint, resumeInfo, options);
1503
1508
  }
1504
1509
  async initializeSessionState(sessionId, definition, workflowId, workflowName, options) {
1505
- const dagManager = new DAGManager(definition);
1506
- const validation = dagManager.validate();
1510
+ const loopEnabled = options?.loopEnabled ?? definition.config?.loopEnabled ?? false;
1511
+ const dagManager = new DAGManager(definition, { allowCycles: loopEnabled });
1512
+ const validation = dagManager.validate({ skipCycleCheck: loopEnabled });
1507
1513
  if (!validation.valid) {
1508
1514
  throw new Error(`Invalid workflow: ${validation.errors.join(", ")}`);
1509
1515
  }
@@ -1516,10 +1522,15 @@ var init_engine = __esm(() => {
1516
1522
  debug: options?.debug ?? definition.config?.debug ?? false
1517
1523
  };
1518
1524
  const executor = new Executor(this.nodeRegistry, eventBus, executorOptions, this.sessionComponent);
1525
+ const workflowInput = options?.workflowInput ?? options?.input;
1519
1526
  const config = {
1520
1527
  parallelLimit,
1521
1528
  timeout: options?.timeout ?? definition.config?.timeout ?? null,
1522
- debug: options?.debug ?? definition.config?.debug ?? false
1529
+ debug: options?.debug ?? definition.config?.debug ?? false,
1530
+ strict: options?.strict ?? definition.config?.strict ?? false,
1531
+ maxIterations: options?.maxIterations ?? definition.config?.maxIterations ?? 100,
1532
+ loopEnabled,
1533
+ workflowInput
1523
1534
  };
1524
1535
  const abortController = new AbortController;
1525
1536
  let resolveCompleted;
@@ -1546,12 +1557,14 @@ var init_engine = __esm(() => {
1546
1557
  resolveCompleted,
1547
1558
  rejectCompleted,
1548
1559
  workflowHistory: [],
1549
- agentSessions: new Map
1560
+ agentSessions: new Map,
1561
+ edges: dagManager.buildEdges(definition.edges || []),
1562
+ loopIterationCount: 0
1550
1563
  };
1551
1564
  this.setupEventHandlers(sessionState);
1552
1565
  return sessionState;
1553
1566
  }
1554
- async runWithResume(sessionId, resumePoint, options) {
1567
+ async runWithResume(sessionId, resumePoint, resumeInfo, options) {
1555
1568
  const sessionState = this.activeSessions.get(sessionId);
1556
1569
  if (!sessionState) {
1557
1570
  throw new Error(`Session not found: ${sessionId}`);
@@ -1580,12 +1593,17 @@ var init_engine = __esm(() => {
1580
1593
  } else if (options?.input && typeof options.input === "object") {
1581
1594
  inputForExecution = options.input;
1582
1595
  }
1596
+ if (resumeInfo?.nodeOutputs) {
1597
+ const completedNodeIds = Array.from(resumeInfo.nodeOutputs.keys());
1598
+ sessionState.scheduler.markCompletedFromRestore(completedNodeIds);
1599
+ }
1583
1600
  this.scheduleAndExecute(sessionState, {
1584
1601
  input: inputForExecution,
1585
1602
  pendingNodeId,
1586
- agentSessionId
1603
+ agentSessionId,
1604
+ restoredOutputs: resumeInfo?.nodeOutputs
1587
1605
  }).catch((error) => {
1588
- logger.error(`Workflow ${sessionId} scheduling error:`, error);
1606
+ logger2.error(`Workflow ${sessionId} scheduling error:`, error);
1589
1607
  this.failWorkflow(sessionState, error);
1590
1608
  });
1591
1609
  if (options?.sync !== false) {
@@ -1600,7 +1618,7 @@ var init_engine = __esm(() => {
1600
1618
  const sessionId = await this.createSession(workflow, options);
1601
1619
  const sessionState = await this.initializeSessionState(sessionId, definition, workflowId, workflowName, options);
1602
1620
  this.activeSessions.set(sessionId, sessionState);
1603
- return this.runWithResume(sessionId, { type: "entry_node" }, options);
1621
+ return this.runWithResume(sessionId, { type: "entry_node" }, undefined, options);
1604
1622
  }
1605
1623
  async pause(sessionId) {
1606
1624
  const sessionState = this.activeSessions.get(sessionId);
@@ -1620,7 +1638,7 @@ var init_engine = __esm(() => {
1620
1638
  });
1621
1639
  }
1622
1640
  await sessionState.eventBus.publish(createWorkflowEvent("workflow.paused", this.getRunIdFromSessionId(sessionId), {}));
1623
- logger.info(`[WorkflowEngine] Workflow paused: ${sessionId}`);
1641
+ logger2.info(`[WorkflowEngine] Workflow paused: ${sessionId}`);
1624
1642
  }
1625
1643
  async stop(sessionId, reason) {
1626
1644
  const sessionState = this.activeSessions.get(sessionId);
@@ -1659,15 +1677,12 @@ var init_engine = __esm(() => {
1659
1677
  eventBus.on("node.completed", async (event) => {
1660
1678
  if (event.type !== "node.completed")
1661
1679
  return;
1662
- sessionState.nodeOutputs.set(event.node_id, event.output);
1663
- scheduler.markCompleted(event.node_id);
1664
1680
  if (event.output && typeof event.output === "object" && "workflowHistory" in event.output) {
1665
1681
  const messages = event.output.workflowHistory;
1666
1682
  if (Array.isArray(messages)) {
1667
1683
  sessionState.workflowHistory.push(...messages);
1668
1684
  }
1669
1685
  }
1670
- this.checkAndFinalize(sessionState);
1671
1686
  });
1672
1687
  eventBus.on("node.failed", async (event) => {
1673
1688
  if (event.type !== "node.failed")
@@ -1678,7 +1693,7 @@ var init_engine = __esm(() => {
1678
1693
  eventBus.on("node.interrupt", async (event) => {
1679
1694
  if (event.type !== "node.interrupt")
1680
1695
  return;
1681
- logger.info(`Workflow paused at node "${event.node_id}" - ask_user pending`);
1696
+ logger2.info(`Workflow paused at node "${event.node_id}" - ask_user pending`);
1682
1697
  sessionState.status = "paused";
1683
1698
  sessionState.abortController.abort();
1684
1699
  if (this.sessionComponent) {
@@ -1720,6 +1735,14 @@ var init_engine = __esm(() => {
1720
1735
  async scheduleAndExecute(sessionState, options) {
1721
1736
  const { scheduler, executor, eventBus, abortController, nodeOutputs } = sessionState;
1722
1737
  const { input: globalInput, pendingNodeId, agentSessionId, restoredOutputs } = options || {};
1738
+ if (globalInput) {
1739
+ sessionState.config.workflowInput = {
1740
+ ...sessionState.config.workflowInput || {},
1741
+ ...globalInput
1742
+ };
1743
+ }
1744
+ sessionState.loopIterationCount = 0;
1745
+ const maxIterations = sessionState.config.maxIterations;
1723
1746
  if (restoredOutputs) {
1724
1747
  for (const [nodeId, output] of restoredOutputs) {
1725
1748
  nodeOutputs.set(nodeId, output);
@@ -1739,7 +1762,7 @@ var init_engine = __esm(() => {
1739
1762
  break;
1740
1763
  if (scheduled >= (sessionState.config.parallelLimit ?? Infinity))
1741
1764
  break;
1742
- await this.startNode(sessionState, nodeId, globalInput);
1765
+ await this.startNode(sessionState, nodeId, sessionState.config.workflowInput);
1743
1766
  scheduled++;
1744
1767
  }
1745
1768
  return true;
@@ -1757,6 +1780,12 @@ var init_engine = __esm(() => {
1757
1780
  if (state.pending.length === 0 && state.running.length === 0) {
1758
1781
  break;
1759
1782
  }
1783
+ if (sessionState.config.loopEnabled && sessionState.loopIterationCount >= maxIterations) {
1784
+ logger2.error(`[WorkflowEngine] Max loop iterations ${maxIterations} exceeded`);
1785
+ sessionState.rejectCompleted(new WorkflowError(`Workflow exceeded maximum loop iterations: ${maxIterations}`));
1786
+ await this.stop(sessionState.sessionId, "Max iterations exceeded");
1787
+ return;
1788
+ }
1760
1789
  try {
1761
1790
  await this.waitForNextNodeEvent(sessionState, abortController.signal);
1762
1791
  } catch {
@@ -1809,15 +1838,8 @@ var init_engine = __esm(() => {
1809
1838
  scheduler.markFailed(nodeId);
1810
1839
  return;
1811
1840
  }
1812
- await this.writeNodeStart(sessionId, nodeId, nodeDef.type, input);
1813
- const context = this.createExecutionContext(sessionState, nodeId, input);
1814
- executor.executeNode(nodeDef, context).catch(async (error) => {
1815
- if (error instanceof AskUserError) {
1816
- await eventBus.publish(createNodeInterruptEvent(runId, nodeId, nodeDef.type, error.query, error.agentSessionId));
1817
- return;
1818
- }
1819
- scheduler.markFailed(nodeId);
1820
- });
1841
+ const context = this.buildNodeContext(sessionState, nodeId, input);
1842
+ await this.validateAndExecute(nodeDef, context, sessionState);
1821
1843
  }
1822
1844
  async writeNodeStart(sessionId, nodeId, nodeType, input) {
1823
1845
  if (this.sessionComponent) {
@@ -1862,7 +1884,10 @@ var init_engine = __esm(() => {
1862
1884
  scheduler.markFailed(nodeId);
1863
1885
  return;
1864
1886
  }
1865
- const context = this.createExecutionContext(sessionState, nodeId, { userQuery: options?.userQuery }, true);
1887
+ const context = this.buildNodeContext(sessionState, nodeId, {
1888
+ ...sessionState.config.workflowInput || {},
1889
+ ...options?.userQuery ? { userQuery: options.userQuery } : {}
1890
+ });
1866
1891
  if (options?.agentSessionId) {
1867
1892
  context.agentSessionId = options.agentSessionId;
1868
1893
  }
@@ -1883,16 +1908,103 @@ var init_engine = __esm(() => {
1883
1908
  }
1884
1909
  }
1885
1910
  }
1886
- executor.executeNode(nodeDef, context).catch((error) => {
1911
+ await this.validateAndExecute(nodeDef, context, sessionState);
1912
+ }
1913
+ async preExecuteValidation(nodeId, nodeDef, context, options) {
1914
+ if (!nodeDef) {
1915
+ logger2.warn(`Node ${nodeId} has no configuration`);
1916
+ return;
1917
+ }
1918
+ const config = nodeDef.config || {};
1919
+ if (context.debug) {
1920
+ logger2.debug(`Pre-execute validation for node ${nodeId}`, {
1921
+ workflowInput: context.workflowInput,
1922
+ previousOutputs: Array.from(context.previousOutputs.keys()),
1923
+ hasTemplateResolver: !!context.templateResolver
1924
+ });
1925
+ }
1926
+ if (context.templateResolver) {
1927
+ const { unresolved } = context.templateResolver.resolveAndValidate(config);
1928
+ if (unresolved.length > 0) {
1929
+ if (options?.strict) {
1930
+ context.templateResolver.resolveStrict(config, nodeId);
1931
+ }
1932
+ logger2.warn(`[WorkflowEngine] Node "${nodeId}" has unresolved templates:`, unresolved);
1933
+ }
1934
+ }
1935
+ }
1936
+ async validateAndExecute(nodeDef, context, sessionState) {
1937
+ const { scheduler, executor, eventBus } = sessionState;
1938
+ const { nodeId } = context;
1939
+ const strictMode = sessionState.config?.strict ?? false;
1940
+ try {
1941
+ await this.preExecuteValidation(nodeId, nodeDef, context, { strict: strictMode });
1942
+ } catch (err) {
1943
+ logger2.error(`[WorkflowEngine] Strict validation failed for node ${nodeId}:`, err);
1944
+ scheduler.markFailed(nodeId);
1945
+ return;
1946
+ }
1947
+ try {
1948
+ const result = await executor.executeNode(nodeDef, context);
1949
+ if (result.error) {
1950
+ return;
1951
+ }
1952
+ sessionState.nodeOutputs.set(nodeId, result.output);
1953
+ scheduler.markCompleted(nodeId);
1954
+ if (result.output && typeof result.output === "object" && "workflowHistory" in result.output) {
1955
+ const messages = result.output.workflowHistory;
1956
+ if (Array.isArray(messages)) {
1957
+ sessionState.workflowHistory.push(...messages);
1958
+ }
1959
+ }
1960
+ await this.processConditionalEdgesAfterCompletion(sessionState, nodeId);
1961
+ this.checkAndFinalize(sessionState);
1962
+ } catch (error) {
1887
1963
  if (error instanceof AskUserError) {
1888
- const event = createNodeInterruptEvent(runId, nodeId, nodeDef.type, error.query, error.agentSessionId);
1889
- eventBus.publish(event);
1964
+ await eventBus.publish(createNodeInterruptEvent(context.runId, nodeId, nodeDef.type, error.query, error.agentSessionId));
1890
1965
  return;
1891
1966
  }
1892
1967
  scheduler.markFailed(nodeId);
1968
+ }
1969
+ }
1970
+ async processConditionalEdgesAfterCompletion(sessionState, completedNodeId) {
1971
+ if (!sessionState.edges || sessionState.edges.length === 0) {
1972
+ return;
1973
+ }
1974
+ const { scheduler, eventBus } = sessionState;
1975
+ const runId = this.getRunIdFromSessionId(sessionState.sessionId);
1976
+ const templateResolver = new TemplateResolver({
1977
+ input: sessionState.config.workflowInput || {},
1978
+ previousOutputs: new Map(sessionState.nodeOutputs)
1893
1979
  });
1980
+ logger2.debug(`[WorkflowEngine] Processing edges after "${completedNodeId}" completed. edges=${JSON.stringify(sessionState.edges)}`);
1981
+ const edgeResult = scheduler.processEdgesAfterCompletion(completedNodeId, sessionState.edges, templateResolver, sessionState.config.loopEnabled);
1982
+ logger2.debug(`[WorkflowEngine] Edge processing result: ${JSON.stringify(edgeResult)}`);
1983
+ for (const nodeId of edgeResult.resetNodes) {
1984
+ sessionState.nodeOutputs.delete(nodeId);
1985
+ }
1986
+ for (const { nodeId, reason } of edgeResult.skipped) {
1987
+ await eventBus.publish(createWorkflowEvent("node.skipped", runId, {
1988
+ node_id: nodeId,
1989
+ reason
1990
+ }));
1991
+ }
1992
+ if (edgeResult.reEntries.length > 0) {
1993
+ sessionState.loopIterationCount += edgeResult.reEntries.length;
1994
+ logger2.debug(`[WorkflowEngine] Loop iteration #${sessionState.loopIterationCount}: re-entering ${edgeResult.reEntries.join(", ")}`);
1995
+ }
1996
+ const workflowInput = sessionState.config.workflowInput || {};
1997
+ for (const targetId of edgeResult.toStart) {
1998
+ if (targetId === "__end__" || targetId === "__END__") {
1999
+ logger2.info(`[WorkflowEngine] Workflow terminated via __end__ edge from node "${completedNodeId}"`);
2000
+ scheduler.markCompleted("__end__");
2001
+ await this.completeWorkflow(sessionState);
2002
+ } else {
2003
+ await this.startNode(sessionState, targetId, workflowInput);
2004
+ }
2005
+ }
1894
2006
  }
1895
- createExecutionContext(sessionState, nodeId, globalInput, forceGlobalInput) {
2007
+ buildNodeContext(sessionState, nodeId, globalInput) {
1896
2008
  const nodeDef = sessionState.dagManager.getNode(nodeId);
1897
2009
  const runId = this.getRunIdFromSessionId(sessionState.sessionId);
1898
2010
  const deps = nodeDef.depends_on || [];
@@ -1903,15 +2015,15 @@ var init_engine = __esm(() => {
1903
2015
  input[depId] = depOutput;
1904
2016
  }
1905
2017
  }
1906
- const analysis = sessionState.dagManager.analyze();
1907
- const isEntryNode = analysis.entryNodes.includes(nodeId);
1908
- if ((isEntryNode || forceGlobalInput) && globalInput) {
1909
- Object.assign(input, globalInput);
1910
- }
2018
+ const workflowInput = globalInput ?? sessionState.config.workflowInput ?? {};
2019
+ const templateResolver = new TemplateResolver({
2020
+ input: workflowInput,
2021
+ previousOutputs: new Map(sessionState.nodeOutputs)
2022
+ });
1911
2023
  const askUser = (query) => {
1912
2024
  throw new AskUserError(runId, sessionState.sessionId, nodeId, nodeDef.type, query);
1913
2025
  };
1914
- return {
2026
+ const context = {
1915
2027
  runId,
1916
2028
  sessionId: sessionState.sessionId,
1917
2029
  workflowName: sessionState.workflowName,
@@ -1924,8 +2036,12 @@ var init_engine = __esm(() => {
1924
2036
  nodeOutputs: sessionState.nodeOutputs,
1925
2037
  workflowHistory: sessionState.workflowHistory,
1926
2038
  sessionComponent: this.sessionComponent,
1927
- askUser
2039
+ askUser,
2040
+ workflowInput,
2041
+ templateResolver,
2042
+ resolvedConfig: templateResolver.resolveValue(nodeDef.config ?? {})
1928
2043
  };
2044
+ return context;
1929
2045
  }
1930
2046
  checkAndFinalize(sessionState) {
1931
2047
  const state = sessionState.scheduler.getState();
@@ -1938,6 +2054,10 @@ var init_engine = __esm(() => {
1938
2054
  }
1939
2055
  }
1940
2056
  async completeWorkflow(sessionState) {
2057
+ if (sessionState.status !== "running") {
2058
+ return;
2059
+ }
2060
+ sessionState.status = "completed";
1941
2061
  const durationMs = Date.now() - sessionState.startedAt.getTime();
1942
2062
  const sessionId = sessionState.sessionId;
1943
2063
  const runId = this.getRunIdFromSessionId(sessionId);
@@ -1964,6 +2084,10 @@ var init_engine = __esm(() => {
1964
2084
  this.cleanupSession(sessionState);
1965
2085
  }
1966
2086
  async failWorkflow(sessionState, error) {
2087
+ if (sessionState.status !== "running") {
2088
+ return;
2089
+ }
2090
+ sessionState.status = "failed";
1967
2091
  const durationMs = Date.now() - sessionState.startedAt.getTime();
1968
2092
  const sessionId = sessionState.sessionId;
1969
2093
  const runId = this.getRunIdFromSessionId(sessionId);
@@ -2065,6 +2189,12 @@ var init_engine = __esm(() => {
2065
2189
  __legacyDecorateClassTS([
2066
2190
  TracedAs("workflow.engine.resumeNode", { recordParams: true, recordResult: true, log: true })
2067
2191
  ], WorkflowEngine.prototype, "resumeNode", null);
2192
+ __legacyDecorateClassTS([
2193
+ TracedAs("workflow.engine.preExecuteValidation", { log: true })
2194
+ ], WorkflowEngine.prototype, "preExecuteValidation", null);
2195
+ __legacyDecorateClassTS([
2196
+ TracedAs("workflow.engine.buildNodeContext", { log: true })
2197
+ ], WorkflowEngine.prototype, "buildNodeContext", null);
2068
2198
  __legacyDecorateClassTS([
2069
2199
  TracedAs("workflow.engine.checkAndFinalize", { recordParams: true, recordResult: true, log: true })
2070
2200
  ], WorkflowEngine.prototype, "checkAndFinalize", null);