callman-core 1.25.1 → 1.27.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.
- package/dist/host/db/mysqlAdapter.d.ts.map +1 -1
- package/dist/host/db/mysqlAdapter.js +39 -9
- package/dist/host/db/mysqlAdapter.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/request/executeRequest.d.ts +1 -1
- package/dist/request/executeRequest.d.ts.map +1 -1
- package/dist/request/executeRequest.js +8 -1
- package/dist/request/executeRequest.js.map +1 -1
- package/dist/scenario-runner/adapters/request-executor.d.ts.map +1 -1
- package/dist/scenario-runner/adapters/request-executor.js +53 -3
- package/dist/scenario-runner/adapters/request-executor.js.map +1 -1
- package/dist/scenario-runner/adapters/script-executor.js +1 -1
- package/dist/scenario-runner/adapters/script-executor.js.map +1 -1
- package/dist/scenario-runner/adapters/types.d.ts +1 -0
- package/dist/scenario-runner/adapters/types.d.ts.map +1 -1
- package/dist/scenario-runner/boundSnapshot.d.ts +15 -0
- package/dist/scenario-runner/boundSnapshot.d.ts.map +1 -0
- package/dist/scenario-runner/boundSnapshot.js +67 -0
- package/dist/scenario-runner/boundSnapshot.js.map +1 -0
- package/dist/scenario-runner/runScenario.d.ts +1 -1
- package/dist/scenario-runner/runScenario.d.ts.map +1 -1
- package/dist/scenario-runner/runScenario.js +146 -25
- package/dist/scenario-runner/runScenario.js.map +1 -1
- package/dist/scenario-runner/runScenarioWithDataset.d.ts.map +1 -1
- package/dist/scenario-runner/runScenarioWithDataset.js +21 -6
- package/dist/scenario-runner/runScenarioWithDataset.js.map +1 -1
- package/dist/scenario-runner/types.d.ts +19 -0
- package/dist/scenario-runner/types.d.ts.map +1 -1
- package/dist/script/isolated-runner.d.ts +7 -1
- package/dist/script/isolated-runner.d.ts.map +1 -1
- package/dist/script/isolated-runner.js +11 -5
- package/dist/script/isolated-runner.js.map +1 -1
- package/dist/script/worker-entry.js +62 -28
- package/dist/script/worker-entry.js.map +1 -1
- package/dist/script/worker-pool.d.ts +60 -0
- package/dist/script/worker-pool.d.ts.map +1 -0
- package/dist/script/worker-pool.js +274 -0
- package/dist/script/worker-pool.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist-cjs/host/db/mysqlAdapter.js +39 -9
- package/dist-cjs/host/db/mysqlAdapter.js.map +1 -1
- package/dist-cjs/index.js +4 -1
- package/dist-cjs/index.js.map +1 -1
- package/dist-cjs/request/executeRequest.js +8 -1
- package/dist-cjs/request/executeRequest.js.map +1 -1
- package/dist-cjs/scenario-runner/adapters/request-executor.js +53 -3
- package/dist-cjs/scenario-runner/adapters/request-executor.js.map +1 -1
- package/dist-cjs/scenario-runner/adapters/script-executor.js +1 -1
- package/dist-cjs/scenario-runner/adapters/script-executor.js.map +1 -1
- package/dist-cjs/scenario-runner/boundSnapshot.js +71 -0
- package/dist-cjs/scenario-runner/boundSnapshot.js.map +1 -0
- package/dist-cjs/scenario-runner/runScenario.js +146 -25
- package/dist-cjs/scenario-runner/runScenario.js.map +1 -1
- package/dist-cjs/scenario-runner/runScenarioWithDataset.js +21 -6
- package/dist-cjs/scenario-runner/runScenarioWithDataset.js.map +1 -1
- package/dist-cjs/script/isolated-runner.js +12 -5
- package/dist-cjs/script/isolated-runner.js.map +1 -1
- package/dist-cjs/script/worker-entry.js +62 -28
- package/dist-cjs/script/worker-entry.js.map +1 -1
- package/dist-cjs/script/worker-pool.js +312 -0
- package/dist-cjs/script/worker-pool.js.map +1 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import { evaluateAssertionConfig, validateAssertionConfig, } from "./assertion.j
|
|
|
4
4
|
import { createSubScenarioStepId, buildRuntimeStepDescriptors, DEFAULT_END_LOOP_DELAY_MS, DEFAULT_END_LOOP_ITERATIONS, flattenScenarioNodesForContext, MAX_END_LOOP_ITERATIONS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POLL_MAX_ATTEMPTS, MAX_POLL_ATTEMPTS, DEFAULT_FOREACH_MAX_ITERATIONS, MAX_FOREACH_ITERATIONS, DEFAULT_WS_CONNECT_TIMEOUT_MS, MAX_WS_CONNECT_TIMEOUT_MS, DEFAULT_WS_EXPECT_TIMEOUT_MS, MAX_WS_EXPECT_TIMEOUT_MS, MAX_WS_WAIT_DURATION_MS, MAX_WS_STEPS, DEFAULT_WS_CLOSE_CODE, } from "./graphTraversal.js";
|
|
5
5
|
import { evaluateWebsocketExpect } from "./websocket.js";
|
|
6
6
|
import { executeWithScenarioNodePolicy } from "./executionPolicyRunner.js";
|
|
7
|
+
import { boundSnapshot } from "./boundSnapshot.js";
|
|
7
8
|
import { getLegacyScenarioNodeExecutionPolicy, getScenarioNodeExecutionPolicy, supportsScenarioNodeExecutionPolicy, } from "./nodePolicy.js";
|
|
8
9
|
import { buildScenarioExecutionGraph, buildScenarioTemplateValues, collectExclusiveBranchNodeIds, evaluateScenarioExpression, resolveScenarioJsonTemplateString, resolveScenarioTemplateString, } from "./templateResolver.js";
|
|
9
10
|
import { executeWaitNode, ScenarioWaitNodeCancelledError, ScenarioWaitNodeValidationError, } from "./waitNodeExecutor.js";
|
|
@@ -110,19 +111,24 @@ const createInputSnapshot = (context) => ({
|
|
|
110
111
|
result: context.redisResult ?? null,
|
|
111
112
|
},
|
|
112
113
|
});
|
|
113
|
-
const createContextSnapshot = (context) =>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
114
|
+
const createContextSnapshot = (context, mode = "full") => {
|
|
115
|
+
// "bounded": the payload-bearing slots are size-capped copies instead of
|
|
116
|
+
// references, so the event log never pins a node's full response body.
|
|
117
|
+
const bound = mode === "bounded" ? boundSnapshot : (value) => value;
|
|
118
|
+
return {
|
|
119
|
+
environment: { ...context.environment },
|
|
120
|
+
globals: { ...context.globals },
|
|
121
|
+
workflowContext: bound({ ...context.workflowContext }),
|
|
122
|
+
nodeMetaByLabel: cloneNodeMetaByLabel(context.nodeMetaByLabel),
|
|
123
|
+
workflowMeta: cloneWorkflowExecutionMeta(context.workflowMeta),
|
|
124
|
+
responseRoot: context.responseRoot ? bound({ ...context.responseRoot }) : null,
|
|
125
|
+
lastResponse: bound(context.lastResponse),
|
|
126
|
+
dbResult: bound(context.dbResult),
|
|
127
|
+
kafkaEvent: bound(context.kafkaEvent),
|
|
128
|
+
kafkaCapture: bound(context.kafkaCapture),
|
|
129
|
+
redisResult: bound(context.redisResult),
|
|
130
|
+
};
|
|
131
|
+
};
|
|
126
132
|
const buildKafkaEventValue = (message) => {
|
|
127
133
|
const fallbackValue = {
|
|
128
134
|
value: message.valueJson ?? message.valueText,
|
|
@@ -333,6 +339,7 @@ const createStepExecutionRecord = ({ stepId, stepType, title, }) => ({
|
|
|
333
339
|
activeBranch: null,
|
|
334
340
|
ai: null,
|
|
335
341
|
inputData: null,
|
|
342
|
+
resolvedInput: null,
|
|
336
343
|
outputData: null,
|
|
337
344
|
incomingEdgeId: null,
|
|
338
345
|
startedAt: null,
|
|
@@ -512,6 +519,7 @@ const updateRecordFromEvent = (records, event) => {
|
|
|
512
519
|
activeBranch: event.activeBranch ?? current.activeBranch,
|
|
513
520
|
ai: event.ai ?? current.ai,
|
|
514
521
|
inputData: event.inputData ?? current.inputData,
|
|
522
|
+
resolvedInput: event.resolvedInput ?? current.resolvedInput ?? null,
|
|
515
523
|
outputData: event.outputData ?? current.outputData,
|
|
516
524
|
incomingEdgeId: event.incomingEdgeId ?? current.incomingEdgeId,
|
|
517
525
|
startedAt: current.startedAt ?? event.at,
|
|
@@ -557,7 +565,7 @@ const callHandler = async (handlers, event) => {
|
|
|
557
565
|
return;
|
|
558
566
|
}
|
|
559
567
|
};
|
|
560
|
-
export const runScenario = async ({ scenario, environment = {}, globals = {}, runtimeAdapters, eventHandlers, signal, minNodeVisualStateMs = DEFAULT_MIN_NODE_VISUAL_STATE_MS, }) => {
|
|
568
|
+
export const runScenario = async ({ scenario, environment = {}, globals = {}, runtimeAdapters, eventHandlers, signal, minNodeVisualStateMs = DEFAULT_MIN_NODE_VISUAL_STATE_MS, eventContextMode = "full", }) => {
|
|
561
569
|
const runId = createRunId();
|
|
562
570
|
const startedAtMs = Date.now();
|
|
563
571
|
const startedAt = new Date(startedAtMs).toISOString();
|
|
@@ -568,6 +576,9 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
568
576
|
const localStatuses = new Map(runtimeStepIds.map((stepId) => [stepId, "idle"]));
|
|
569
577
|
const records = new Map();
|
|
570
578
|
const events = [];
|
|
579
|
+
// Every emitted event carries a context snapshot; `runtimeContext` is
|
|
580
|
+
// assigned just below and is always initialised before the first emit.
|
|
581
|
+
const snapshotContext = () => createContextSnapshot(runtimeContext, eventContextMode);
|
|
571
582
|
const initialWorkflowMeta = createInitialWorkflowMeta({
|
|
572
583
|
startedAt,
|
|
573
584
|
totalNodes: runtimeStepIds.length,
|
|
@@ -862,6 +873,11 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
862
873
|
}
|
|
863
874
|
const executionStartedAtMs = Date.now();
|
|
864
875
|
let inputData = createInputSnapshot(runtimeContext);
|
|
876
|
+
// The node's OWN resolved input (query + params, topic + payload, the
|
|
877
|
+
// request as sent…) — set by each node branch below once the values it
|
|
878
|
+
// will act on are known, so a failing node still reports what it tried.
|
|
879
|
+
let resolvedInput = null;
|
|
880
|
+
const resolvedInputForEvent = () => eventContextMode === "bounded" ? boundSnapshot(resolvedInput) : resolvedInput;
|
|
865
881
|
let statusCode = null;
|
|
866
882
|
let statusExpectation = null;
|
|
867
883
|
let response = null;
|
|
@@ -892,6 +908,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
892
908
|
statusCode = null;
|
|
893
909
|
response = null;
|
|
894
910
|
outputData = null;
|
|
911
|
+
resolvedInput = null;
|
|
895
912
|
testResults = [];
|
|
896
913
|
contractResults = [];
|
|
897
914
|
contractSummary = null;
|
|
@@ -977,7 +994,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
977
994
|
runId,
|
|
978
995
|
stepId,
|
|
979
996
|
node,
|
|
980
|
-
context:
|
|
997
|
+
context: snapshotContext(),
|
|
981
998
|
kafkaCapturePlans,
|
|
982
999
|
captureFromTimestampMs: startedAtMs,
|
|
983
1000
|
signal,
|
|
@@ -993,6 +1010,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
993
1010
|
statusCode = requestResult.response.status;
|
|
994
1011
|
statusExpectation = requestResult.statusExpectation ?? null;
|
|
995
1012
|
outputData = requestResult.outputData;
|
|
1013
|
+
resolvedInput = requestResult.resolvedInput ?? null;
|
|
996
1014
|
testResults = requestResult.testResults;
|
|
997
1015
|
contractResults = requestResult.contractResults;
|
|
998
1016
|
contractSummary = requestResult.contractSummary;
|
|
@@ -1024,6 +1042,12 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1024
1042
|
return resolved;
|
|
1025
1043
|
})
|
|
1026
1044
|
: (node.data.config.params ?? []).map((param) => resolveScenarioTemplateString(param, templateValues));
|
|
1045
|
+
resolvedInput = {
|
|
1046
|
+
connectionId: node.data.config.connectionId,
|
|
1047
|
+
query: resolvedQuery,
|
|
1048
|
+
params: resolvedParams,
|
|
1049
|
+
useSession: Boolean(node.data.config.useSession),
|
|
1050
|
+
};
|
|
1027
1051
|
const dbResult = await runtimeAdapters.dbExecutor({
|
|
1028
1052
|
runId,
|
|
1029
1053
|
stepId,
|
|
@@ -1038,7 +1062,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1038
1062
|
},
|
|
1039
1063
|
}
|
|
1040
1064
|
: {}),
|
|
1041
|
-
context:
|
|
1065
|
+
context: snapshotContext(),
|
|
1042
1066
|
signal,
|
|
1043
1067
|
});
|
|
1044
1068
|
runtimeContext.dbResult = dbResult.dbResult;
|
|
@@ -1048,6 +1072,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1048
1072
|
}
|
|
1049
1073
|
}
|
|
1050
1074
|
if (node.type === "wait") {
|
|
1075
|
+
resolvedInput = { durationMs: node.data.config.durationMs };
|
|
1051
1076
|
try {
|
|
1052
1077
|
outputData = await executeWaitNode({
|
|
1053
1078
|
durationMs: node.data.config.durationMs,
|
|
@@ -1071,7 +1096,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1071
1096
|
runId,
|
|
1072
1097
|
stepId,
|
|
1073
1098
|
node,
|
|
1074
|
-
context:
|
|
1099
|
+
context: snapshotContext(),
|
|
1075
1100
|
signal,
|
|
1076
1101
|
});
|
|
1077
1102
|
scriptSource = scriptResult.scriptSource;
|
|
@@ -1090,12 +1115,16 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1090
1115
|
const resolvedCommands = node.data.config.commands
|
|
1091
1116
|
.map((command) => resolveScenarioTemplateString(command, templateValues))
|
|
1092
1117
|
.filter((command) => command.trim().length > 0);
|
|
1118
|
+
resolvedInput = {
|
|
1119
|
+
connectionId: node.data.config.connectionId,
|
|
1120
|
+
commands: resolvedCommands,
|
|
1121
|
+
};
|
|
1093
1122
|
const redisResult = await runtimeAdapters.redisExecutor({
|
|
1094
1123
|
runId,
|
|
1095
1124
|
stepId,
|
|
1096
1125
|
node,
|
|
1097
1126
|
resolvedCommands,
|
|
1098
|
-
context:
|
|
1127
|
+
context: snapshotContext(),
|
|
1099
1128
|
signal,
|
|
1100
1129
|
});
|
|
1101
1130
|
runtimeContext.redisResult = redisResult.redisResult;
|
|
@@ -1118,6 +1147,13 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1118
1147
|
: null;
|
|
1119
1148
|
// Thinned emission keeps stored events bounded (~100 tick events).
|
|
1120
1149
|
const pollEmitStride = Math.max(1, Math.ceil(pollMaxAttempts / 100));
|
|
1150
|
+
resolvedInput = {
|
|
1151
|
+
actionType: pollConfig.actionType,
|
|
1152
|
+
intervalMs: pollBaseIntervalMs,
|
|
1153
|
+
timeoutMs: pollTimeoutMs,
|
|
1154
|
+
maxAttempts: pollMaxAttempts,
|
|
1155
|
+
onTimeout: pollConfig.onTimeout ?? "fail",
|
|
1156
|
+
};
|
|
1121
1157
|
// One tick = the embedded action through the SAME host adapters real
|
|
1122
1158
|
// nodes use (desktop IPC / backend drivers both covered), publishing
|
|
1123
1159
|
// results into the runtime context exactly like the real node would,
|
|
@@ -1147,7 +1183,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1147
1183
|
position: node.position,
|
|
1148
1184
|
data: { label: node.data.label, config: pollConfig.request },
|
|
1149
1185
|
},
|
|
1150
|
-
context:
|
|
1186
|
+
context: snapshotContext(),
|
|
1151
1187
|
kafkaCapturePlans: [],
|
|
1152
1188
|
captureFromTimestampMs: startedAtMs,
|
|
1153
1189
|
signal,
|
|
@@ -1187,7 +1223,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1187
1223
|
},
|
|
1188
1224
|
resolvedQuery,
|
|
1189
1225
|
resolvedParams,
|
|
1190
|
-
context:
|
|
1226
|
+
context: snapshotContext(),
|
|
1191
1227
|
signal,
|
|
1192
1228
|
});
|
|
1193
1229
|
runtimeContext.dbResult = tickResult.dbResult;
|
|
@@ -1210,7 +1246,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1210
1246
|
data: { label: node.data.label, config: pollConfig.redis },
|
|
1211
1247
|
},
|
|
1212
1248
|
resolvedCommands,
|
|
1213
|
-
context:
|
|
1249
|
+
context: snapshotContext(),
|
|
1214
1250
|
signal,
|
|
1215
1251
|
});
|
|
1216
1252
|
runtimeContext.redisResult = tickResult.redisResult;
|
|
@@ -1228,7 +1264,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1228
1264
|
position: node.position,
|
|
1229
1265
|
data: { label: node.data.label, config: pollConfig.script },
|
|
1230
1266
|
},
|
|
1231
|
-
context:
|
|
1267
|
+
context: snapshotContext(),
|
|
1232
1268
|
signal,
|
|
1233
1269
|
});
|
|
1234
1270
|
scriptSource = tickResult.scriptSource;
|
|
@@ -1347,11 +1383,17 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1347
1383
|
workflowMeta: runtimeContext.workflowMeta,
|
|
1348
1384
|
useRichBlocks: node.data.config.advanced?.useRichBlocks !== false,
|
|
1349
1385
|
});
|
|
1386
|
+
resolvedInput = {
|
|
1387
|
+
provider: node.data.config.provider,
|
|
1388
|
+
severity: normalizeNotificationSeverity(node.data.config.severity),
|
|
1389
|
+
channel: resolvedChannelOverride,
|
|
1390
|
+
message: resolvedMessage || "N/A",
|
|
1391
|
+
};
|
|
1350
1392
|
const notificationResult = await runtimeAdapters.notificationExecutor({
|
|
1351
1393
|
runId,
|
|
1352
1394
|
stepId,
|
|
1353
1395
|
node,
|
|
1354
|
-
context:
|
|
1396
|
+
context: snapshotContext(),
|
|
1355
1397
|
deliveryRequest,
|
|
1356
1398
|
signal,
|
|
1357
1399
|
});
|
|
@@ -1374,6 +1416,13 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1374
1416
|
: "";
|
|
1375
1417
|
const resolvedKey = resolvedKeyRaw.length > 0 ? resolvedKeyRaw : null;
|
|
1376
1418
|
const resolvedValue = resolveScenarioTemplateString(node.data.config.messageValue ?? "", templateValues);
|
|
1419
|
+
resolvedInput = {
|
|
1420
|
+
mode: "publish",
|
|
1421
|
+
topic: resolvedTopic,
|
|
1422
|
+
key: resolvedKey,
|
|
1423
|
+
valueFormat,
|
|
1424
|
+
value: resolvedValue,
|
|
1425
|
+
};
|
|
1377
1426
|
if (valueFormat === "json" && resolvedValue.trim().length > 0) {
|
|
1378
1427
|
try {
|
|
1379
1428
|
JSON.parse(resolvedValue);
|
|
@@ -1395,6 +1444,14 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1395
1444
|
continue;
|
|
1396
1445
|
resolvedHeaders[headerKey] = resolveScenarioTemplateString(header.value, templateValues);
|
|
1397
1446
|
}
|
|
1447
|
+
resolvedInput = {
|
|
1448
|
+
mode: "publish",
|
|
1449
|
+
topic: resolvedTopic,
|
|
1450
|
+
key: resolvedKey,
|
|
1451
|
+
valueFormat,
|
|
1452
|
+
value: resolvedValue,
|
|
1453
|
+
headers: resolvedHeaders,
|
|
1454
|
+
};
|
|
1398
1455
|
const { publishResult } = await runtimeAdapters.kafkaExecutor({
|
|
1399
1456
|
runId,
|
|
1400
1457
|
stepId,
|
|
@@ -1420,6 +1477,12 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1420
1477
|
else {
|
|
1421
1478
|
const rules = node.data.config.rules?.filter((rule) => rule.trim()) ?? [];
|
|
1422
1479
|
const resolvedMatchMode = node.data.config.matchMode ?? "regex";
|
|
1480
|
+
resolvedInput = {
|
|
1481
|
+
mode: "consume",
|
|
1482
|
+
topic: resolvedTopic,
|
|
1483
|
+
matchMode: resolvedMatchMode,
|
|
1484
|
+
rules,
|
|
1485
|
+
};
|
|
1423
1486
|
const prefetchedCaptureResult = prefetchedKafkaResults.get(stepId);
|
|
1424
1487
|
const captureResult = prefetchedCaptureResult ??
|
|
1425
1488
|
(await runtimeAdapters.kafkaExecutor({
|
|
@@ -1527,6 +1590,13 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1527
1590
|
rules: step.rules,
|
|
1528
1591
|
};
|
|
1529
1592
|
});
|
|
1593
|
+
resolvedInput = {
|
|
1594
|
+
url: resolvedUrl,
|
|
1595
|
+
headers: resolvedHeaders,
|
|
1596
|
+
subprotocols: resolvedSubprotocols,
|
|
1597
|
+
connectTimeoutMs,
|
|
1598
|
+
steps: resolvedSteps,
|
|
1599
|
+
};
|
|
1530
1600
|
const wsExecution = await runtimeAdapters.websocketExecutor({
|
|
1531
1601
|
runId,
|
|
1532
1602
|
stepId,
|
|
@@ -1647,12 +1717,19 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1647
1717
|
runId,
|
|
1648
1718
|
stepId,
|
|
1649
1719
|
node,
|
|
1650
|
-
context:
|
|
1720
|
+
context: snapshotContext(),
|
|
1651
1721
|
signal,
|
|
1652
1722
|
});
|
|
1653
1723
|
throwIfStopped(signal);
|
|
1654
1724
|
aiDetails = aiResult.details;
|
|
1655
1725
|
outputData = aiResult.outputData;
|
|
1726
|
+
resolvedInput = aiDetails
|
|
1727
|
+
? {
|
|
1728
|
+
provider: aiDetails.provider,
|
|
1729
|
+
model: aiDetails.model,
|
|
1730
|
+
prompt: aiDetails.resolvedPrompt,
|
|
1731
|
+
}
|
|
1732
|
+
: null;
|
|
1656
1733
|
if (aiResult.failureMessage) {
|
|
1657
1734
|
throw new ScenarioNodeFailureError(aiResult.failureMessage);
|
|
1658
1735
|
}
|
|
@@ -1681,6 +1758,11 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1681
1758
|
conditionMode = conditionPayload.mode;
|
|
1682
1759
|
conditionDebug = conditionPayload.debug;
|
|
1683
1760
|
outputData = conditionPayload;
|
|
1761
|
+
resolvedInput = {
|
|
1762
|
+
mode: conditionPayload.mode,
|
|
1763
|
+
expression: conditionPayload.expression,
|
|
1764
|
+
preview: conditionPayload.preview,
|
|
1765
|
+
};
|
|
1684
1766
|
await emit({
|
|
1685
1767
|
type: "step:condition",
|
|
1686
1768
|
runId,
|
|
@@ -1721,6 +1803,13 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1721
1803
|
});
|
|
1722
1804
|
assertionResults = assertionEvaluation.checks;
|
|
1723
1805
|
assertionSummary = assertionEvaluation;
|
|
1806
|
+
resolvedInput = {
|
|
1807
|
+
matchMode: assertionEvaluation.matchMode,
|
|
1808
|
+
checks: assertionEvaluation.checks.map((check) => ({
|
|
1809
|
+
name: check.name,
|
|
1810
|
+
enabled: check.enabled,
|
|
1811
|
+
})),
|
|
1812
|
+
};
|
|
1724
1813
|
// Downstream nodes can reference e.g. {{AssertNode1.passed}}.
|
|
1725
1814
|
outputData = {
|
|
1726
1815
|
passed: assertionEvaluation.passed,
|
|
@@ -1825,6 +1914,19 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
1825
1914
|
const collectResults = foreachConfig.collectResults !== false;
|
|
1826
1915
|
// Thinned emission keeps stored events bounded (~100 iteration markers).
|
|
1827
1916
|
const iterationEmitStride = Math.max(1, Math.ceil(processLimit / 100));
|
|
1917
|
+
resolvedInput = {
|
|
1918
|
+
sourceMode: foreachConfig.sourceMode,
|
|
1919
|
+
itemsPath: foreachConfig.sourceMode === "inline"
|
|
1920
|
+
? null
|
|
1921
|
+
: (foreachConfig.itemsPath ?? "").trim(),
|
|
1922
|
+
itemAlias: itemAlias || null,
|
|
1923
|
+
itemCount: items.length,
|
|
1924
|
+
maxIterations,
|
|
1925
|
+
delayMs: iterationDelayMs,
|
|
1926
|
+
onItemFailure,
|
|
1927
|
+
collectResults,
|
|
1928
|
+
items,
|
|
1929
|
+
};
|
|
1828
1930
|
const internalGraph = buildScenarioExecutionGraph(foreachConfig.nodes, foreachConfig.edges);
|
|
1829
1931
|
const internalScope = {
|
|
1830
1932
|
graph: internalGraph,
|
|
@@ -2008,6 +2110,21 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
2008
2110
|
const writtenEnvironment = {};
|
|
2009
2111
|
const writtenGlobals = {};
|
|
2010
2112
|
const extractionOutcomes = [];
|
|
2113
|
+
resolvedInput = {
|
|
2114
|
+
assignments: (node.data.config.assignments ?? [])
|
|
2115
|
+
.filter((assignment) => assignment && assignment.enabled !== false)
|
|
2116
|
+
.map((assignment) => ({
|
|
2117
|
+
scope: assignment.scope === "global" ? "global" : "environment",
|
|
2118
|
+
key: assignment.key ?? "",
|
|
2119
|
+
...(assignment.extractorType && assignment.extractorType !== "template"
|
|
2120
|
+
? {
|
|
2121
|
+
extractorType: assignment.extractorType,
|
|
2122
|
+
source: assignment.source ?? "",
|
|
2123
|
+
expression: assignment.expression ?? "",
|
|
2124
|
+
}
|
|
2125
|
+
: { value: assignment.value ?? "" }),
|
|
2126
|
+
})),
|
|
2127
|
+
};
|
|
2011
2128
|
for (const assignment of node.data.config.assignments ?? []) {
|
|
2012
2129
|
if (!assignment || assignment.enabled === false) {
|
|
2013
2130
|
continue;
|
|
@@ -2093,6 +2210,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
2093
2210
|
}
|
|
2094
2211
|
if (node.type === "aggregator") {
|
|
2095
2212
|
const resolvedTemplate = resolveScenarioJsonTemplateString(node.data.config.template, templateValues);
|
|
2213
|
+
resolvedInput = { template: resolvedTemplate };
|
|
2096
2214
|
try {
|
|
2097
2215
|
outputData = JSON.parse(resolvedTemplate);
|
|
2098
2216
|
}
|
|
@@ -2238,6 +2356,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
2238
2356
|
activeBranch,
|
|
2239
2357
|
ai: aiDetails,
|
|
2240
2358
|
inputData,
|
|
2359
|
+
resolvedInput: resolvedInputForEvent(),
|
|
2241
2360
|
outputData,
|
|
2242
2361
|
incomingEdgeId: incomingEdgeId ?? null,
|
|
2243
2362
|
});
|
|
@@ -2321,6 +2440,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
2321
2440
|
activeBranch,
|
|
2322
2441
|
ai: aiDetails,
|
|
2323
2442
|
inputData,
|
|
2443
|
+
resolvedInput: resolvedInputForEvent(),
|
|
2324
2444
|
outputData,
|
|
2325
2445
|
incomingEdgeId: incomingEdgeId ?? null,
|
|
2326
2446
|
});
|
|
@@ -2399,6 +2519,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
2399
2519
|
activeBranch,
|
|
2400
2520
|
ai: aiDetails,
|
|
2401
2521
|
inputData,
|
|
2522
|
+
resolvedInput: resolvedInputForEvent(),
|
|
2402
2523
|
outputData,
|
|
2403
2524
|
incomingEdgeId: incomingEdgeId ?? null,
|
|
2404
2525
|
});
|
|
@@ -2639,7 +2760,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
|
|
|
2639
2760
|
durationMs: Date.now() - startedAtMs,
|
|
2640
2761
|
totalSteps: runtimeStepIds.length,
|
|
2641
2762
|
completedSteps: Array.from(localStatuses.values()).filter(isTerminalStatus).length,
|
|
2642
|
-
context:
|
|
2763
|
+
context: snapshotContext(),
|
|
2643
2764
|
workflowMeta: cloneWorkflowExecutionMeta(finalWorkflowMeta),
|
|
2644
2765
|
nodeExecutions: runtimeStepIds.map((stepId) => {
|
|
2645
2766
|
const descriptor = runtimeStepDescriptors.find((entry) => entry.stepId === stepId);
|