@exellix/graph-engine 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/.env.example +3 -0
  2. package/CHANGELOG.md +208 -0
  3. package/README.md +827 -0
  4. package/dist/src/errors/ExellixGraphError.d.ts +38 -0
  5. package/dist/src/errors/ExellixGraphError.js +21 -0
  6. package/dist/src/errors/exellixGraphErrorCodes.d.ts +31 -0
  7. package/dist/src/errors/exellixGraphErrorCodes.js +32 -0
  8. package/dist/src/index.d.ts +100 -0
  9. package/dist/src/index.js +75 -0
  10. package/dist/src/inspection/contractInspection.d.ts +21 -0
  11. package/dist/src/inspection/contractInspection.js +526 -0
  12. package/dist/src/inspection/contractTypes.d.ts +137 -0
  13. package/dist/src/inspection/contractTypes.js +1 -0
  14. package/dist/src/inspection/controlInspection.d.ts +22 -0
  15. package/dist/src/inspection/controlInspection.js +130 -0
  16. package/dist/src/inspection/graphInspection.d.ts +51 -0
  17. package/dist/src/inspection/graphInspection.js +467 -0
  18. package/dist/src/inspection/index.d.ts +21 -0
  19. package/dist/src/inspection/index.js +17 -0
  20. package/dist/src/inspection/nodeInspection.d.ts +42 -0
  21. package/dist/src/inspection/nodeInspection.js +474 -0
  22. package/dist/src/inspection/types.d.ts +321 -0
  23. package/dist/src/inspection/types.js +14 -0
  24. package/dist/src/inspection/validateAiTasksNodeExtensions.d.ts +12 -0
  25. package/dist/src/inspection/validateAiTasksNodeExtensions.js +119 -0
  26. package/dist/src/inspection/validateCatalogPlanning.d.ts +21 -0
  27. package/dist/src/inspection/validateCatalogPlanning.js +187 -0
  28. package/dist/src/integrations/ActivityTrackerIntegration.d.ts +86 -0
  29. package/dist/src/integrations/ActivityTrackerIntegration.js +134 -0
  30. package/dist/src/integrations/ActivixGraphRunIntegration.d.ts +34 -0
  31. package/dist/src/integrations/ActivixGraphRunIntegration.js +338 -0
  32. package/dist/src/integrations/ActivixNodeActivityIntegration.d.ts +33 -0
  33. package/dist/src/integrations/ActivixNodeActivityIntegration.js +220 -0
  34. package/dist/src/integrations/cataloxGraphCatalog.d.ts +21 -0
  35. package/dist/src/integrations/cataloxGraphCatalog.js +30 -0
  36. package/dist/src/integrations/createActivixExellixIntegration.d.ts +14 -0
  37. package/dist/src/integrations/createActivixExellixIntegration.js +16 -0
  38. package/dist/src/integrations/createActivixFromEnv.d.ts +31 -0
  39. package/dist/src/integrations/createActivixFromEnv.js +53 -0
  40. package/dist/src/loaders/FileGraphLoader.d.ts +23 -0
  41. package/dist/src/loaders/FileGraphLoader.js +31 -0
  42. package/dist/src/playground/PlaygroundReporter.d.ts +40 -0
  43. package/dist/src/playground/PlaygroundReporter.js +480 -0
  44. package/dist/src/playground/index.d.ts +1 -0
  45. package/dist/src/playground/index.js +1 -0
  46. package/dist/src/runtime/ExellixGraphRuntime.d.ts +263 -0
  47. package/dist/src/runtime/ExellixGraphRuntime.js +1716 -0
  48. package/dist/src/runtime/GraphEngine.d.ts +33 -0
  49. package/dist/src/runtime/GraphEngine.js +4 -0
  50. package/dist/src/runtime/aiTasksObservability.d.ts +6 -0
  51. package/dist/src/runtime/aiTasksObservability.js +37 -0
  52. package/dist/src/runtime/aiTasksStrategyPhases.d.ts +46 -0
  53. package/dist/src/runtime/aiTasksStrategyPhases.js +93 -0
  54. package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.d.ts +17 -0
  55. package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.js +46 -0
  56. package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +67 -0
  57. package/dist/src/runtime/buildAiTasksRunTaskRequest.js +164 -0
  58. package/dist/src/runtime/buildRunLog.d.ts +27 -0
  59. package/dist/src/runtime/buildRunLog.js +234 -0
  60. package/dist/src/runtime/buildRunTaskTaskConfigurationForward.d.ts +9 -0
  61. package/dist/src/runtime/buildRunTaskTaskConfigurationForward.js +80 -0
  62. package/dist/src/runtime/buildTaskNodeJobContext.d.ts +11 -0
  63. package/dist/src/runtime/buildTaskNodeJobContext.js +30 -0
  64. package/dist/src/runtime/canonicalModelUsed.d.ts +6 -0
  65. package/dist/src/runtime/canonicalModelUsed.js +36 -0
  66. package/dist/src/runtime/contextualScope.d.ts +7 -0
  67. package/dist/src/runtime/contextualScope.js +121 -0
  68. package/dist/src/runtime/dataFiltersEvaluation.d.ts +60 -0
  69. package/dist/src/runtime/dataFiltersEvaluation.js +169 -0
  70. package/dist/src/runtime/deepMerge.d.ts +5 -0
  71. package/dist/src/runtime/deepMerge.js +22 -0
  72. package/dist/src/runtime/events.d.ts +92 -0
  73. package/dist/src/runtime/events.js +122 -0
  74. package/dist/src/runtime/executionMatrixHost.d.ts +98 -0
  75. package/dist/src/runtime/executionMatrixHost.js +134 -0
  76. package/dist/src/runtime/executionVariableBuckets.d.ts +67 -0
  77. package/dist/src/runtime/executionVariableBuckets.js +96 -0
  78. package/dist/src/runtime/finalizers/errors.d.ts +9 -0
  79. package/dist/src/runtime/finalizers/errors.js +10 -0
  80. package/dist/src/runtime/finalizers/executeFinalizer.d.ts +40 -0
  81. package/dist/src/runtime/finalizers/executeFinalizer.js +471 -0
  82. package/dist/src/runtime/finalizers/schema.d.ts +18 -0
  83. package/dist/src/runtime/finalizers/schema.js +63 -0
  84. package/dist/src/runtime/finalizers/validateFinalizer.d.ts +16 -0
  85. package/dist/src/runtime/finalizers/validateFinalizer.js +534 -0
  86. package/dist/src/runtime/graphDocumentFingerprint.d.ts +8 -0
  87. package/dist/src/runtime/graphDocumentFingerprint.js +21 -0
  88. package/dist/src/runtime/graphEngineMemoryPaths.d.ts +12 -0
  89. package/dist/src/runtime/graphEngineMemoryPaths.js +55 -0
  90. package/dist/src/runtime/graphResponseMapping.d.ts +23 -0
  91. package/dist/src/runtime/graphResponseMapping.js +156 -0
  92. package/dist/src/runtime/graphResponseMigration.d.ts +7 -0
  93. package/dist/src/runtime/graphResponseMigration.js +44 -0
  94. package/dist/src/runtime/graphRunExecutionSeed.d.ts +29 -0
  95. package/dist/src/runtime/graphRunExecutionSeed.js +61 -0
  96. package/dist/src/runtime/graphRunIdentity.d.ts +7 -0
  97. package/dist/src/runtime/graphRunIdentity.js +18 -0
  98. package/dist/src/runtime/localSkills/deterministicRule.d.ts +137 -0
  99. package/dist/src/runtime/localSkills/deterministicRule.js +196 -0
  100. package/dist/src/runtime/localSkills/index.d.ts +12 -0
  101. package/dist/src/runtime/localSkills/index.js +14 -0
  102. package/dist/src/runtime/localSkills/memorixItemToScopedOutput.d.ts +7 -0
  103. package/dist/src/runtime/localSkills/memorixItemToScopedOutput.js +104 -0
  104. package/dist/src/runtime/localSkills/memorixRuntime.d.ts +9 -0
  105. package/dist/src/runtime/localSkills/memorixRuntime.js +70 -0
  106. package/dist/src/runtime/localSkills/memorixScopedConfig.d.ts +16 -0
  107. package/dist/src/runtime/localSkills/memorixScopedConfig.js +18 -0
  108. package/dist/src/runtime/localSkills/scopedAnswerAssembler.d.ts +23 -0
  109. package/dist/src/runtime/localSkills/scopedAnswerAssembler.js +35 -0
  110. package/dist/src/runtime/localSkills/scopedAnswerFields.d.ts +12 -0
  111. package/dist/src/runtime/localSkills/scopedAnswerFields.js +66 -0
  112. package/dist/src/runtime/localSkills/scopedAnswerWriter.d.ts +32 -0
  113. package/dist/src/runtime/localSkills/scopedAnswerWriter.js +156 -0
  114. package/dist/src/runtime/localSkills/scopedDataReader.d.ts +47 -0
  115. package/dist/src/runtime/localSkills/scopedDataReader.js +89 -0
  116. package/dist/src/runtime/localSkills/utils.d.ts +12 -0
  117. package/dist/src/runtime/localSkills/utils.js +39 -0
  118. package/dist/src/runtime/materializeStructuredRunTaskInput.d.ts +9 -0
  119. package/dist/src/runtime/materializeStructuredRunTaskInput.js +34 -0
  120. package/dist/src/runtime/memory.d.ts +51 -0
  121. package/dist/src/runtime/memory.js +250 -0
  122. package/dist/src/runtime/mergeExellixGraphRuntimeInvocation.d.ts +18 -0
  123. package/dist/src/runtime/mergeExellixGraphRuntimeInvocation.js +32 -0
  124. package/dist/src/runtime/modelConfigSelection.d.ts +7 -0
  125. package/dist/src/runtime/modelConfigSelection.js +37 -0
  126. package/dist/src/runtime/narrixIngestEnv.d.ts +9 -0
  127. package/dist/src/runtime/narrixIngestEnv.js +18 -0
  128. package/dist/src/runtime/pathExpr.d.ts +36 -0
  129. package/dist/src/runtime/pathExpr.js +131 -0
  130. package/dist/src/runtime/predicates.d.ts +14 -0
  131. package/dist/src/runtime/predicates.js +86 -0
  132. package/dist/src/runtime/readTaskNodeInputsConfig.d.ts +23 -0
  133. package/dist/src/runtime/readTaskNodeInputsConfig.js +27 -0
  134. package/dist/src/runtime/resolveExecutionPipelineForTaskNode.d.ts +11 -0
  135. package/dist/src/runtime/resolveExecutionPipelineForTaskNode.js +93 -0
  136. package/dist/src/runtime/resolveGraphEngineMemoryPaths.d.ts +63 -0
  137. package/dist/src/runtime/resolveGraphEngineMemoryPaths.js +213 -0
  138. package/dist/src/runtime/resolveModelConfigForNode.d.ts +20 -0
  139. package/dist/src/runtime/resolveModelConfigForNode.js +69 -0
  140. package/dist/src/runtime/resolveNarrixForTaskNode.d.ts +14 -0
  141. package/dist/src/runtime/resolveNarrixForTaskNode.js +19 -0
  142. package/dist/src/runtime/resolveTaskKey.d.ts +11 -0
  143. package/dist/src/runtime/resolveTaskKey.js +28 -0
  144. package/dist/src/runtime/resolveTaskNodeInputs.d.ts +25 -0
  145. package/dist/src/runtime/resolveTaskNodeInputs.js +140 -0
  146. package/dist/src/runtime/runTaskAugments.d.ts +17 -0
  147. package/dist/src/runtime/runTaskAugments.js +37 -0
  148. package/dist/src/runtime/runTaskResponse.d.ts +4 -0
  149. package/dist/src/runtime/runTaskResponse.js +13 -0
  150. package/dist/src/runtime/runtimeObjects.d.ts +85 -0
  151. package/dist/src/runtime/runtimeObjects.js +50 -0
  152. package/dist/src/runtime/smartInputPaths.d.ts +13 -0
  153. package/dist/src/runtime/smartInputPaths.js +38 -0
  154. package/dist/src/runtime/stepRetry.d.ts +21 -0
  155. package/dist/src/runtime/stepRetry.js +238 -0
  156. package/dist/src/runtime/synthesizedContextPipeline.d.ts +12 -0
  157. package/dist/src/runtime/synthesizedContextPipeline.js +28 -0
  158. package/dist/src/runtime/taskNodeConditionsEvaluation.d.ts +27 -0
  159. package/dist/src/runtime/taskNodeConditionsEvaluation.js +140 -0
  160. package/dist/src/runtime/taskNodeMainReadiness.d.ts +45 -0
  161. package/dist/src/runtime/taskNodeMainReadiness.js +164 -0
  162. package/dist/src/runtime/taskNodeRunTaskPreflight.d.ts +89 -0
  163. package/dist/src/runtime/taskNodeRunTaskPreflight.js +204 -0
  164. package/dist/src/runtime/validateCanonicalGraphDocument.d.ts +25 -0
  165. package/dist/src/runtime/validateCanonicalGraphDocument.js +567 -0
  166. package/dist/src/runtime/variables.d.ts +2 -0
  167. package/dist/src/runtime/variables.js +1 -0
  168. package/dist/src/runtime/withTimeout.d.ts +5 -0
  169. package/dist/src/runtime/withTimeout.js +20 -0
  170. package/dist/src/types/aiTaskProfile.d.ts +41 -0
  171. package/dist/src/types/aiTaskProfile.js +6 -0
  172. package/dist/src/types/aiTasksDerivedTypes.d.ts +5 -0
  173. package/dist/src/types/aiTasksDerivedTypes.js +1 -0
  174. package/dist/src/types/events.d.ts +23 -0
  175. package/dist/src/types/events.js +1 -0
  176. package/dist/src/types/job.d.ts +9 -0
  177. package/dist/src/types/job.js +1 -0
  178. package/dist/src/types/narrix.d.ts +60 -0
  179. package/dist/src/types/narrix.js +1 -0
  180. package/dist/src/types/options.d.ts +122 -0
  181. package/dist/src/types/options.js +1 -0
  182. package/dist/src/types/refs.d.ts +747 -0
  183. package/dist/src/types/refs.js +12 -0
  184. package/dist/src/types/results.d.ts +103 -0
  185. package/dist/src/types/results.js +1 -0
  186. package/dist/src/types/runLog.d.ts +72 -0
  187. package/dist/src/types/runLog.js +18 -0
  188. package/dist/src/types/taskNodeConfiguration.d.ts +95 -0
  189. package/dist/src/types/taskNodeConfiguration.js +3 -0
  190. package/dist/src/util/packageVersion.d.ts +2 -0
  191. package/dist/src/util/packageVersion.js +12 -0
  192. package/dist/testkit/RealTasksClient.d.ts +16 -0
  193. package/dist/testkit/RealTasksClient.js +143 -0
  194. package/dist/testkit/depGraphEngineFactory.d.ts +6 -0
  195. package/dist/testkit/depGraphEngineFactory.js +54 -0
  196. package/dist/testkit/exellixRuntimeObjects.d.ts +7 -0
  197. package/dist/testkit/exellixRuntimeObjects.js +25 -0
  198. package/dist/testkit/inMemoryGraphLoader.d.ts +6 -0
  199. package/dist/testkit/inMemoryGraphLoader.js +12 -0
  200. package/dist/testkit/index.d.ts +4 -0
  201. package/dist/testkit/index.js +4 -0
  202. package/package.json +70 -0
@@ -0,0 +1,1716 @@
1
+ import { mergeExecutionObject, copyExecutionContextFields, } from "./memory.js";
2
+ import { buildAiTasksRunTaskRequest, extractRunTaskStrategyOverrides, resolveJobTypeId, } from "./buildAiTasksRunTaskRequest.js";
3
+ import { runTaskResponseSucceeded } from "./runTaskResponse.js";
4
+ import { getAiTaskProfileStrategyKeys, runEngineAiTasksStrategyPhase, } from "./aiTasksStrategyPhases.js";
5
+ import { mergeGraphDocumentModel, EXELLIX_GRAPH_MODEL_VARIABLE_KEY, } from "../types/refs.js";
6
+ import { normalizeSynthesizedContextConfig } from "./synthesizedContextPipeline.js";
7
+ import { resolveExecutionPipelineForTaskNode } from "./resolveExecutionPipelineForTaskNode.js";
8
+ import { resolveTaskNodeInputsForRunTask } from "./resolveTaskNodeInputs.js";
9
+ import { assertAiTasksNodeExtensionsValid } from "../inspection/validateAiTasksNodeExtensions.js";
10
+ import { buildRunTaskMainInput, extractCallerInputsBag } from "./resolveGraphEngineMemoryPaths.js";
11
+ import { mirrorRuntimeInputRawOnExecutionMemory, mirrorStructuredInputOntoExecutionMemory, } from "./materializeStructuredRunTaskInput.js";
12
+ import { buildMainReadinessResolutionRoot, evaluateTaskNodeMainReadiness, resolveMainReadinessPolicy, } from "./taskNodeMainReadiness.js";
13
+ import { mergeXynthesizedPatchIntoExecution, seedGraphRunExecutionState, xynthesizedOutboundForNode, } from "./graphRunExecutionSeed.js";
14
+ import { buildTaskNodeJobContext } from "./buildTaskNodeJobContext.js";
15
+ import { resolveNarrixForTaskNode } from "./resolveNarrixForTaskNode.js";
16
+ import { assertFinalizerRequiredReadsResolvable, validateGraphFinalizer, } from "./finalizers/validateFinalizer.js";
17
+ import { executeDeterministicFinalizer, executeSynthesizeFinalizer } from "./finalizers/executeFinalizer.js";
18
+ import { createFinalizerError } from "./finalizers/errors.js";
19
+ import { assertCanonicalGraphDocument } from "./validateCanonicalGraphDocument.js";
20
+ import { applyGraphResponseDefinition } from "./graphResponseMapping.js";
21
+ import { migrateLegacyGraphResponse } from "./graphResponseMigration.js";
22
+ import { computeGraphDocumentContentSha256 } from "./graphDocumentFingerprint.js";
23
+ import { buildAiTasksObservabilityRecord } from "./aiTasksObservability.js";
24
+ import { buildRunTaskIdentityEnvelope, mergeDefinedLlmCallParts, shouldForwardRunTaskTraceMode, } from "./runTaskAugments.js";
25
+ import { buildRunLog, extractLogxerCorrelationFromMetadata, extractTaskRunLogFromMetadata, resolveRunLogLimits, } from "./buildRunLog.js";
26
+ import { setRuntimeObjectsLastJobId, summarizeRuntimeObjectsForPlayground } from "./runtimeObjects.js";
27
+ import { assertHostJobId, newGraphRunTaskId } from "./graphRunIdentity.js";
28
+ import { resolveTaskKey } from "./resolveTaskKey.js";
29
+ import { buildPredicateEvalContextForNode, mirrorTaskVariablesOnExecution, readExecutionVariableBuckets, seedGraphVariableBucketsFromRuntime, } from "./variables.js";
30
+ import { mergeExellixGraphRuntimeInvocation } from "./mergeExellixGraphRuntimeInvocation.js";
31
+ import { resolveStepRetryPolicy, runRunTaskWithRetry } from "./stepRetry.js";
32
+ import { isLocalSkillKey, runScopedDataReader, runDeterministicRule, runScopedAnswerWriter, runScopedAnswerAssembler, } from "./localSkills/index.js";
33
+ import { evaluateGraphPredicate } from "./predicates.js";
34
+ import { evaluateStructuredDataFilters } from "./dataFiltersEvaluation.js";
35
+ import { evaluateTaskNodeConditions } from "./taskNodeConditionsEvaluation.js";
36
+ import { resolveModelConfigForNode } from "./resolveModelConfigForNode.js";
37
+ import { createRunx } from "@x12i/runx";
38
+ import { selectByPath } from "./pathExpr.js";
39
+ import { createGraphStartEvent, createGraphCompleteEvent, createGraphFailEvent, createNodeStartEvent, createNodeCompleteEvent, createNodeFailEvent, } from "./events.js";
40
+ import { ExellixGraphErrorCode } from "../errors/exellixGraphErrorCodes.js";
41
+ const NODE_LIFECYCLE_EMITTED = "__exellixNodeLifecycleEmitted";
42
+ function skippedByConditionsOutput(skipReason) {
43
+ return Object.freeze({
44
+ __exellixSkipped: true,
45
+ skipReason,
46
+ });
47
+ }
48
+ const DEFAULT_CORE_OBJECTIVE_PROPERTY_NAME = "coreObjective";
49
+ const DEFAULT_CORE_OBJECTIVE_SOURCE_PATH = "input.coreObjective";
50
+ const DEFAULT_NODE_RESPONSE_SINGULAR = "nodeResponse";
51
+ const DEFAULT_NODE_RESPONSE_PLURAL = "nodeResponses";
52
+ const SUPPORTED_CORE_OBJECTIVE_SOURCE_ROOTS = new Set([
53
+ "input",
54
+ "execution",
55
+ "variables",
56
+ "jobVariables",
57
+ "taskVariables",
58
+ "jobMemory",
59
+ "taskMemory",
60
+ "job",
61
+ ]);
62
+ function markNodeLifecycleEmitted(err) {
63
+ if (err != null && typeof err === "object") {
64
+ err[NODE_LIFECYCLE_EMITTED] = true;
65
+ }
66
+ }
67
+ function wasNodeLifecycleEmitted(err) {
68
+ return (err != null &&
69
+ typeof err === "object" &&
70
+ err[NODE_LIFECYCLE_EMITTED] === true);
71
+ }
72
+ function taskRunSucceeded(res) {
73
+ return runTaskResponseSucceeded(res);
74
+ }
75
+ function unwrapNodeResponse(output) {
76
+ if (output != null &&
77
+ typeof output === "object" &&
78
+ !Array.isArray(output) &&
79
+ output.parsed !== undefined) {
80
+ return output.parsed;
81
+ }
82
+ return output;
83
+ }
84
+ function resolveNodeResponseKeys(graphExecution) {
85
+ const singular = graphExecution?.nodesResponses?.singular;
86
+ const plural = graphExecution?.nodesResponses?.plural;
87
+ return {
88
+ singular: typeof singular === "string" && singular.length > 0 ? singular : DEFAULT_NODE_RESPONSE_SINGULAR,
89
+ plural: typeof plural === "string" && plural.length > 0 ? plural : DEFAULT_NODE_RESPONSE_PLURAL,
90
+ };
91
+ }
92
+ function resolveCoreObjectiveConfig(graphExecution) {
93
+ const propertyName = graphExecution?.coreObjective?.propertyName;
94
+ const sourcePath = graphExecution?.coreObjective?.sourcePath;
95
+ return {
96
+ propertyName: typeof propertyName === "string" && propertyName.length > 0
97
+ ? propertyName
98
+ : DEFAULT_CORE_OBJECTIVE_PROPERTY_NAME,
99
+ sourcePath: typeof sourcePath === "string" && sourcePath.length > 0
100
+ ? sourcePath
101
+ : DEFAULT_CORE_OBJECTIVE_SOURCE_PATH,
102
+ };
103
+ }
104
+ function isSupportedCoreObjectiveSourcePath(path) {
105
+ const root = path.split(".")[0];
106
+ return SUPPORTED_CORE_OBJECTIVE_SOURCE_ROOTS.has(root);
107
+ }
108
+ function resolveCoreObjectiveValue(args) {
109
+ if (!isSupportedCoreObjectiveSourcePath(args.sourcePath))
110
+ return undefined;
111
+ const executionRec = args.execution != null && typeof args.execution === "object" && !Array.isArray(args.execution)
112
+ ? args.execution
113
+ : undefined;
114
+ return selectByPath({
115
+ input: executionRec?.input,
116
+ execution: args.execution,
117
+ variables: executionRec?.jobVariables ?? args.variables,
118
+ jobVariables: executionRec?.jobVariables,
119
+ taskVariables: executionRec?.taskVariables,
120
+ jobMemory: args.jobMemory,
121
+ taskMemory: args.taskMemory,
122
+ job: args.job,
123
+ }, args.sourcePath);
124
+ }
125
+ function isPlainRecord(v) {
126
+ return v != null && typeof v === "object" && !Array.isArray(v);
127
+ }
128
+ function cloneJsonLike(value) {
129
+ if (value === undefined || value === null)
130
+ return value;
131
+ return JSON.parse(JSON.stringify(value));
132
+ }
133
+ function jsonValuesEqual(a, b) {
134
+ if (a === b)
135
+ return true;
136
+ if (Array.isArray(a) || Array.isArray(b)) {
137
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
138
+ return false;
139
+ return a.every((item, index) => jsonValuesEqual(item, b[index]));
140
+ }
141
+ if (isPlainRecord(a) || isPlainRecord(b)) {
142
+ if (!isPlainRecord(a) || !isPlainRecord(b))
143
+ return false;
144
+ const aKeys = Object.keys(a);
145
+ const bKeys = Object.keys(b);
146
+ if (aKeys.length !== bKeys.length)
147
+ return false;
148
+ return aKeys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && jsonValuesEqual(a[key], b[key]));
149
+ }
150
+ return false;
151
+ }
152
+ function arrayStartsWith(value, prefix) {
153
+ if (prefix.length > value.length)
154
+ return false;
155
+ return prefix.every((item, index) => jsonValuesEqual(value[index], item));
156
+ }
157
+ function applyExecutionDelta(target, next, base) {
158
+ for (const [key, nextValue] of Object.entries(next)) {
159
+ const baseValue = base[key];
160
+ if (jsonValuesEqual(nextValue, baseValue))
161
+ continue;
162
+ const currentValue = target[key];
163
+ if (isPlainRecord(nextValue) && isPlainRecord(currentValue)) {
164
+ applyExecutionDelta(currentValue, nextValue, isPlainRecord(baseValue) ? baseValue : {});
165
+ continue;
166
+ }
167
+ if (Array.isArray(nextValue) && Array.isArray(baseValue) && Array.isArray(currentValue) && arrayStartsWith(nextValue, baseValue)) {
168
+ target[key] = [...currentValue, ...nextValue.slice(baseValue.length)];
169
+ continue;
170
+ }
171
+ target[key] = cloneJsonLike(nextValue);
172
+ }
173
+ }
174
+ function mergeExecutionUpdate(current, next, base) {
175
+ if (!isPlainRecord(next))
176
+ return next;
177
+ if (!isPlainRecord(current))
178
+ return cloneJsonLike(next);
179
+ const merged = cloneJsonLike(current);
180
+ applyExecutionDelta(merged, next, isPlainRecord(base) ? base : {});
181
+ return merged;
182
+ }
183
+ function readRuntimeNodeConfig(nodes, nodeId) {
184
+ if (nodes == null || typeof nodeId !== "string" || nodeId.length === 0)
185
+ return undefined;
186
+ const candidate = nodes[nodeId];
187
+ return isPlainRecord(candidate) ? candidate : undefined;
188
+ }
189
+ function mergeAliasConfigs(rootAliases, nodeAliases) {
190
+ const aliases = {};
191
+ for (const source of [rootAliases, nodeAliases]) {
192
+ if (!isPlainRecord(source))
193
+ continue;
194
+ for (const [alias, model] of Object.entries(source)) {
195
+ if (alias.trim() !== "" && typeof model === "string" && model.trim() !== "") {
196
+ aliases[alias] = model;
197
+ }
198
+ }
199
+ }
200
+ return Object.keys(aliases).length > 0 ? aliases : undefined;
201
+ }
202
+ function taskNodeNeedsRunxClient(conditions) {
203
+ return conditions?.jsConditionFunction != null || conditions?.aiCondition != null;
204
+ }
205
+ function modelConfigSelectionNeedsRunx(sel) {
206
+ if (sel == null || !Array.isArray(sel.cases))
207
+ return false;
208
+ return sel.cases.some((c) => c.when?.jsConditionFunction != null || c.when?.aiCondition != null);
209
+ }
210
+ function graphNeedsRunxClient(graph, runtimeModelConfig) {
211
+ if (modelConfigSelectionNeedsRunx(runtimeModelConfig))
212
+ return true;
213
+ if (modelConfigSelectionNeedsRunx(graph.modelConfig))
214
+ return true;
215
+ for (const n of graph.nodes ?? []) {
216
+ const tn = n;
217
+ if (taskNodeNeedsRunxClient(tn.conditions))
218
+ return true;
219
+ if (modelConfigSelectionNeedsRunx(tn.taskConfiguration?.modelConfig))
220
+ return true;
221
+ }
222
+ return false;
223
+ }
224
+ function mergeKnowledgePatchIntoRunTaskMemory(memory, patch) {
225
+ if (patch == null)
226
+ return memory;
227
+ const base = isPlainRecord(memory) ? { ...memory } : {};
228
+ const existingKnowledge = isPlainRecord(base.knowledge) ? base.knowledge : {};
229
+ return {
230
+ ...base,
231
+ knowledge: {
232
+ ...existingKnowledge,
233
+ ...patch,
234
+ },
235
+ };
236
+ }
237
+ function applyTaskResultMapping(args) {
238
+ if (args.mapping == null || args.outputForMapping === undefined)
239
+ return args.targetMemory;
240
+ const { path, mode, map, fields } = args.mapping;
241
+ return mergeExecutionObject(isPlainRecord(args.targetMemory) ? args.targetMemory : {}, path, args.outputForMapping, mode, map || fields ? { map, fields } : undefined, {
242
+ node: args.node,
243
+ variables: args.variables,
244
+ });
245
+ }
246
+ async function resolveKnowledgePatch(args) {
247
+ const refs = [...new Set((args.refs ?? []).filter((ref) => typeof ref === "string" && ref.trim() !== "").map((ref) => ref.trim()))];
248
+ if (refs.length === 0 || args.resolver == null)
249
+ return undefined;
250
+ const patch = await args.resolver(refs, args.context);
251
+ return patch ?? undefined;
252
+ }
253
+ function normalizeRuntimeExecutionMemory(runtime) {
254
+ const execution = isPlainRecord(runtime.executionMemory)
255
+ ? runtime.executionMemory
256
+ : {};
257
+ if (runtime.input !== undefined) {
258
+ execution.input = isPlainRecord(runtime.input) ? { ...runtime.input } : runtime.input;
259
+ }
260
+ else if (!isPlainRecord(execution.input)) {
261
+ execution.input = {};
262
+ }
263
+ if (isPlainRecord(runtime.inputs)) {
264
+ execution.inputs = { ...runtime.inputs };
265
+ }
266
+ mirrorRuntimeInputRawOnExecutionMemory(execution);
267
+ return execution;
268
+ }
269
+ function normalizeRuntimeOutputsMemory(runtime) {
270
+ return isPlainRecord(runtime.outputsMemory) ? runtime.outputsMemory : {};
271
+ }
272
+ export function createExellixGraphRuntime(opts) {
273
+ function parsePositiveInt(v) {
274
+ const n = Number(v);
275
+ if (!Number.isFinite(n))
276
+ return undefined;
277
+ const i = Math.floor(n);
278
+ return i > 0 ? i : undefined;
279
+ }
280
+ /**
281
+ * Concurrency tiers (highest → lowest):
282
+ * 1) node-level: node.concurrency | node.taskConfiguration.concurrency | node.data.concurrency
283
+ * 2) graph-level: graph.concurrency | graph.metadata.concurrency | graph.config?.concurrency
284
+ * 3) env-level: process.env.WOREX_GRAPH_CONCURRENCY
285
+ * 4) default fallback: 4
286
+ *
287
+ * Node-level is "confusing" because it affects siblings, so we interpret it as:
288
+ * - If multiple runnable nodes specify concurrency, we take the MIN across them
289
+ * (strictest wins) for the current batch.
290
+ */
291
+ function resolveConcurrencyLimit(args) {
292
+ // Tier 4: default fallback
293
+ let limit = 4;
294
+ // Tier 3: env
295
+ const envLimit = parsePositiveInt(process.env.WOREX_GRAPH_CONCURRENCY);
296
+ if (envLimit)
297
+ limit = envLimit;
298
+ // Tier 2: graph-level
299
+ const graphLimit = parsePositiveInt(args.graph?.concurrency) ??
300
+ parsePositiveInt(args.graph?.metadata?.concurrency) ??
301
+ parsePositiveInt(args.graph?.config?.concurrency);
302
+ if (graphLimit)
303
+ limit = graphLimit;
304
+ // Tier 1: node-level (strictest wins for the batch)
305
+ const nodeLimits = args.runnableNodes
306
+ .map((n) => parsePositiveInt(n?.concurrency) ??
307
+ parsePositiveInt(n?.taskConfiguration?.concurrency) ??
308
+ parsePositiveInt(n?.data?.concurrency))
309
+ .filter((x) => typeof x === "number");
310
+ if (nodeLimits.length) {
311
+ limit = Math.min(limit, ...nodeLimits);
312
+ }
313
+ // Also respect runtime option if you still want it as a hard cap:
314
+ const hardCap = parsePositiveInt(opts.concurrency);
315
+ if (hardCap)
316
+ limit = Math.min(limit, hardCap);
317
+ return Math.max(1, limit);
318
+ }
319
+ /**
320
+ * Runs items with a concurrency limit. If failFast=true, stops scheduling new items
321
+ * after first error, but does not "cancel" in-flight executions.
322
+ */
323
+ async function runPool(items, concurrency, worker, options) {
324
+ const failFastLocal = options?.failFast ?? false;
325
+ let index = 0;
326
+ let aborted = false;
327
+ async function runner() {
328
+ while (true) {
329
+ if (aborted)
330
+ return;
331
+ const i = index++;
332
+ if (i >= items.length)
333
+ return;
334
+ try {
335
+ await worker(items[i]);
336
+ }
337
+ catch (e) {
338
+ if (failFastLocal)
339
+ aborted = true;
340
+ throw e;
341
+ }
342
+ }
343
+ }
344
+ const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => runner());
345
+ // We want all worker errors, but caller usually handles errors per-node.
346
+ // Here, we let errors bubble.
347
+ await Promise.all(workers);
348
+ }
349
+ async function executeNode(input) {
350
+ const graphRunTaskId = input.graphRunTaskId ?? newGraphRunTaskId();
351
+ const runTaskJobIdStr = String(input.job?.jobId ?? input.job?.id ?? "");
352
+ const lifecycleJobId = runTaskJobIdStr || String(input.job?.jobId ?? input.job?.id ?? "standalone-job");
353
+ const effectiveLlmCall = mergeDefinedLlmCallParts((input.llmCall ?? opts.llmCall), input.node?.taskConfiguration?.llmCall);
354
+ const forwardRunTaskTrace = shouldForwardRunTaskTraceMode({
355
+ runTaskExecutionMode: input.runTaskExecutionMode ?? opts.runTaskExecutionMode,
356
+ });
357
+ const runTaskIdentityBase = input.runTaskIdentity ?? opts.runTaskIdentity;
358
+ if (input.node?.type === 'finalizer') {
359
+ const finalizer = input.node;
360
+ let execution = input.execution;
361
+ const outputsMemory = isPlainRecord(input.outputsMemory) ? input.outputsMemory : {};
362
+ if (execution == null || typeof execution !== 'object')
363
+ execution = {};
364
+ if (!execution._trace)
365
+ execution._trace = { nodes: {} };
366
+ if (!execution._trace.nodes)
367
+ execution._trace.nodes = {};
368
+ const startedAt = Date.now();
369
+ const finalizerIdentity = buildRunTaskIdentityEnvelope({
370
+ base: runTaskIdentityBase,
371
+ nodeMeta: finalizer.metadata,
372
+ graphId: input.graphId,
373
+ nodeId: String(finalizer.id),
374
+ taskId: graphRunTaskId,
375
+ jobId: lifecycleJobId,
376
+ });
377
+ if (input.eventEmitter) {
378
+ input.eventEmitter.emit(createNodeStartEvent(lifecycleJobId, input.graphId, graphRunTaskId, finalizer, "finalizer", undefined, input.jobCorrelation));
379
+ }
380
+ try {
381
+ const mode = finalizer.finalizerType === 'synthesize' ? 'ai' : 'deterministic';
382
+ const { parsed, rawContent } = finalizer.finalizerType === 'synthesize'
383
+ ? await executeSynthesizeFinalizer({
384
+ finalizer,
385
+ config: finalizer.config,
386
+ executionMemory: execution,
387
+ outputsMemory,
388
+ runTask: (req) => opts.tasksClient.runTask({
389
+ ...req,
390
+ agentId: input.job?.agentId ?? "standalone-agent",
391
+ jobTypeId: resolveJobTypeId(input.job),
392
+ taskTypeId: `exellix-graph-finalizer:${String(finalizer.config?.utilityKey ?? "synthesize")}`,
393
+ coreSkillId: String(finalizer.id),
394
+ nodeId: String(finalizer.id),
395
+ graphId: input.graphId,
396
+ masterSkillId: input.graphId,
397
+ masterSkillActivityId: `${lifecycleJobId}:${finalizer.id}`,
398
+ input: req.input ?? {},
399
+ identity: {
400
+ ...finalizerIdentity,
401
+ ...(req.identity &&
402
+ typeof req.identity === "object" &&
403
+ !Array.isArray(req.identity)
404
+ ? req.identity
405
+ : {}),
406
+ kind: "finalizer",
407
+ },
408
+ ...(input.modelConfig != null ? { modelConfig: input.modelConfig } : {}),
409
+ ...(effectiveLlmCall != null ? { llmCall: effectiveLlmCall } : {}),
410
+ ...(forwardRunTaskTrace ? { executionMode: "trace" } : {}),
411
+ ...((input.runTaskDiagnostics ?? opts.runTaskDiagnostics) != null
412
+ ? { diagnostics: input.runTaskDiagnostics ?? opts.runTaskDiagnostics }
413
+ : {}),
414
+ }),
415
+ context: {
416
+ graphId: input.graphId,
417
+ jobId: input.job?.jobId,
418
+ taskId: graphRunTaskId,
419
+ agentId: input.job?.agentId,
420
+ },
421
+ })
422
+ : {
423
+ ...executeDeterministicFinalizer({
424
+ finalizer,
425
+ executionMemory: execution,
426
+ outputsMemory,
427
+ graph: input.graph,
428
+ }),
429
+ rawContent: undefined,
430
+ };
431
+ const endedAt = Date.now();
432
+ execution._trace.nodes[String(finalizer.id)] = {
433
+ startedAt,
434
+ endedAt,
435
+ skillKey: 'finalizer',
436
+ ok: true,
437
+ durationMs: endedAt - startedAt,
438
+ summary: {
439
+ finalizerType: finalizer.finalizerType,
440
+ mode,
441
+ ...(mode === 'ai' ? { executionPolicy: finalizer.config?.executionPolicy } : {}),
442
+ },
443
+ };
444
+ const finalizerOut = { parsed, ...(rawContent ? { rawContent } : {}) };
445
+ const outputForMapping = parsed !== undefined
446
+ ? (isPlainRecord(parsed)
447
+ ? { ...parsed, parsed, ...(rawContent ? { rawContent } : {}) }
448
+ : { parsed, ...(rawContent ? { rawContent } : {}) })
449
+ : rawContent
450
+ ? { rawContent }
451
+ : undefined;
452
+ const updatedOutputsMemory = applyTaskResultMapping({
453
+ targetMemory: outputsMemory,
454
+ mapping: finalizer.outputMapping,
455
+ outputForMapping,
456
+ node: finalizer,
457
+ variables: {},
458
+ });
459
+ if (input.eventEmitter) {
460
+ input.eventEmitter.emit(createNodeCompleteEvent(lifecycleJobId, input.graphId, graphRunTaskId, finalizer, "finalizer", {
461
+ output: finalizerOut,
462
+ memoryAfter: {
463
+ jobMemory: input.jobMemory,
464
+ taskMemory: input.taskMemory,
465
+ execution,
466
+ outputsMemory: updatedOutputsMemory,
467
+ },
468
+ }, input.jobCorrelation));
469
+ }
470
+ return {
471
+ nodeId: String(finalizer.id),
472
+ skillKey: 'finalizer',
473
+ output: finalizerOut,
474
+ execution,
475
+ outputsMemory: updatedOutputsMemory,
476
+ };
477
+ }
478
+ catch (e) {
479
+ const endedAt = Date.now();
480
+ const err = e?.code != null
481
+ ? e
482
+ : createFinalizerError({
483
+ code: 'GRAPH_FINALIZER_OUTPUT_INVALID',
484
+ message: e?.message ?? 'Finalizer failed',
485
+ finalizerNodeId: String(finalizer.id),
486
+ details: { error: e },
487
+ });
488
+ execution._trace.nodes[String(finalizer.id)] = {
489
+ startedAt,
490
+ endedAt,
491
+ skillKey: 'finalizer',
492
+ ok: false,
493
+ durationMs: endedAt - startedAt,
494
+ error: { message: err.message, code: err.code, stack: err.stack },
495
+ };
496
+ if (input.eventEmitter) {
497
+ input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, finalizer, "finalizer", {
498
+ error: { message: err.message, code: err.code, stack: err.stack },
499
+ memoryAfter: {
500
+ jobMemory: input.jobMemory,
501
+ taskMemory: input.taskMemory,
502
+ execution,
503
+ outputsMemory,
504
+ },
505
+ response: undefined,
506
+ }, input.jobCorrelation));
507
+ }
508
+ markNodeLifecycleEmitted(err);
509
+ throw err;
510
+ }
511
+ }
512
+ const skillKey = resolveTaskKey(input.node, input.skillKeyResolution ?? {}, {
513
+ jobId: runTaskJobIdStr || undefined,
514
+ graphId: input.graphId,
515
+ });
516
+ // Local-skill interception: scoped-data-reader, deterministic-rule, scoped-answer-writer,
517
+ // scoped-answer-assembler all run in-process — never call `tasksClient.runTask`.
518
+ if (isLocalSkillKey(skillKey)) {
519
+ let execution = input.execution;
520
+ const updatedOutputsMemory = isPlainRecord(input.outputsMemory) ? input.outputsMemory : {};
521
+ if (execution == null || typeof execution !== "object")
522
+ execution = {};
523
+ if (!execution._trace)
524
+ execution._trace = { nodes: {} };
525
+ if (!execution._trace.nodes)
526
+ execution._trace.nodes = {};
527
+ const startedAt = Date.now();
528
+ const localCtx = {
529
+ jobId: lifecycleJobId,
530
+ graphId: input.graphId,
531
+ taskId: graphRunTaskId,
532
+ };
533
+ if (input.eventEmitter) {
534
+ input.eventEmitter.emit(createNodeStartEvent(localCtx.jobId, localCtx.graphId, localCtx.taskId, input.node, skillKey, undefined, input.jobCorrelation));
535
+ }
536
+ try {
537
+ const cfg = input.node?.taskConfiguration ?? {};
538
+ const pathOpts = { graphTaskNodeId: String(input.node.id) };
539
+ let output;
540
+ if (skillKey === 'scoped-data-reader') {
541
+ output = (await runScopedDataReader(cfg, execution, pathOpts));
542
+ }
543
+ else if (skillKey === 'deterministic-rule') {
544
+ output = runDeterministicRule(cfg, execution, pathOpts);
545
+ }
546
+ else if (skillKey === 'scoped-answer-writer') {
547
+ output = (await runScopedAnswerWriter(cfg, execution, pathOpts));
548
+ }
549
+ else {
550
+ output = runScopedAnswerAssembler(cfg, execution, pathOpts);
551
+ }
552
+ const outputForMapping = { ...output, parsed: output };
553
+ execution = applyTaskResultMapping({
554
+ targetMemory: execution,
555
+ mapping: input.node?.executionMapping,
556
+ outputForMapping,
557
+ node: input.node,
558
+ variables: {},
559
+ });
560
+ const endedAt = Date.now();
561
+ if (execution == null || typeof execution !== 'object')
562
+ execution = {};
563
+ if (!execution._trace)
564
+ execution._trace = { nodes: {} };
565
+ if (!execution._trace.nodes)
566
+ execution._trace.nodes = {};
567
+ execution._trace.nodes[String(input.node.id)] = {
568
+ startedAt,
569
+ endedAt,
570
+ skillKey,
571
+ ok: true,
572
+ durationMs: endedAt - startedAt,
573
+ };
574
+ if (input.eventEmitter) {
575
+ input.eventEmitter.emit(createNodeCompleteEvent(localCtx.jobId, localCtx.graphId, localCtx.taskId, input.node, skillKey, {
576
+ output,
577
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution, outputsMemory: updatedOutputsMemory },
578
+ response: { success: true, output },
579
+ }, input.jobCorrelation));
580
+ }
581
+ return {
582
+ nodeId: input.node.id,
583
+ skillKey,
584
+ output: { ...output, parsed: output },
585
+ execution,
586
+ outputsMemory: updatedOutputsMemory,
587
+ };
588
+ }
589
+ catch (err) {
590
+ const endedAt = Date.now();
591
+ execution._trace.nodes[String(input.node.id)] = {
592
+ startedAt,
593
+ endedAt,
594
+ skillKey,
595
+ ok: false,
596
+ durationMs: endedAt - startedAt,
597
+ error: { message: err?.message ?? 'Local skill failed', code: err?.code },
598
+ };
599
+ if (input.eventEmitter) {
600
+ input.eventEmitter.emit(createNodeFailEvent(localCtx.jobId, localCtx.graphId, localCtx.taskId, input.node, skillKey, {
601
+ error: { code: err?.code ?? 'LOCAL_SKILL_FAILED', message: err?.message ?? 'Local skill failed' },
602
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
603
+ response: undefined,
604
+ }, input.jobCorrelation));
605
+ }
606
+ const wrapped = err?.code != null ? err : new Error(err?.message ?? 'Local skill failed');
607
+ if (wrapped.code == null)
608
+ wrapped.code = 'LOCAL_SKILL_FAILED';
609
+ wrapped.nodeId = input.node?.id;
610
+ markNodeLifecycleEmitted(wrapped);
611
+ throw wrapped;
612
+ }
613
+ }
614
+ if (input.eventEmitter) {
615
+ input.eventEmitter.emit(createNodeStartEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, undefined, input.jobCorrelation));
616
+ }
617
+ const effectiveModelConfig = input.modelConfig;
618
+ // DEBUG: Verify execution object before sending
619
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
620
+ console.log(`[DEBUG] Node ${input.node?.id}: execution object =`, JSON.stringify(input.execution, null, 2));
621
+ }
622
+ let execution = input.execution;
623
+ if (execution == null || typeof execution !== "object")
624
+ execution = {};
625
+ if (!execution._trace)
626
+ execution._trace = { nodes: {} };
627
+ if (!execution._trace.nodes)
628
+ execution._trace.nodes = {};
629
+ try {
630
+ assertAiTasksNodeExtensionsValid(input.node, `nodes.${String(input.node.id)}`);
631
+ }
632
+ catch (pe) {
633
+ if (input.eventEmitter) {
634
+ input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
635
+ error: { message: pe?.message, code: pe?.code, stack: pe?.stack },
636
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
637
+ response: undefined,
638
+ }, input.jobCorrelation));
639
+ }
640
+ markNodeLifecycleEmitted(pe);
641
+ throw pe;
642
+ }
643
+ let executionPipeline;
644
+ try {
645
+ executionPipeline = resolveExecutionPipelineForTaskNode({
646
+ node: input.node,
647
+ optionsExecutionPipeline: input.graphExecutionPipeline,
648
+ executionExecutionPipeline: input.execution?.executionPipeline,
649
+ });
650
+ }
651
+ catch (pe) {
652
+ if (input.eventEmitter) {
653
+ input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
654
+ error: { message: pe?.message, code: pe?.code, stack: pe?.stack },
655
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
656
+ response: undefined,
657
+ }, input.jobCorrelation));
658
+ }
659
+ const err = new Error(pe?.message ?? "Execution pipeline resolution failed");
660
+ err.code = pe?.code ?? "INPUT_SYNTHESIS_PIPELINE_CONFLICT";
661
+ err.nodeId = input.node?.id;
662
+ markNodeLifecycleEmitted(err);
663
+ throw err;
664
+ }
665
+ const hasSynthesizedContextPreStep = Array.isArray(executionPipeline) &&
666
+ executionPipeline.some((s) => s.phase === "pre" && s.type === "synthesized-context");
667
+ const preStepConfig = hasSynthesizedContextPreStep && Array.isArray(executionPipeline)
668
+ ? normalizeSynthesizedContextConfig(executionPipeline.find((s) => s.phase === "pre" && s.type === "synthesized-context")?.config)
669
+ : undefined;
670
+ const includeContextInPrompt = hasSynthesizedContextPreStep && preStepConfig?.autoEnableContext !== true;
671
+ const executionRec = execution;
672
+ mirrorTaskVariablesOnExecution({
673
+ execution: executionRec,
674
+ node: input.node,
675
+ runtimeTaskVariables: input.taskVariables ?? undefined,
676
+ });
677
+ const variableBuckets = readExecutionVariableBuckets(executionRec);
678
+ const jobVariables = variableBuckets.jobVariables;
679
+ const hasExistingSynthesizedContext = execution != null &&
680
+ typeof execution === "object" &&
681
+ execution.synthesizedContext != null;
682
+ const clearSynthForTask = input.clearSynthesizedContextPerNode === true &&
683
+ hasSynthesizedContextPreStep &&
684
+ hasExistingSynthesizedContext;
685
+ mirrorRuntimeInputRawOnExecutionMemory(executionRec);
686
+ const nodeBindings = resolveTaskNodeInputsForRunTask({
687
+ node: input.node,
688
+ execution: executionRec,
689
+ });
690
+ const jobContext = buildTaskNodeJobContext({
691
+ node: input.node,
692
+ jobMemory: input.jobMemory,
693
+ executionMemory: executionRec,
694
+ variables: jobVariables,
695
+ });
696
+ const narrix = resolveNarrixForTaskNode({
697
+ nodeMetadataNarrix: input.node?.taskConfiguration?.narrix != null
698
+ ? { ...input.node.taskConfiguration.narrix }
699
+ : undefined,
700
+ graphVariablesNarrix: jobVariables?.narrix,
701
+ aiTaskProfile: input.node?.taskConfiguration?.aiTaskProfile,
702
+ });
703
+ const metaTk = input.node?.taskConfiguration?.taskKind;
704
+ const taskKindForward = metaTk === "decision" || metaTk === "utility" || metaTk === "content" ? metaTk : undefined;
705
+ const autoValDec = input.node?.taskConfiguration?.autoValidateDecisionOutput;
706
+ const strategyKeys = getAiTaskProfileStrategyKeys(input.node?.taskConfiguration);
707
+ const startedAt = Date.now();
708
+ if (strategyKeys.preStrategyKey != null) {
709
+ const preRes = await runEngineAiTasksStrategyPhase({
710
+ phase: "pre",
711
+ strategySkillKey: strategyKeys.preStrategyKey,
712
+ parentSkillKey: skillKey,
713
+ aiTasks: opts.tasksClient,
714
+ job: input.job,
715
+ nodeId: String(input.node.id),
716
+ graphId: input.graphId,
717
+ taskId: graphRunTaskId,
718
+ runTaskJobId: lifecycleJobId,
719
+ execution: executionRec,
720
+ variables: jobVariables,
721
+ jobMemory: input.jobMemory,
722
+ taskMemory: input.taskMemory,
723
+ jobContext,
724
+ prevNodeId: input.prevNodeId,
725
+ modelConfig: effectiveModelConfig,
726
+ llmCall: effectiveLlmCall,
727
+ runTaskIdentity: runTaskIdentityBase,
728
+ nodeTaskConfiguration: input.node?.taskConfiguration,
729
+ forwardRunTaskTrace,
730
+ runTaskDiagnostics: (input.runTaskDiagnostics ?? opts.runTaskDiagnostics),
731
+ nodeTimeoutMs: input.nodeTimeoutMs,
732
+ });
733
+ if (!preRes.ok) {
734
+ const err = new Error(preRes.response.error?.message ?? "ENGINE_PRE_STRATEGY_FAILED");
735
+ err.code = preRes.response.error?.code ?? "ENGINE_PRE_STRATEGY_FAILED";
736
+ err.nodeId = input.node?.id;
737
+ if (input.eventEmitter) {
738
+ input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
739
+ error: {
740
+ message: err.message,
741
+ code: err.code,
742
+ ...(preRes.response.error != null ? { details: preRes.response.error } : {}),
743
+ },
744
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
745
+ response: preRes.response,
746
+ }, input.jobCorrelation));
747
+ }
748
+ markNodeLifecycleEmitted(err);
749
+ throw err;
750
+ }
751
+ }
752
+ const executionForTask = clearSynthForTask
753
+ ? (() => {
754
+ const c = { ...executionRec };
755
+ delete c.synthesizedContext;
756
+ return c;
757
+ })()
758
+ : executionRec;
759
+ const callerInputs = extractCallerInputsBag({
760
+ execution: executionRec,
761
+ jobMemory: isPlainRecord(input.jobMemory) ? input.jobMemory : undefined,
762
+ });
763
+ const taskInput = buildRunTaskMainInput({
764
+ execution: executionRec,
765
+ jobMemory: isPlainRecord(input.jobMemory) ? input.jobMemory : undefined,
766
+ nodeBindings: {
767
+ ...nodeBindings,
768
+ ...(input.node?.data?.upstreamNodeIds !== undefined
769
+ ? { upstreamNodeIds: input.node.data.upstreamNodeIds }
770
+ : {}),
771
+ },
772
+ });
773
+ mirrorStructuredInputOntoExecutionMemory(executionForTask, taskInput);
774
+ const graphDocModel = jobVariables[EXELLIX_GRAPH_MODEL_VARIABLE_KEY];
775
+ const graphEntry = graphDocModel != null && typeof graphDocModel === "object" && !Array.isArray(graphDocModel)
776
+ ? graphDocModel.graphEntry
777
+ : undefined;
778
+ const readinessPolicy = resolveMainReadinessPolicy({
779
+ runtimePolicy: input.mainReadinessPolicy,
780
+ nodeTaskConfiguration: input.node?.taskConfiguration,
781
+ });
782
+ const readinessRoot = buildMainReadinessResolutionRoot({
783
+ taskInput,
784
+ callerInputs,
785
+ executionMemory: executionForTask,
786
+ xynthesized: xynthesizedOutboundForNode(executionRec, String(input.node.id)),
787
+ jobMemory: input.jobMemory,
788
+ taskMemory: input.taskMemory,
789
+ });
790
+ const readiness = evaluateTaskNodeMainReadiness({
791
+ policy: readinessPolicy,
792
+ node: input.node,
793
+ graphEntry,
794
+ resolutionRoot: readinessRoot,
795
+ });
796
+ if (!readiness.ok) {
797
+ const err = new Error(`MAIN_READINESS_FAILED: ${readiness.failedChecks.join(", ")} (nodeId=${String(input.node.id)})`);
798
+ err.code = ExellixGraphErrorCode.MAIN_READINESS_FAILED;
799
+ err.nodeId = input.node?.id;
800
+ err.readiness = readiness;
801
+ if (input.eventEmitter) {
802
+ input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
803
+ error: {
804
+ message: err.message,
805
+ code: err.code,
806
+ details: { readiness },
807
+ },
808
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution: executionRec },
809
+ response: undefined,
810
+ }, input.jobCorrelation));
811
+ }
812
+ markNodeLifecycleEmitted(err);
813
+ throw err;
814
+ }
815
+ const runTaskJobMemory = mergeKnowledgePatchIntoRunTaskMemory(input.jobMemory, input.jobKnowledgePatch);
816
+ const runTaskTaskMemory = mergeKnowledgePatchIntoRunTaskMemory(input.taskMemory, input.taskKnowledgePatch);
817
+ const metaStrats = input.node?.taskConfiguration?.executionStrategies;
818
+ const req = buildAiTasksRunTaskRequest({
819
+ skillKey,
820
+ job: input.job,
821
+ nodeId: String(input.node.id),
822
+ taskConfiguration: input.node?.taskConfiguration,
823
+ graphId: input.graphId,
824
+ taskId: graphRunTaskId,
825
+ runTaskJobId: lifecycleJobId,
826
+ taskInput,
827
+ callerInputs,
828
+ variables: jobVariables,
829
+ jobMemory: runTaskJobMemory,
830
+ taskMemory: runTaskTaskMemory,
831
+ executionMemory: executionForTask,
832
+ jobContext,
833
+ prevNodeId: input.prevNodeId,
834
+ executionPipeline: executionPipeline,
835
+ includeContextInPrompt: includeContextInPrompt === true ? true : undefined,
836
+ narrix: narrix ?? undefined,
837
+ ...extractRunTaskStrategyOverrides(input.node?.taskConfiguration),
838
+ outputValidation: input.node?.taskConfiguration?.aiTasksOutputValidation != null &&
839
+ typeof input.node.taskConfiguration.aiTasksOutputValidation === "object" &&
840
+ input.node.taskConfiguration.aiTasksOutputValidation !== null &&
841
+ "schema" in input.node.taskConfiguration.aiTasksOutputValidation
842
+ ? input.node.taskConfiguration.aiTasksOutputValidation
843
+ : undefined,
844
+ diagnostics: (input.runTaskDiagnostics ?? opts.runTaskDiagnostics),
845
+ modelConfig: effectiveModelConfig ?? undefined,
846
+ llmCall: effectiveLlmCall,
847
+ identity: buildRunTaskIdentityEnvelope({
848
+ base: runTaskIdentityBase,
849
+ nodeMeta: input.node?.taskConfiguration,
850
+ graphId: input.graphId,
851
+ nodeId: String(input.node.id),
852
+ taskId: graphRunTaskId,
853
+ jobId: lifecycleJobId,
854
+ }),
855
+ executionMode: forwardRunTaskTrace ? "trace" : undefined,
856
+ taskKind: taskKindForward,
857
+ autoValidateDecisionOutput: typeof autoValDec === "boolean" ? autoValDec : undefined,
858
+ executionStrategies: Array.isArray(metaStrats) ? metaStrats : [],
859
+ xynthesized: xynthesizedOutboundForNode(executionRec, String(input.node.id)),
860
+ smartInput: input.node.smartInput,
861
+ });
862
+ // TRACE: Validate request object before sending
863
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
864
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: Node ${input.node?.id} - Request built`);
865
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory =`, JSON.stringify(req.executionMemory, null, 2));
866
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory type =`, typeof req.executionMemory);
867
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory is undefined?`, req.executionMemory === undefined);
868
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory is null?`, req.executionMemory === null);
869
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: req has executionMemory property?`, 'executionMemory' in req);
870
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: req keys =`, Object.keys(req));
871
+ if (req.executionMemory && typeof req.executionMemory === 'object') {
872
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory keys =`, Object.keys(req.executionMemory));
873
+ }
874
+ }
875
+ let res;
876
+ const nodeTimeoutMs = input.nodeTimeoutMs;
877
+ const stepRetryPolicyResolved = resolveStepRetryPolicy(input.stepRetryPolicy ?? opts.stepRetryPolicy, input.node);
878
+ let retryOutcome;
879
+ try {
880
+ retryOutcome = await runRunTaskWithRetry({
881
+ initialRequest: req,
882
+ doRunTask: (r) => opts.tasksClient.runTask(r),
883
+ nodeTimeoutMs,
884
+ policy: stepRetryPolicyResolved,
885
+ nodeId: String(input.node.id),
886
+ skillKey,
887
+ nodeTimeoutErrorFactory: () => {
888
+ const err = new Error(`Node execution timed out after ${nodeTimeoutMs}ms (nodeId=${String(input.node.id)}, skillKey=${String(skillKey)})`);
889
+ err.code = "NODE_TIMEOUT";
890
+ err.nodeId = input.node.id;
891
+ return err;
892
+ },
893
+ });
894
+ res = retryOutcome.response;
895
+ }
896
+ catch (runErr) {
897
+ const endedAt = Date.now();
898
+ const att = Array.isArray(runErr?.stepRetryAttempts) ? runErr.stepRetryAttempts : [];
899
+ execution._trace.nodes[input.node.id] = {
900
+ startedAt,
901
+ endedAt,
902
+ skillKey,
903
+ ok: false,
904
+ durationMs: endedAt - startedAt,
905
+ error: {
906
+ message: runErr?.message ?? "Task run failed",
907
+ code: runErr?.code,
908
+ stack: runErr?.stack,
909
+ },
910
+ ...(att.length ? { attempts: att } : {}),
911
+ };
912
+ const syn = Array.isArray(runErr?.syntheticStepRetryRunLog) ? runErr.syntheticStepRetryRunLog : [];
913
+ const prevLog = Array.isArray(runErr?.taskRunLog) ? runErr.taskRunLog : [];
914
+ runErr.taskRunLog = [...syn, ...prevLog];
915
+ if (input.eventEmitter) {
916
+ input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
917
+ error: {
918
+ message: runErr?.message ?? "Task run failed",
919
+ code: runErr?.code,
920
+ stack: runErr?.stack,
921
+ },
922
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
923
+ response: undefined,
924
+ }, input.jobCorrelation));
925
+ }
926
+ markNodeLifecycleEmitted(runErr);
927
+ throw runErr;
928
+ }
929
+ const endedAt = Date.now();
930
+ const durationMs = res.parsed?.meta?.durationMs ?? (endedAt - startedAt);
931
+ const activityId = res.parsed?.meta?.activityId ?? res.parsed?.meta?.localTaskId;
932
+ const summary = res.parsed?.meta != null ? { ...res.parsed.meta } : undefined;
933
+ if (!taskRunSucceeded(res)) {
934
+ execution._trace.nodes[input.node.id] = {
935
+ startedAt,
936
+ endedAt,
937
+ skillKey,
938
+ ok: false,
939
+ durationMs,
940
+ activityId,
941
+ summary,
942
+ error: {
943
+ message: res.error?.message ?? "Task run failed",
944
+ code: res.error?.code,
945
+ },
946
+ attempts: retryOutcome.attempts,
947
+ };
948
+ const resMeta = res.metadata ?? res.meta;
949
+ const taskRunLog = [...retryOutcome.syntheticRunLog, ...extractTaskRunLogFromMetadata(resMeta)];
950
+ const logxerCorrelationId = extractLogxerCorrelationFromMetadata(resMeta);
951
+ const err = new Error(`TASK_RUN_FAILED: ${skillKey}`);
952
+ err.code = res.error?.code ?? "TASK_RUN_FAILED";
953
+ err.skillKey = skillKey;
954
+ err.diagnostics = res.diagnostics;
955
+ err.nodeId = input.node?.id;
956
+ if (taskRunLog.length)
957
+ err.taskRunLog = taskRunLog;
958
+ if (logxerCorrelationId)
959
+ err.logxerCorrelationId = logxerCorrelationId;
960
+ err.stepRetryAttempts = retryOutcome.attempts;
961
+ if (input.eventEmitter) {
962
+ input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
963
+ error: {
964
+ message: err.message,
965
+ code: err.code,
966
+ },
967
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
968
+ response: res,
969
+ }, input.jobCorrelation));
970
+ }
971
+ markNodeLifecycleEmitted(err);
972
+ throw err;
973
+ }
974
+ let updatedExecution = res.execution !== undefined ? res.execution : execution;
975
+ copyExecutionContextFields(updatedExecution, execution, ["webContext", "synthesizedContext"]);
976
+ copyExecutionContextFields(updatedExecution, res.executionMemory, ["webContext", "synthesizedContext"]);
977
+ const xPatchRt = res.xynthesizedPatch;
978
+ mergeXynthesizedPatchIntoExecution({
979
+ execution: updatedExecution,
980
+ nodeId: String(input.node.id),
981
+ patch: xPatchRt,
982
+ });
983
+ const traceSynthExtrasRt = {
984
+ smartInput: { paths: [...(input.node.smartInput?.paths ?? [])] },
985
+ xynthesized: {
986
+ jobPatchKeys: Object.keys(xPatchRt?.job ?? {}),
987
+ taskPatchKeys: Object.keys(xPatchRt?.task ?? {}),
988
+ executionPatchKeys: Object.keys(xPatchRt?.execution ?? {}),
989
+ },
990
+ };
991
+ const mergedTraceSummaryRt = summary != null && typeof summary === "object" && !Array.isArray(summary)
992
+ ? { ...summary, ...traceSynthExtrasRt }
993
+ : traceSynthExtrasRt;
994
+ execution._trace.nodes[input.node.id] = {
995
+ startedAt,
996
+ endedAt,
997
+ skillKey,
998
+ ok: true,
999
+ durationMs,
1000
+ activityId,
1001
+ summary: mergedTraceSummaryRt,
1002
+ attempts: retryOutcome.attempts,
1003
+ };
1004
+ // Handle execution object updates from response (patch merged above)
1005
+ // Note: We don't modify jobMemory or taskMemory - they're set before the job starts and passed through as-is
1006
+ if (strategyKeys.postStrategyKey != null) {
1007
+ const postExec = updatedExecution != null && typeof updatedExecution === "object" && !Array.isArray(updatedExecution)
1008
+ ? updatedExecution
1009
+ : executionRec;
1010
+ const postRes = await runEngineAiTasksStrategyPhase({
1011
+ phase: "post",
1012
+ strategySkillKey: strategyKeys.postStrategyKey,
1013
+ parentSkillKey: skillKey,
1014
+ aiTasks: opts.tasksClient,
1015
+ job: input.job,
1016
+ nodeId: String(input.node.id),
1017
+ graphId: input.graphId,
1018
+ taskId: graphRunTaskId,
1019
+ runTaskJobId: lifecycleJobId,
1020
+ execution: postExec,
1021
+ variables: jobVariables,
1022
+ jobMemory: input.jobMemory,
1023
+ taskMemory: input.taskMemory,
1024
+ jobContext,
1025
+ prevNodeId: input.prevNodeId,
1026
+ modelConfig: effectiveModelConfig,
1027
+ llmCall: effectiveLlmCall,
1028
+ runTaskIdentity: runTaskIdentityBase,
1029
+ nodeTaskConfiguration: input.node?.taskConfiguration,
1030
+ forwardRunTaskTrace,
1031
+ runTaskDiagnostics: (input.runTaskDiagnostics ?? opts.runTaskDiagnostics),
1032
+ nodeTimeoutMs: input.nodeTimeoutMs,
1033
+ });
1034
+ if (!postRes.ok) {
1035
+ const err = new Error(postRes.response.error?.message ?? "ENGINE_POST_STRATEGY_FAILED");
1036
+ err.code = postRes.response.error?.code ?? "ENGINE_POST_STRATEGY_FAILED";
1037
+ err.nodeId = input.node?.id;
1038
+ if (input.eventEmitter) {
1039
+ input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
1040
+ error: {
1041
+ message: err.message,
1042
+ code: err.code,
1043
+ ...(postRes.response.error != null ? { details: postRes.response.error } : {}),
1044
+ },
1045
+ memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution: postExec },
1046
+ response: postRes.response,
1047
+ }, input.jobCorrelation));
1048
+ }
1049
+ markNodeLifecycleEmitted(err);
1050
+ throw err;
1051
+ }
1052
+ updatedExecution = postExec;
1053
+ }
1054
+ // TRACE: Log execution object from response
1055
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true' || process.env.DEBUG_OUTPUT_MAPPING === 'true') {
1056
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: Node ${input.node?.id} - Processing response`);
1057
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.execution =`, JSON.stringify(res.execution, null, 2));
1058
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: input.execution =`, JSON.stringify(input.execution, null, 2));
1059
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: updatedExecution (before mapping) =`, JSON.stringify(updatedExecution, null, 2));
1060
+ }
1061
+ // Normalize task output for executionMapping.
1062
+ // Normalize: create output object that includes both response output and parsed
1063
+ // This allows path resolution to access both "output.parsed.shortAnswer" and "output.shortAnswer"
1064
+ // If response.parsed exists, wrap it in { parsed: ... } structure for path resolution
1065
+ const responseParsed = res.parsed;
1066
+ // TRACE: Log response structure
1067
+ if (process.env.DEBUG_OUTPUT_MAPPING === 'true' || process.env.DEBUG_EXECUTION_MEMORY === 'true') {
1068
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: Node ${input.node?.id} - Response structure`);
1069
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed =`, JSON.stringify(res.parsed, null, 2));
1070
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed type =`, typeof res.parsed);
1071
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed keys =`, res.parsed && typeof res.parsed === 'object' ? Object.keys(res.parsed) : 'N/A');
1072
+ if (res.parsed && typeof res.parsed === 'object') {
1073
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed.shortAnswer =`, res.parsed.shortAnswer);
1074
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed.fullAnswer =`, res.parsed.fullAnswer);
1075
+ }
1076
+ }
1077
+ // Create output object similar to executeNode.ts - use parsed as the base, or create structure
1078
+ const outputForMapping = res.parsed !== undefined
1079
+ ? (typeof res.parsed === 'object' && res.parsed !== null && !Array.isArray(res.parsed))
1080
+ ? { ...res.parsed, parsed: responseParsed || res.parsed }
1081
+ : { parsed: responseParsed } // Wrap parsed in object structure for path resolution
1082
+ : undefined;
1083
+ // TRACE: Log outputForMapping structure
1084
+ if (process.env.DEBUG_OUTPUT_MAPPING === 'true' || process.env.DEBUG_EXECUTION_MEMORY === 'true') {
1085
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping structure`);
1086
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping =`, JSON.stringify(outputForMapping, null, 2));
1087
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping.parsed =`, JSON.stringify(outputForMapping?.parsed, null, 2));
1088
+ if (outputForMapping?.parsed) {
1089
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping.parsed.shortAnswer =`, outputForMapping.parsed.shortAnswer);
1090
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping.parsed.fullAnswer =`, outputForMapping.parsed.fullAnswer);
1091
+ }
1092
+ }
1093
+ updatedExecution = applyTaskResultMapping({
1094
+ targetMemory: updatedExecution,
1095
+ mapping: input.node?.executionMapping,
1096
+ outputForMapping,
1097
+ node: input.node,
1098
+ variables: jobVariables,
1099
+ });
1100
+ const updatedOutputsMemory = isPlainRecord(input.outputsMemory) ? input.outputsMemory : {};
1101
+ // TRACE: Log final execution state before returning
1102
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true' || process.env.DEBUG_OUTPUT_MAPPING === 'true') {
1103
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: Node ${input.node?.id} - Final execution state before returning`);
1104
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: updatedExecution =`, JSON.stringify(updatedExecution, null, 2));
1105
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: updatedExecution type =`, typeof updatedExecution);
1106
+ console.log(`[TRACE] ExellixGraphRuntime.executeNode: updatedExecution is undefined?`, updatedExecution === undefined);
1107
+ }
1108
+ const resMetaOk = res.metadata ?? res.meta;
1109
+ const taskRunLogOk = [...retryOutcome.syntheticRunLog, ...extractTaskRunLogFromMetadata(resMetaOk)];
1110
+ const logxerCorrelationIdOk = extractLogxerCorrelationFromMetadata(resMetaOk);
1111
+ const aiTasksObservability = buildAiTasksObservabilityRecord(res, retryOutcome.lastRequest);
1112
+ if (retryOutcome.attempts.length) {
1113
+ aiTasksObservability.attempts = retryOutcome.attempts;
1114
+ }
1115
+ if (readiness.warnings.length > 0) {
1116
+ aiTasksObservability.mainReadinessWarnings = readiness.warnings;
1117
+ aiTasksObservability.mainReadiness = readiness;
1118
+ }
1119
+ const taskOutput = {
1120
+ rawContent: res.rawContent ?? res.rawText,
1121
+ parsed: res.parsed,
1122
+ };
1123
+ if (input.eventEmitter) {
1124
+ input.eventEmitter.emit(createNodeCompleteEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
1125
+ output: taskOutput,
1126
+ memoryAfter: {
1127
+ jobMemory: input.jobMemory,
1128
+ taskMemory: input.taskMemory,
1129
+ execution: updatedExecution,
1130
+ outputsMemory: updatedOutputsMemory,
1131
+ },
1132
+ }, input.jobCorrelation));
1133
+ }
1134
+ return {
1135
+ nodeId: input.node.id,
1136
+ skillKey,
1137
+ output: taskOutput,
1138
+ execution: updatedExecution, // Return execution object so it can be accumulated
1139
+ outputsMemory: updatedOutputsMemory,
1140
+ request: req,
1141
+ ...(taskRunLogOk.length ? { taskRunLog: taskRunLogOk } : {}),
1142
+ ...(logxerCorrelationIdOk ? { logxerCorrelationId: logxerCorrelationIdOk } : {}),
1143
+ aiTasksObservability,
1144
+ };
1145
+ }
1146
+ async function executeGraph(input) {
1147
+ const { model: suppliedGraph, runtime } = input;
1148
+ const { graph } = migrateLegacyGraphResponse(suppliedGraph);
1149
+ const merged = mergeExellixGraphRuntimeInvocation(runtime, opts);
1150
+ const failFast = merged.failFast;
1151
+ const debugMode = runtime.debugMode === true;
1152
+ const eventEmitter = runtime.eventEmitter ?? opts.eventEmitter;
1153
+ const jobId = assertHostJobId(runtime.jobId);
1154
+ const graphTaskId = newGraphRunTaskId();
1155
+ const job = { ...(runtime.job ?? {}), id: jobId, jobId };
1156
+ if (runtime.mode === "backward" && !runtime.goalNodeId) {
1157
+ const err = new Error(`BACKWARD_GOAL_REQUIRED: mode=backward requires goalNodeId`);
1158
+ err.code = "BACKWARD_GOAL_REQUIRED";
1159
+ throw err;
1160
+ }
1161
+ const resolvedGraphIdRaw = graph?.id;
1162
+ if (resolvedGraphIdRaw == null || resolvedGraphIdRaw === '') {
1163
+ throw new Error('GRAPH_ID_REQUIRED: execution request model must include a non-empty id');
1164
+ }
1165
+ const resolvedGraphId = String(resolvedGraphIdRaw);
1166
+ assertCanonicalGraphDocument(graph, { jobId, graphId: resolvedGraphId });
1167
+ let runxClient = opts.runx;
1168
+ if (graphNeedsRunxClient(graph, merged.modelConfig)) {
1169
+ if (!runxClient) {
1170
+ if (!opts.runxCreateOptions) {
1171
+ const err = new Error("RUNX_REQUIRED: graph uses jsConditionFunction, aiCondition, or conditional modelConfig cases that require runx. Pass runx or runxCreateOptions on createExellixGraphRuntime.");
1172
+ err.code = "RUNX_REQUIRED";
1173
+ throw err;
1174
+ }
1175
+ runxClient = await createRunx(opts.runxCreateOptions);
1176
+ await runxClient.bootstrap();
1177
+ await runxClient.reload();
1178
+ }
1179
+ }
1180
+ const graphAudit = {
1181
+ source: 'model',
1182
+ contentSha256: computeGraphDocumentContentSha256(suppliedGraph),
1183
+ };
1184
+ const graphDocumentModel = mergeGraphDocumentModel(graph);
1185
+ const graphExecution = graphDocumentModel.graphExecution;
1186
+ const nodeResponseKeys = resolveNodeResponseKeys(graphExecution);
1187
+ const coreObjectiveConfig = resolveCoreObjectiveConfig(graphExecution);
1188
+ setRuntimeObjectsLastJobId(merged.runtimeObjects, jobId);
1189
+ const { finalizer } = validateGraphFinalizer(graph);
1190
+ const engine = opts.engineFactory.create({
1191
+ graph,
1192
+ mode: runtime.mode,
1193
+ goalNodeId: runtime.goalNodeId,
1194
+ dimension: runtime.dimension,
1195
+ initialState: runtime.initialState,
1196
+ initialVariables: runtime.initialVariables,
1197
+ });
1198
+ const outputsByNodeId = {};
1199
+ const stepsResponses = [];
1200
+ const errors = [];
1201
+ const finalizerNodeId = String(finalizer.id);
1202
+ const jobCorrelation = job?.jobType != null ? { jobType: job.jobType } : undefined;
1203
+ // Pre-compute incoming-edge map for conditional-edge filtering of plan.nextNodes.
1204
+ const rawEdges = Array.isArray(graph.edges) ? graph.edges : [];
1205
+ const hasConditionalEdges = rawEdges.some((e) => e && typeof e === "object" && e.when != null);
1206
+ const incomingByTo = new Map();
1207
+ if (hasConditionalEdges) {
1208
+ for (const e of rawEdges) {
1209
+ if (!e || typeof e !== "object")
1210
+ continue;
1211
+ const to = e.to;
1212
+ if (typeof to !== "string" || to.length === 0)
1213
+ continue;
1214
+ const list = incomingByTo.get(to) ?? [];
1215
+ list.push(e);
1216
+ incomingByTo.set(to, list);
1217
+ }
1218
+ }
1219
+ const graphRunStartedAt = Date.now();
1220
+ const taskRunLogBuffer = [];
1221
+ let logxerCorrelationIdLast;
1222
+ function appendTaskRunLogFromError(e) {
1223
+ if (Array.isArray(e?.taskRunLog))
1224
+ taskRunLogBuffer.push(...e.taskRunLog);
1225
+ if (typeof e?.logxerCorrelationId === "string" && e.logxerCorrelationId.length > 0) {
1226
+ logxerCorrelationIdLast = e.logxerCorrelationId;
1227
+ }
1228
+ }
1229
+ function finalizeGraphPayload(graphStatus) {
1230
+ const lim = resolveRunLogLimits({
1231
+ runLogMode: merged.runLogMode,
1232
+ maxRunLogEntries: merged.maxRunLogEntries,
1233
+ maxRunLogDataJsonChars: merged.maxRunLogDataJsonChars,
1234
+ defaults: {},
1235
+ });
1236
+ const built = buildRunLog({
1237
+ ...lim,
1238
+ jobId,
1239
+ taskId: graphTaskId,
1240
+ graphId: resolvedGraphId,
1241
+ graphRunStartedAt,
1242
+ graphStatus,
1243
+ execution: currentExecution,
1244
+ taskAppendedEntries: taskRunLogBuffer,
1245
+ logxerCorrelationId: logxerCorrelationIdLast,
1246
+ });
1247
+ return {
1248
+ execution: currentExecution,
1249
+ outputsMemory: currentOutputsMemory,
1250
+ ...built,
1251
+ };
1252
+ }
1253
+ // Track current memory and execution state (will be updated as nodes execute)
1254
+ let currentJobMemory = runtime.jobMemory || {};
1255
+ let currentTaskMemory = runtime.taskMemory;
1256
+ let currentExecution = normalizeRuntimeExecutionMemory(runtime);
1257
+ let currentOutputsMemory = normalizeRuntimeOutputsMemory(runtime);
1258
+ if (!currentExecution._trace)
1259
+ currentExecution._trace = { nodes: {} };
1260
+ if (!currentExecution._trace.nodes)
1261
+ currentExecution._trace.nodes = {};
1262
+ const jobKnowledgePatch = await resolveKnowledgePatch({
1263
+ refs: graph.jobKnowledge,
1264
+ resolver: opts.resolveJobKnowledge,
1265
+ context: { model: graph, runtime, graphId: resolvedGraphId, jobId, taskId: graphTaskId },
1266
+ });
1267
+ seedGraphRunExecutionState({
1268
+ execution: currentExecution,
1269
+ jobMemory: currentJobMemory,
1270
+ job: runtime.job,
1271
+ });
1272
+ seedGraphVariableBucketsFromRuntime({
1273
+ execution: currentExecution,
1274
+ graph,
1275
+ runtime,
1276
+ job,
1277
+ });
1278
+ assertFinalizerRequiredReadsResolvable({
1279
+ graph,
1280
+ finalizer,
1281
+ executionMemory: currentExecution,
1282
+ outputsMemory: currentOutputsMemory,
1283
+ });
1284
+ const coreObjectiveValue = resolveCoreObjectiveValue({
1285
+ sourcePath: coreObjectiveConfig.sourcePath,
1286
+ execution: currentExecution,
1287
+ jobMemory: currentJobMemory,
1288
+ taskMemory: currentTaskMemory,
1289
+ job,
1290
+ });
1291
+ if (eventEmitter) {
1292
+ eventEmitter.emit(createGraphStartEvent(jobId, resolvedGraphId, graphTaskId, {
1293
+ agentId: job?.agentId,
1294
+ input: {
1295
+ variables: runtime.variables,
1296
+ jobMemory: currentJobMemory,
1297
+ taskMemory: currentTaskMemory,
1298
+ },
1299
+ ...(jobCorrelation ?? {}),
1300
+ }));
1301
+ }
1302
+ function buildDebugTrace() {
1303
+ if (!debugMode)
1304
+ return undefined;
1305
+ const traceNodes = (currentExecution?._trace?.nodes ?? {});
1306
+ const out = [];
1307
+ for (const [nodeId, entry] of Object.entries(traceNodes)) {
1308
+ if (entry == null || typeof entry !== "object")
1309
+ continue;
1310
+ out.push({ nodeId, ...entry });
1311
+ }
1312
+ // Stable order by startedAt, then by nodeId.
1313
+ out.sort((a, b) => (a.startedAt ?? 0) - (b.startedAt ?? 0) || a.nodeId.localeCompare(b.nodeId));
1314
+ return { nodes: out };
1315
+ }
1316
+ function appendStepResponse(node, output) {
1317
+ if (node?.type === "finalizer")
1318
+ return;
1319
+ const nodeResponse = unwrapNodeResponse(output);
1320
+ stepsResponses.push({
1321
+ [coreObjectiveConfig.propertyName]: coreObjectiveValue,
1322
+ [nodeResponseKeys.singular]: nodeResponse,
1323
+ });
1324
+ }
1325
+ function buildFinalOutputFromGraphResponse() {
1326
+ const executionMemoryForResponse = isPlainRecord(currentExecution)
1327
+ ? structuredClone(currentExecution)
1328
+ : currentExecution;
1329
+ return applyGraphResponseDefinition({
1330
+ response: graph.response,
1331
+ context: {
1332
+ graph,
1333
+ executionMemory: executionMemoryForResponse,
1334
+ outputsMemory: isPlainRecord(currentOutputsMemory)
1335
+ ? structuredClone(currentOutputsMemory)
1336
+ : currentOutputsMemory,
1337
+ outputsByNodeId,
1338
+ stepsResponses,
1339
+ },
1340
+ });
1341
+ }
1342
+ const playgroundReporter = runtime.playgroundReporter ?? opts.playgroundReporter;
1343
+ if (playgroundReporter) {
1344
+ playgroundReporter.step("graph:start", {
1345
+ jobId,
1346
+ taskId: graphTaskId,
1347
+ graphId: resolvedGraphId,
1348
+ executionKeys: Object.keys(currentExecution),
1349
+ variablesKeys: runtime.variables != null ? Object.keys(runtime.variables) : [],
1350
+ executionExcerpt: typeof currentExecution === "object" && currentExecution !== null
1351
+ ? JSON.stringify(currentExecution, null, 2).slice(0, 500)
1352
+ : undefined,
1353
+ ...(merged.playgroundMeta != null && typeof merged.playgroundMeta === "object"
1354
+ ? merged.playgroundMeta
1355
+ : {}),
1356
+ });
1357
+ if (merged.runtimeObjects) {
1358
+ playgroundReporter.step("graph:observability", summarizeRuntimeObjectsForPlayground(merged.runtimeObjects));
1359
+ }
1360
+ }
1361
+ // TRACE: Log initial execution state
1362
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
1363
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: Initial execution state`);
1364
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: runtime.executionMemory =`, JSON.stringify(runtime.executionMemory, null, 2));
1365
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution =`, JSON.stringify(currentExecution, null, 2));
1366
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution type =`, typeof currentExecution);
1367
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution is undefined?`, currentExecution === undefined);
1368
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution is null?`, currentExecution === null);
1369
+ }
1370
+ function recordForStructuredDataFilters() {
1371
+ return isPlainRecord(currentExecution?.input)
1372
+ ? currentExecution.input
1373
+ : {};
1374
+ }
1375
+ const graphEntryForGate = graphDocumentModel.graphEntry;
1376
+ if (graphEntryForGate?.dataFilters !== undefined) {
1377
+ const entryEv = evaluateStructuredDataFilters(graphEntryForGate.dataFilters, recordForStructuredDataFilters());
1378
+ if (entryEv.status === "unsupported_shape" ||
1379
+ (entryEv.status === "evaluated" && !entryEv.ok)) {
1380
+ const err = new Error("GRAPH_ENTRY_DATA_FILTERS_REJECTED: metadata.graphEntry.dataFilters (structured) did not accept runtime.executionMemory.input");
1381
+ err.code = ExellixGraphErrorCode.GRAPH_ENTRY_DATA_FILTERS_REJECTED;
1382
+ const finalOutput = buildFinalOutputFromGraphResponse();
1383
+ const result = {
1384
+ jobId,
1385
+ taskId: graphTaskId,
1386
+ graphId: resolvedGraphId,
1387
+ status: "failed",
1388
+ outputsByNodeId,
1389
+ stepsResponses,
1390
+ engineSnapshot: engine.snapshot(),
1391
+ ...(finalOutput !== undefined ? { finalOutput } : {}),
1392
+ errors: [...errors, { error: err }],
1393
+ graphAudit,
1394
+ ...finalizeGraphPayload("failed"),
1395
+ };
1396
+ const debug = buildDebugTrace();
1397
+ if (debug)
1398
+ result.debug = debug;
1399
+ if (eventEmitter) {
1400
+ eventEmitter.emit(createGraphFailEvent(jobId, resolvedGraphId, graphTaskId, err, {
1401
+ finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
1402
+ ...(jobCorrelation ?? {}),
1403
+ }));
1404
+ }
1405
+ return result;
1406
+ }
1407
+ }
1408
+ while (true) {
1409
+ const plan = engine.plan();
1410
+ if (plan.status === "completed") {
1411
+ const graphStatus = errors.length ? "failed" : "completed";
1412
+ const finalOutput = buildFinalOutputFromGraphResponse();
1413
+ const result = {
1414
+ jobId,
1415
+ taskId: graphTaskId,
1416
+ graphId: resolvedGraphId,
1417
+ status: graphStatus,
1418
+ outputsByNodeId,
1419
+ stepsResponses,
1420
+ engineSnapshot: engine.snapshot(),
1421
+ ...(finalOutput !== undefined ? { finalOutput } : {}),
1422
+ ...(errors.length === 0 ? { finalizerNodeId, finalizerType: finalizer.finalizerType } : {}),
1423
+ errors: errors.length ? errors : undefined,
1424
+ graphAudit,
1425
+ ...finalizeGraphPayload(graphStatus),
1426
+ };
1427
+ const debug = buildDebugTrace();
1428
+ if (debug)
1429
+ result.debug = debug;
1430
+ if (eventEmitter) {
1431
+ if (graphStatus === "completed") {
1432
+ eventEmitter.emit(createGraphCompleteEvent(jobId, resolvedGraphId, graphTaskId, {
1433
+ output: finalOutput,
1434
+ nodesExecuted: Object.keys(outputsByNodeId).length,
1435
+ finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
1436
+ ...(jobCorrelation ?? {}),
1437
+ }));
1438
+ }
1439
+ else {
1440
+ eventEmitter.emit(createGraphFailEvent(jobId, resolvedGraphId, graphTaskId, errors[0]?.error, {
1441
+ finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
1442
+ ...(jobCorrelation ?? {}),
1443
+ }));
1444
+ }
1445
+ }
1446
+ return result;
1447
+ }
1448
+ if (plan.status !== "continue") {
1449
+ const err = new Error(`GRAPH_BLOCKED: status=${plan.status}`);
1450
+ err.code = "GRAPH_BLOCKED";
1451
+ const finalOutput = buildFinalOutputFromGraphResponse();
1452
+ const result = {
1453
+ jobId,
1454
+ taskId: graphTaskId,
1455
+ graphId: resolvedGraphId,
1456
+ status: "failed",
1457
+ outputsByNodeId,
1458
+ stepsResponses,
1459
+ engineSnapshot: engine.snapshot(),
1460
+ ...(finalOutput !== undefined ? { finalOutput } : {}),
1461
+ errors: [...errors, { error: err }],
1462
+ graphAudit,
1463
+ ...finalizeGraphPayload("failed"),
1464
+ };
1465
+ const debug = buildDebugTrace();
1466
+ if (debug)
1467
+ result.debug = debug;
1468
+ if (eventEmitter) {
1469
+ eventEmitter.emit(createGraphFailEvent(jobId, resolvedGraphId, graphTaskId, err, {
1470
+ finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
1471
+ ...(jobCorrelation ?? {}),
1472
+ }));
1473
+ }
1474
+ return result;
1475
+ }
1476
+ let runnableNodes = plan.nextNodes ?? [];
1477
+ // Conditional edge filtering: a node with incoming edges that are *all* conditional must
1478
+ // have at least one incoming edge whose `when` evaluates true to be runnable this round.
1479
+ // Roots and nodes with at least one unconditional incoming edge always pass through.
1480
+ if (hasConditionalEdges && runnableNodes.length > 0) {
1481
+ const planningContextBase = {
1482
+ executionMemory: currentExecution,
1483
+ jobMemory: currentJobMemory,
1484
+ taskMemory: currentTaskMemory,
1485
+ };
1486
+ runnableNodes = runnableNodes.filter((n) => {
1487
+ const id = n?.id;
1488
+ if (typeof id !== "string" || id.length === 0)
1489
+ return true;
1490
+ const incoming = incomingByTo.get(id) ?? [];
1491
+ if (incoming.length === 0)
1492
+ return true;
1493
+ const hasUnconditional = incoming.some((e) => e?.when == null);
1494
+ if (hasUnconditional)
1495
+ return true;
1496
+ const planningContext = buildPredicateEvalContextForNode({
1497
+ executionMemory: currentExecution,
1498
+ jobMemory: currentJobMemory,
1499
+ taskMemory: currentTaskMemory,
1500
+ node: n,
1501
+ runtimeTaskVariables: runtime.taskVariables,
1502
+ });
1503
+ return incoming.some((e) => evaluateGraphPredicate(e.when, planningContext));
1504
+ });
1505
+ }
1506
+ const dataFiltersRecord = recordForStructuredDataFilters();
1507
+ const conditionCtxBase = {
1508
+ executionMemory: currentExecution,
1509
+ jobMemory: currentJobMemory,
1510
+ taskMemory: currentTaskMemory,
1511
+ job,
1512
+ };
1513
+ const skippedByConditions = [];
1514
+ const gatedRunnable = [];
1515
+ for (const n of runnableNodes) {
1516
+ if (n?.type === "finalizer") {
1517
+ gatedRunnable.push(n);
1518
+ continue;
1519
+ }
1520
+ const nodePredicateCtx = buildPredicateEvalContextForNode({
1521
+ executionMemory: currentExecution,
1522
+ jobMemory: currentJobMemory,
1523
+ taskMemory: currentTaskMemory,
1524
+ node: n,
1525
+ runtimeTaskVariables: runtime.taskVariables,
1526
+ });
1527
+ const condEv = await evaluateTaskNodeConditions(n.conditions, { ...conditionCtxBase, ...nodePredicateCtx }, dataFiltersRecord, { runx: runxClient });
1528
+ if (!condEv.ok) {
1529
+ skippedByConditions.push({
1530
+ node: n,
1531
+ skipReason: condEv.skipReason ?? "condition_eval_error",
1532
+ });
1533
+ continue;
1534
+ }
1535
+ gatedRunnable.push(n);
1536
+ }
1537
+ for (const { node, skipReason } of skippedByConditions) {
1538
+ engine.commit({
1539
+ nodeId: String(node.id),
1540
+ output: { ...skippedByConditionsOutput(skipReason) },
1541
+ });
1542
+ }
1543
+ runnableNodes = gatedRunnable;
1544
+ const concurrency = resolveConcurrencyLimit({ graph, runnableNodes });
1545
+ // Execute the current runnable batch in parallel (bounded)
1546
+ // Important: we only call plan() again AFTER all commits from this batch land.
1547
+ let batchHadFailFastError = false;
1548
+ await runPool(runnableNodes, concurrency, async (node) => {
1549
+ // If failFast was triggered, don't schedule more work (runPool also stops scheduling)
1550
+ if (batchHadFailFastError)
1551
+ return;
1552
+ const executionBaseForNode = cloneJsonLike(currentExecution);
1553
+ const executionForNode = cloneJsonLike(executionBaseForNode);
1554
+ const outputsBaseForNode = cloneJsonLike(currentOutputsMemory);
1555
+ const outputsForNode = cloneJsonLike(outputsBaseForNode);
1556
+ // TRACE: Log execution before passing to node
1557
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
1558
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: About to execute node ${node.id}`);
1559
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode =`, JSON.stringify(executionForNode, null, 2));
1560
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode type =`, typeof executionForNode);
1561
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode is undefined?`, executionForNode === undefined);
1562
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode is null?`, executionForNode === null);
1563
+ if (executionForNode && typeof executionForNode === 'object') {
1564
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode keys =`, Object.keys(executionForNode));
1565
+ }
1566
+ }
1567
+ try {
1568
+ const isTaskNodeForKnowledge = node?.type !== "finalizer";
1569
+ const taskKnowledgePatch = await resolveKnowledgePatch({
1570
+ refs: isTaskNodeForKnowledge
1571
+ ? (Array.isArray(node.taskKnowledge) ? node.taskKnowledge : [])
1572
+ : undefined,
1573
+ resolver: opts.resolveTaskKnowledge,
1574
+ context: { model: graph, runtime, graphId: resolvedGraphId, jobId, taskId: graphTaskId, node: node },
1575
+ });
1576
+ const nodeTaskMemory = isPlainRecord(currentTaskMemory) ? { ...currentTaskMemory } : currentTaskMemory;
1577
+ const runtimeNodeConfig = readRuntimeNodeConfig(merged.nodes, typeof node.id === "string" ? node.id : undefined);
1578
+ const effectiveModelConfig = await resolveModelConfigForNode({
1579
+ runtimeNodeConfig,
1580
+ nodeModelConfig: node.taskConfiguration?.modelConfig,
1581
+ runtimeModelConfig: merged.modelConfig,
1582
+ graphModelConfig: graph.modelConfig,
1583
+ runtimeAliasConfig: merged.aliasConfig,
1584
+ conditionCtx: buildPredicateEvalContextForNode({
1585
+ executionMemory: currentExecution,
1586
+ jobMemory: currentJobMemory,
1587
+ taskMemory: currentTaskMemory,
1588
+ node: node,
1589
+ runtimeTaskVariables: runtime.taskVariables,
1590
+ }),
1591
+ executionInput: dataFiltersRecord,
1592
+ runx: runxClient,
1593
+ });
1594
+ const r = await executeNode({
1595
+ graphId: resolvedGraphId,
1596
+ graph,
1597
+ graphRunTaskId: graphTaskId,
1598
+ node,
1599
+ job,
1600
+ jobMemory: currentJobMemory,
1601
+ taskMemory: nodeTaskMemory,
1602
+ jobKnowledgePatch,
1603
+ taskKnowledgePatch,
1604
+ execution: executionForNode,
1605
+ outputsMemory: outputsForNode,
1606
+ variables: runtime.variables,
1607
+ taskVariables: runtime.taskVariables,
1608
+ modelConfig: effectiveModelConfig,
1609
+ llmCall: merged.llmCall,
1610
+ runTaskIdentity: merged.runTaskIdentity,
1611
+ runTaskExecutionMode: merged.runTaskExecutionMode,
1612
+ runTaskDiagnostics: merged.runTaskDiagnostics,
1613
+ graphExecutionPipeline: merged.executionPipeline,
1614
+ skillKeyResolution: merged.skillKeyResolution,
1615
+ nodeTimeoutMs: merged.nodeTimeoutMs,
1616
+ clearSynthesizedContextPerNode: merged.clearSynthesizedContextPerNode,
1617
+ stepRetryPolicy: merged.stepRetryPolicy,
1618
+ mainReadinessPolicy: merged.mainReadinessPolicy,
1619
+ eventEmitter,
1620
+ jobCorrelation,
1621
+ });
1622
+ outputsByNodeId[r.nodeId] = r.output;
1623
+ appendStepResponse(node, r.output);
1624
+ engine.commit({ nodeId: r.nodeId, output: r.output });
1625
+ const trl = r.taskRunLog;
1626
+ if (trl?.length)
1627
+ taskRunLogBuffer.push(...trl);
1628
+ const lcid = r.logxerCorrelationId;
1629
+ if (typeof lcid === "string" && lcid.length > 0)
1630
+ logxerCorrelationIdLast = lcid;
1631
+ // Update execution object from node result if available.
1632
+ // Task nodes write execution state through executionMapping.
1633
+ if (r.execution !== undefined) {
1634
+ // TRACE: Log execution object update
1635
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
1636
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: Node ${node.id} returned execution object`);
1637
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: r.execution =`, JSON.stringify(r.execution, null, 2));
1638
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: r.execution type =`, typeof r.execution);
1639
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: r.execution keys =`, r.execution && typeof r.execution === 'object' ? Object.keys(r.execution) : 'N/A');
1640
+ }
1641
+ currentExecution = mergeExecutionUpdate(currentExecution, r.execution, executionBaseForNode);
1642
+ // TRACE: Log updated currentExecution
1643
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
1644
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: Updated currentExecution =`, JSON.stringify(currentExecution, null, 2));
1645
+ }
1646
+ }
1647
+ else {
1648
+ // TRACE: Log when execution is not returned
1649
+ if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
1650
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: Node ${node.id} did NOT return execution object`);
1651
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: r.execution =`, r.execution);
1652
+ console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution remains =`, JSON.stringify(currentExecution, null, 2));
1653
+ }
1654
+ }
1655
+ if (r.outputsMemory !== undefined) {
1656
+ currentOutputsMemory = mergeExecutionUpdate(currentOutputsMemory, r.outputsMemory, outputsBaseForNode);
1657
+ }
1658
+ // Note: We don't modify jobMemory or taskMemory here
1659
+ // They are set before the job starts and we just pass them through
1660
+ }
1661
+ catch (e) {
1662
+ appendTaskRunLogFromError(e);
1663
+ errors.push({ nodeId: node?.id, error: e });
1664
+ engine.commit({ nodeId: node?.id, error: e });
1665
+ if (eventEmitter && !wasNodeLifecycleEmitted(e)) {
1666
+ eventEmitter.emit(createNodeFailEvent(jobId, resolvedGraphId, graphTaskId, node, String(e?.skillKey ?? ""), {
1667
+ error: { code: e?.code, message: e?.message, stack: e?.stack },
1668
+ memoryAfter: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
1669
+ response: undefined,
1670
+ }, jobCorrelation));
1671
+ }
1672
+ if (failFast) {
1673
+ batchHadFailFastError = true;
1674
+ // Throw to stop scheduling new nodes in runPool
1675
+ throw e;
1676
+ }
1677
+ }
1678
+ }, { failFast }).catch((e) => {
1679
+ // failFast path: we intentionally end early after stopping scheduling
1680
+ // (in-flight nodes may still finish; JS won't cancel them).
1681
+ if (!failFast)
1682
+ throw e;
1683
+ });
1684
+ if (failFast && errors.length) {
1685
+ const finalOutput = buildFinalOutputFromGraphResponse();
1686
+ const result = {
1687
+ jobId,
1688
+ taskId: graphTaskId,
1689
+ graphId: resolvedGraphId,
1690
+ status: "failed",
1691
+ outputsByNodeId,
1692
+ stepsResponses,
1693
+ engineSnapshot: engine.snapshot(),
1694
+ ...(finalOutput !== undefined ? { finalOutput } : {}),
1695
+ errors,
1696
+ graphAudit,
1697
+ ...finalizeGraphPayload("failed"),
1698
+ };
1699
+ const debug = buildDebugTrace();
1700
+ if (debug)
1701
+ result.debug = debug;
1702
+ if (eventEmitter) {
1703
+ eventEmitter.emit(createGraphFailEvent(jobId, resolvedGraphId, graphTaskId, errors[0]?.error, {
1704
+ finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
1705
+ ...(jobCorrelation ?? {}),
1706
+ }));
1707
+ }
1708
+ return result;
1709
+ }
1710
+ }
1711
+ }
1712
+ return {
1713
+ executeGraph,
1714
+ executeNode, // exported for direct single-node test if you want
1715
+ };
1716
+ }