callman-core 1.31.0 → 1.33.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 (126) hide show
  1. package/ai-packs/db-queries.md +1 -1
  2. package/ai-packs/scenario-nodes.md +89 -15
  3. package/dist/ai/prompts/scenario.d.ts +1 -1
  4. package/dist/ai/prompts/scenario.d.ts.map +1 -1
  5. package/dist/ai/prompts/scenario.js +6 -3
  6. package/dist/ai/prompts/scenario.js.map +1 -1
  7. package/dist/ai/prompts/scenarioNodeDefaults.d.ts.map +1 -1
  8. package/dist/ai/prompts/scenarioNodeDefaults.js +9 -0
  9. package/dist/ai/prompts/scenarioNodeDefaults.js.map +1 -1
  10. package/dist/ai/prompts/scenarioNodeReference.d.ts.map +1 -1
  11. package/dist/ai/prompts/scenarioNodeReference.js +136 -17
  12. package/dist/ai/prompts/scenarioNodeReference.js.map +1 -1
  13. package/dist/host/db/db-query-reference.js +1 -1
  14. package/dist/host/db/db-query-reference.js.map +1 -1
  15. package/dist/host/db/mongoAdapter.d.ts +1 -0
  16. package/dist/host/db/mongoAdapter.d.ts.map +1 -1
  17. package/dist/host/db/mongoAdapter.js +21 -0
  18. package/dist/host/db/mongoAdapter.js.map +1 -1
  19. package/dist/host/db/queryExecutor.js +2 -2
  20. package/dist/host/db/queryExecutor.js.map +1 -1
  21. package/dist/host/index.d.ts +3 -1
  22. package/dist/host/index.d.ts.map +1 -1
  23. package/dist/host/index.js +4 -1
  24. package/dist/host/index.js.map +1 -1
  25. package/dist/host/temporal/temporalService.d.ts +67 -0
  26. package/dist/host/temporal/temporalService.d.ts.map +1 -0
  27. package/dist/host/temporal/temporalService.js +1182 -0
  28. package/dist/host/temporal/temporalService.js.map +1 -0
  29. package/dist/host/temporal/types.d.ts +13 -0
  30. package/dist/host/temporal/types.d.ts.map +1 -0
  31. package/dist/host/temporal/types.js +5 -0
  32. package/dist/host/temporal/types.js.map +1 -0
  33. package/dist/scenario-runner/adapters/connection-config.d.ts +17 -1
  34. package/dist/scenario-runner/adapters/connection-config.d.ts.map +1 -1
  35. package/dist/scenario-runner/adapters/index.d.ts +4 -2
  36. package/dist/scenario-runner/adapters/index.d.ts.map +1 -1
  37. package/dist/scenario-runner/adapters/index.js +8 -0
  38. package/dist/scenario-runner/adapters/index.js.map +1 -1
  39. package/dist/scenario-runner/adapters/kafka-executor.d.ts +5 -0
  40. package/dist/scenario-runner/adapters/kafka-executor.d.ts.map +1 -1
  41. package/dist/scenario-runner/adapters/kafka-executor.js +12 -3
  42. package/dist/scenario-runner/adapters/kafka-executor.js.map +1 -1
  43. package/dist/scenario-runner/adapters/temporal-executor.d.ts +10 -0
  44. package/dist/scenario-runner/adapters/temporal-executor.d.ts.map +1 -0
  45. package/dist/scenario-runner/adapters/temporal-executor.js +186 -0
  46. package/dist/scenario-runner/adapters/temporal-executor.js.map +1 -0
  47. package/dist/scenario-runner/adapters/types.d.ts +17 -5
  48. package/dist/scenario-runner/adapters/types.d.ts.map +1 -1
  49. package/dist/scenario-runner/condition.d.ts.map +1 -1
  50. package/dist/scenario-runner/condition.js +73 -0
  51. package/dist/scenario-runner/condition.js.map +1 -1
  52. package/dist/scenario-runner/graphTraversal.d.ts +6 -0
  53. package/dist/scenario-runner/graphTraversal.d.ts.map +1 -1
  54. package/dist/scenario-runner/graphTraversal.js +9 -0
  55. package/dist/scenario-runner/graphTraversal.js.map +1 -1
  56. package/dist/scenario-runner/index.d.ts +4 -0
  57. package/dist/scenario-runner/index.d.ts.map +1 -1
  58. package/dist/scenario-runner/index.js +8 -0
  59. package/dist/scenario-runner/index.js.map +1 -1
  60. package/dist/scenario-runner/kafkaEvent.d.ts +3 -0
  61. package/dist/scenario-runner/kafkaEvent.d.ts.map +1 -0
  62. package/dist/scenario-runner/kafkaEvent.js +18 -0
  63. package/dist/scenario-runner/kafkaEvent.js.map +1 -0
  64. package/dist/scenario-runner/nodePolicy.d.ts +9 -2
  65. package/dist/scenario-runner/nodePolicy.d.ts.map +1 -1
  66. package/dist/scenario-runner/nodePolicy.js +53 -0
  67. package/dist/scenario-runner/nodePolicy.js.map +1 -1
  68. package/dist/scenario-runner/runScenario.d.ts.map +1 -1
  69. package/dist/scenario-runner/runScenario.js +115 -22
  70. package/dist/scenario-runner/runScenario.js.map +1 -1
  71. package/dist/scenario-runner/scenarioReference.d.ts +45 -0
  72. package/dist/scenario-runner/scenarioReference.d.ts.map +1 -0
  73. package/dist/scenario-runner/scenarioReference.js +151 -0
  74. package/dist/scenario-runner/scenarioReference.js.map +1 -0
  75. package/dist/scenario-runner/temporal.d.ts +73 -0
  76. package/dist/scenario-runner/temporal.d.ts.map +1 -0
  77. package/dist/scenario-runner/temporal.js +377 -0
  78. package/dist/scenario-runner/temporal.js.map +1 -0
  79. package/dist/scenario-runner/types.d.ts +211 -4
  80. package/dist/scenario-runner/types.d.ts.map +1 -1
  81. package/dist/ui-test/commands.d.ts.map +1 -1
  82. package/dist/ui-test/commands.js +71 -0
  83. package/dist/ui-test/commands.js.map +1 -1
  84. package/dist-cjs/ai/prompts/scenario.js +6 -3
  85. package/dist-cjs/ai/prompts/scenario.js.map +1 -1
  86. package/dist-cjs/ai/prompts/scenarioNodeDefaults.js +9 -0
  87. package/dist-cjs/ai/prompts/scenarioNodeDefaults.js.map +1 -1
  88. package/dist-cjs/ai/prompts/scenarioNodeReference.js +135 -16
  89. package/dist-cjs/ai/prompts/scenarioNodeReference.js.map +1 -1
  90. package/dist-cjs/host/db/db-query-reference.js +1 -1
  91. package/dist-cjs/host/db/db-query-reference.js.map +1 -1
  92. package/dist-cjs/host/db/mongoAdapter.js +23 -1
  93. package/dist-cjs/host/db/mongoAdapter.js.map +1 -1
  94. package/dist-cjs/host/db/queryExecutor.js +1 -1
  95. package/dist-cjs/host/db/queryExecutor.js.map +1 -1
  96. package/dist-cjs/host/index.js +21 -2
  97. package/dist-cjs/host/index.js.map +1 -1
  98. package/dist-cjs/host/temporal/temporalService.js +1200 -0
  99. package/dist-cjs/host/temporal/temporalService.js.map +1 -0
  100. package/dist-cjs/host/temporal/types.js +6 -0
  101. package/dist-cjs/host/temporal/types.js.map +1 -0
  102. package/dist-cjs/scenario-runner/adapters/index.js +17 -3
  103. package/dist-cjs/scenario-runner/adapters/index.js.map +1 -1
  104. package/dist-cjs/scenario-runner/adapters/kafka-executor.js +15 -4
  105. package/dist-cjs/scenario-runner/adapters/kafka-executor.js.map +1 -1
  106. package/dist-cjs/scenario-runner/adapters/temporal-executor.js +190 -0
  107. package/dist-cjs/scenario-runner/adapters/temporal-executor.js.map +1 -0
  108. package/dist-cjs/scenario-runner/condition.js +73 -0
  109. package/dist-cjs/scenario-runner/condition.js.map +1 -1
  110. package/dist-cjs/scenario-runner/graphTraversal.js +10 -1
  111. package/dist-cjs/scenario-runner/graphTraversal.js.map +1 -1
  112. package/dist-cjs/scenario-runner/index.js +24 -1
  113. package/dist-cjs/scenario-runner/index.js.map +1 -1
  114. package/dist-cjs/scenario-runner/kafkaEvent.js +22 -0
  115. package/dist-cjs/scenario-runner/kafkaEvent.js.map +1 -0
  116. package/dist-cjs/scenario-runner/nodePolicy.js +56 -1
  117. package/dist-cjs/scenario-runner/nodePolicy.js.map +1 -1
  118. package/dist-cjs/scenario-runner/runScenario.js +114 -21
  119. package/dist-cjs/scenario-runner/runScenario.js.map +1 -1
  120. package/dist-cjs/scenario-runner/scenarioReference.js +159 -0
  121. package/dist-cjs/scenario-runner/scenarioReference.js.map +1 -0
  122. package/dist-cjs/scenario-runner/temporal.js +386 -0
  123. package/dist-cjs/scenario-runner/temporal.js.map +1 -0
  124. package/dist-cjs/ui-test/commands.js +71 -0
  125. package/dist-cjs/ui-test/commands.js.map +1 -1
  126. package/package.json +2 -1
@@ -0,0 +1,18 @@
1
+ const isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2
+ export const buildScenarioKafkaEventValue = (message, topic) => {
3
+ const facts = {
4
+ value: message.valueJson ?? message.valueText,
5
+ text: message.valueText,
6
+ key: message.key,
7
+ ...(topic ? { topic } : {}),
8
+ partition: message.partition,
9
+ offset: message.offset,
10
+ timestamp: message.timestamp,
11
+ headers: Object.fromEntries(message.headers.map((header) => [header.key, header.value])),
12
+ };
13
+ if (isPlainRecord(message.valueJson)) {
14
+ return { ...message.valueJson, meta: facts };
15
+ }
16
+ return facts;
17
+ };
18
+ //# sourceMappingURL=kafkaEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kafkaEvent.js","sourceRoot":"","sources":["../../src/scenario-runner/kafkaEvent.ts"],"names":[],"mappings":"AASA,MAAM,aAAa,GAAG,CAAC,KAAc,EAAoC,EAAE,CACzE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,OAAqC,EACrC,KAAc,EACW,EAAE;IAC3B,MAAM,KAAK,GAA4B;QACrC,KAAK,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS;QAC7C,IAAI,EAAE,OAAO,CAAC,SAAS;QACvB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;KACzF,CAAC;IAEF,IAAI,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
@@ -1,5 +1,12 @@
1
- import { AggregatorScenarioNode, AiScenarioNode, DbScenarioNode, EndScenarioNode, KafkaScenarioNode, NotificationScenarioNode, RedisScenarioNode, RequestScenarioNode, ScenarioNode, ScenarioNodeExecutionPolicy, ScenarioPolicyCapableNode, ScenarioPolicyCapableNodeType, ScriptScenarioNode, SubScenarioNode, ForeachScenarioNode, WebsocketScenarioNode, WaitScenarioNode } from "./types.js";
1
+ import { AggregatorScenarioNode, AiScenarioNode, DbScenarioNode, EndScenarioNode, KafkaScenarioNode, NotificationScenarioNode, RedisScenarioNode, RequestScenarioNode, ScenarioNode, ScenarioNodeExecutionPolicy, ScenarioNodeExpectedFailure, ScenarioNodeType, ScenarioPolicyCapableNode, ScenarioPolicyCapableNodeType, ScriptScenarioNode, SubScenarioNode, ForeachScenarioNode, TemporalScenarioNode, WebsocketScenarioNode, WaitScenarioNode } from "./types.js";
2
2
  export declare const STRICT_SCENARIO_NODE_EXECUTION_POLICY: ScenarioNodeExecutionPolicy;
3
+ export declare const EXPECT_FAILURE_PATTERN_MAX_LENGTH = 500;
4
+ export declare const SCENARIO_EXPECT_FAILURE_NODE_TYPES: readonly ScenarioNodeType[];
5
+ export declare const supportsScenarioNodeExpectedFailure: (type: ScenarioNodeType) => boolean;
6
+ export declare const matchScenarioExpectedFailure: (expectation: ScenarioNodeExpectedFailure, message: string) => {
7
+ matched: boolean;
8
+ mismatchReason: string | null;
9
+ };
3
10
  export declare const supportsScenarioNodeExecutionPolicy: (nodeOrType: ScenarioNode | ScenarioPolicyCapableNodeType) => nodeOrType is ScenarioPolicyCapableNode | ScenarioPolicyCapableNodeType;
4
11
  export declare const normalizeScenarioNodeExecutionPolicy: (value: unknown, fallback?: ScenarioNodeExecutionPolicy) => ScenarioNodeExecutionPolicy;
5
12
  export declare const getLegacyScenarioNodeExecutionPolicy: () => ScenarioNodeExecutionPolicy;
@@ -7,5 +14,5 @@ export declare const getNewScenarioNodeExecutionPolicyPreset: (type: ScenarioPol
7
14
  export declare const getScenarioNodeExecutionPolicy: (node: ScenarioPolicyCapableNode) => ScenarioNodeExecutionPolicy;
8
15
  export declare const getScenarioNodeMaxAttempts: (policy: ScenarioNodeExecutionPolicy) => number;
9
16
  export declare const getScenarioNodePolicyBadges: (node: ScenarioPolicyCapableNode) => string[];
10
- export declare const patchScenarioNodeExecutionPolicy: <TNode extends RequestScenarioNode | DbScenarioNode | WaitScenarioNode | ScriptScenarioNode | KafkaScenarioNode | RedisScenarioNode | NotificationScenarioNode | EndScenarioNode | AggregatorScenarioNode | SubScenarioNode | ForeachScenarioNode | WebsocketScenarioNode | AiScenarioNode>(node: TNode, executionPolicy: ScenarioNodeExecutionPolicy) => TNode;
17
+ export declare const patchScenarioNodeExecutionPolicy: <TNode extends RequestScenarioNode | DbScenarioNode | WaitScenarioNode | ScriptScenarioNode | KafkaScenarioNode | RedisScenarioNode | NotificationScenarioNode | EndScenarioNode | AggregatorScenarioNode | SubScenarioNode | ForeachScenarioNode | WebsocketScenarioNode | AiScenarioNode | TemporalScenarioNode>(node: TNode, executionPolicy: ScenarioNodeExecutionPolicy) => TNode;
11
18
  //# sourceMappingURL=nodePolicy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nodePolicy.d.ts","sourceRoot":"","sources":["../../src/scenario-runner/nodePolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,qCAAqC,EAAE,2BAOnD,CAAC;AAgEF,eAAO,MAAM,mCAAmC,GAC9C,YAAY,YAAY,GAAG,6BAA6B,KACvD,UAAU,IAAI,yBAAyB,GAAG,6BAI5C,CAAC;AAEF,eAAO,MAAM,oCAAoC,GAC/C,OAAO,OAAO,EACd,WAAU,2BAAmE,KAC5E,2BA+DF,CAAC;AAEF,eAAO,MAAM,oCAAoC,QAAO,2BACJ,CAAC;AAErD,eAAO,MAAM,uCAAuC,GAClD,MAAM,6BAA6B,KAClC,2BAAqF,CAAC;AAEzF,eAAO,MAAM,8BAA8B,GACzC,MAAM,yBAAyB,KAC9B,2BAIA,CAAC;AAEJ,eAAO,MAAM,0BAA0B,GACrC,QAAQ,2BAA2B,KAClC,MAA4E,CAAC;AAEhF,eAAO,MAAM,2BAA2B,GACtC,MAAM,yBAAyB,KAC9B,MAAM,EAeR,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAC3C,KAAK,SACD,mBAAmB,GACnB,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,wBAAwB,GACxB,eAAe,GACf,sBAAsB,GACtB,eAAe,GACf,mBAAmB,GACnB,qBAAqB,GACrB,cAAc,EAElB,MAAM,KAAK,EACX,iBAAiB,2BAA2B,KAC3C,KASD,CAAC"}
1
+ {"version":3,"file":"nodePolicy.d.ts","sourceRoot":"","sources":["../../src/scenario-runner/nodePolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,2BAA2B,EAC3B,2BAA2B,EAC3B,gBAAgB,EAChB,yBAAyB,EACzB,6BAA6B,EAC7B,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,qCAAqC,EAAE,2BAOnD,CAAC;AAkEF,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAKrD,eAAO,MAAM,kCAAkC,EAAE,SAAS,gBAAgB,EAYzE,CAAC;AAEF,eAAO,MAAM,mCAAmC,GAAI,MAAM,gBAAgB,KAAG,OAC1B,CAAC;AAapD,eAAO,MAAM,4BAA4B,GACvC,aAAa,2BAA2B,EACxC,SAAS,MAAM,KACd;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAcnD,CAAC;AAEF,eAAO,MAAM,mCAAmC,GAC9C,YAAY,YAAY,GAAG,6BAA6B,KACvD,UAAU,IAAI,yBAAyB,GAAG,6BAI5C,CAAC;AAEF,eAAO,MAAM,oCAAoC,GAC/C,OAAO,OAAO,EACd,WAAU,2BAAmE,KAC5E,2BAqEF,CAAC;AAEF,eAAO,MAAM,oCAAoC,QAAO,2BACJ,CAAC;AAErD,eAAO,MAAM,uCAAuC,GAClD,MAAM,6BAA6B,KAClC,2BAAqF,CAAC;AAEzF,eAAO,MAAM,8BAA8B,GACzC,MAAM,yBAAyB,KAC9B,2BAIA,CAAC;AAEJ,eAAO,MAAM,0BAA0B,GACrC,QAAQ,2BAA2B,KAClC,MAA4E,CAAC;AAEhF,eAAO,MAAM,2BAA2B,GACtC,MAAM,yBAAyB,KAC9B,MAAM,EAmBR,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAC3C,KAAK,SACD,mBAAmB,GACnB,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,wBAAwB,GACxB,eAAe,GACf,sBAAsB,GACtB,eAAe,GACf,mBAAmB,GACnB,qBAAqB,GACrB,cAAc,GACd,oBAAoB,EAExB,MAAM,KAAK,EACX,iBAAiB,2BAA2B,KAC3C,KASD,CAAC"}
@@ -52,6 +52,8 @@ const RECOMMENDED_NEW_NODE_POLICY_PRESETS = {
52
52
  websocket: STRICT_SCENARIO_NODE_EXECUTION_POLICY,
53
53
  // Retry re-sends the whole prompt (a fresh provider call each attempt).
54
54
  ai: STRICT_SCENARIO_NODE_EXECUTION_POLICY,
55
+ // No retry by default: startWorkflow / signal are not idempotent.
56
+ temporal: STRICT_SCENARIO_NODE_EXECUTION_POLICY,
55
57
  };
56
58
  const clonePolicy = (policy) => ({
57
59
  onFailure: policy.onFailure,
@@ -61,6 +63,50 @@ const clonePolicy = (policy) => ({
61
63
  delayMs: policy.retry.delayMs,
62
64
  },
63
65
  });
66
+ export const EXPECT_FAILURE_PATTERN_MAX_LENGTH = 500;
67
+ // Leaf nodes whose own work can fail. Containers (foreach, subscenario,
68
+ // scenario-ref), routing (condition, end), pure data nodes (set-vars,
69
+ // aggregator) and wait have no failure of their own to expect.
70
+ export const SCENARIO_EXPECT_FAILURE_NODE_TYPES = [
71
+ "request",
72
+ "db",
73
+ "redis",
74
+ "kafka",
75
+ "websocket",
76
+ "script",
77
+ "assertion",
78
+ "poll",
79
+ "ai",
80
+ "temporal",
81
+ "notification",
82
+ ];
83
+ export const supportsScenarioNodeExpectedFailure = (type) => SCENARIO_EXPECT_FAILURE_NODE_TYPES.includes(type);
84
+ const normalizeExpectedFailure = (value) => {
85
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
86
+ return undefined;
87
+ }
88
+ const raw = value.messagePattern;
89
+ const pattern = typeof raw === "string" ? raw.trim().slice(0, EXPECT_FAILURE_PATTERN_MAX_LENGTH) : "";
90
+ return pattern ? { messagePattern: pattern } : {};
91
+ };
92
+ // Whether a failure message satisfies the expectation. An empty expectation
93
+ // accepts any failure; an invalid regex never matches (and says so).
94
+ export const matchScenarioExpectedFailure = (expectation, message) => {
95
+ const pattern = expectation.messagePattern?.trim();
96
+ if (!pattern) {
97
+ return { matched: true, mismatchReason: null };
98
+ }
99
+ let re;
100
+ try {
101
+ re = new RegExp(pattern);
102
+ }
103
+ catch {
104
+ return { matched: false, mismatchReason: `invalid expectFailure.messagePattern regex: ${pattern}` };
105
+ }
106
+ return re.test(message)
107
+ ? { matched: true, mismatchReason: null }
108
+ : { matched: false, mismatchReason: `the error did not match /${pattern}/` };
109
+ };
64
110
  export const supportsScenarioNodeExecutionPolicy = (nodeOrType) => {
65
111
  const type = typeof nodeOrType === "string" ? nodeOrType : nodeOrType.type;
66
112
  return type !== "condition";
@@ -104,6 +150,9 @@ export const normalizeScenarioNodeExecutionPolicy = (value, fallback = STRICT_SC
104
150
  const maxDelayMs = retryEnabled && backoffMultiplier && typeof retryMaxDelayRaw === "number" && retryMaxDelayRaw > 0
105
151
  ? Math.max(delayMs, Math.round(retryMaxDelayRaw))
106
152
  : undefined;
153
+ const expectFailure = typeof value === "object" && value !== null && "expectFailure" in value
154
+ ? normalizeExpectedFailure(value.expectFailure)
155
+ : undefined;
107
156
  return {
108
157
  onFailure: normalizedOnFailure,
109
158
  retry: {
@@ -113,6 +162,7 @@ export const normalizeScenarioNodeExecutionPolicy = (value, fallback = STRICT_SC
113
162
  ...(backoffMultiplier ? { backoffMultiplier } : {}),
114
163
  ...(maxDelayMs ? { maxDelayMs } : {}),
115
164
  },
165
+ ...(expectFailure ? { expectFailure } : {}),
116
166
  };
117
167
  };
118
168
  export const getLegacyScenarioNodeExecutionPolicy = () => clonePolicy(STRICT_SCENARIO_NODE_EXECUTION_POLICY);
@@ -131,6 +181,9 @@ export const getScenarioNodePolicyBadges = (node) => {
131
181
  if (policy.retry.enabled) {
132
182
  badges.push(`Retry ×${getScenarioNodeMaxAttempts(policy)}`);
133
183
  }
184
+ if (policy.expectFailure && supportsScenarioNodeExpectedFailure(node.type)) {
185
+ badges.push("Expects failure");
186
+ }
134
187
  return badges;
135
188
  };
136
189
  export const patchScenarioNodeExecutionPolicy = (node, executionPolicy) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"nodePolicy.js","sourceRoot":"","sources":["../../src/scenario-runner/nodePolicy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nodePolicy.js","sourceRoot":"","sources":["../../src/scenario-runner/nodePolicy.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runScenario.d.ts","sourceRoot":"","sources":["../../src/scenario-runner/runScenario.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAMV,kBAAkB,EAUlB,iBAAiB,EAKlB,MAAM,YAAY,CAAC;AA6uBpB,eAAO,MAAM,WAAW,GAAU,qHAS/B,kBAAkB,KAAG,OAAO,CAAC,iBAAiB,CAgnFhD,CAAC"}
1
+ {"version":3,"file":"runScenario.d.ts","sourceRoot":"","sources":["../../src/scenario-runner/runScenario.ts"],"names":[],"mappings":"AA+DA,OAAO,KAAK,EAMV,kBAAkB,EAUlB,iBAAiB,EAMlB,MAAM,YAAY,CAAC;AA2tBpB,eAAO,MAAM,WAAW,GAAU,qHAS/B,kBAAkB,KAAG,OAAO,CAAC,iBAAiB,CAgvFhD,CAAC"}
@@ -6,7 +6,8 @@ import { createSubScenarioStepId, buildRuntimeStepDescriptors, DEFAULT_END_LOOP_
6
6
  import { evaluateWebsocketExpect } from "./websocket.js";
7
7
  import { executeWithScenarioNodePolicy } from "./executionPolicyRunner.js";
8
8
  import { boundSnapshot } from "./boundSnapshot.js";
9
- import { getLegacyScenarioNodeExecutionPolicy, getScenarioNodeExecutionPolicy, supportsScenarioNodeExecutionPolicy, } from "./nodePolicy.js";
9
+ import { buildScenarioKafkaEventValue } from "./kafkaEvent.js";
10
+ import { getLegacyScenarioNodeExecutionPolicy, getScenarioNodeExecutionPolicy, matchScenarioExpectedFailure, supportsScenarioNodeExecutionPolicy, supportsScenarioNodeExpectedFailure, } from "./nodePolicy.js";
10
11
  import { buildScenarioExecutionGraph, buildScenarioTemplateValues, collectExclusiveBranchNodeIds, evaluateScenarioExpression, resolveScenarioJsonTemplateString, resolveScenarioTemplateString, } from "./templateResolver.js";
11
12
  import { executeWaitNode, ScenarioWaitNodeCancelledError, ScenarioWaitNodeValidationError, } from "./waitNodeExecutor.js";
12
13
  import { buildNotificationDeliveryRequest, renderNotificationTemplate, } from "./notification/index.js";
@@ -22,6 +23,11 @@ class ScenarioNodeFailureError extends Error {
22
23
  this.name = "ScenarioNodeFailureError";
23
24
  }
24
25
  }
26
+ // An attempt that succeeded although the node's policy expects a failure.
27
+ // Kept apart from ordinary failures so it is never mistaken for the
28
+ // expected one.
29
+ class ScenarioExpectedFailureNotMetError extends ScenarioNodeFailureError {
30
+ }
25
31
  const DEFAULT_MIN_NODE_VISUAL_STATE_MS = 180;
26
32
  const createId = (prefix) => {
27
33
  const randomUuid = globalThis.crypto?.randomUUID?.();
@@ -122,24 +128,6 @@ const createContextSnapshot = (context, mode = "full") => {
122
128
  redisResult: bound(context.redisResult),
123
129
  };
124
130
  };
125
- const buildKafkaEventValue = (message) => {
126
- const fallbackValue = {
127
- value: message.valueJson ?? message.valueText,
128
- text: message.valueText,
129
- key: message.key,
130
- partition: message.partition,
131
- offset: message.offset,
132
- timestamp: message.timestamp,
133
- headers: Object.fromEntries(message.headers.map((header) => [header.key, header.value])),
134
- };
135
- if (isRecord(message.valueJson)) {
136
- return {
137
- ...message.valueJson,
138
- meta: fallbackValue,
139
- };
140
- }
141
- return fallbackValue;
142
- };
143
131
  const buildKafkaFailureMessage = (result, usedMatch) => {
144
132
  if (result.errorMessage?.trim()) {
145
133
  return result.errorMessage;
@@ -331,6 +319,7 @@ const createStepExecutionRecord = ({ stepId, stepType, title, }) => ({
331
319
  conditionDebug: null,
332
320
  activeBranch: null,
333
321
  ai: null,
322
+ temporal: null,
334
323
  inputData: null,
335
324
  resolvedInput: null,
336
325
  outputData: null,
@@ -511,6 +500,7 @@ const updateRecordFromEvent = (records, event) => {
511
500
  conditionDebug: event.conditionDebug ?? current.conditionDebug,
512
501
  activeBranch: event.activeBranch ?? current.activeBranch,
513
502
  ai: event.ai ?? current.ai,
503
+ temporal: event.temporal ?? current.temporal,
514
504
  inputData: event.inputData ?? current.inputData,
515
505
  resolvedInput: event.resolvedInput ?? current.resolvedInput ?? null,
516
506
  outputData: event.outputData ?? current.outputData,
@@ -864,6 +854,16 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
864
854
  retry: { enabled: false, maxAttempts: 0, delayMs: 0 },
865
855
  };
866
856
  }
857
+ // Negative test: this node must fail. On a db node a non-empty
858
+ // expectError is the more precise expectation and wins.
859
+ const dbExpectsError = node.type === "db" &&
860
+ Boolean(node.data.config.expectError?.sqlState?.trim() ||
861
+ node.data.config.expectError?.messagePattern?.trim());
862
+ const expectedFailure = executionPolicy.expectFailure &&
863
+ supportsScenarioNodeExpectedFailure(node.type) &&
864
+ !dbExpectsError
865
+ ? executionPolicy.expectFailure
866
+ : undefined;
867
867
  const executionStartedAtMs = Date.now();
868
868
  let inputData = createInputSnapshot(runtimeContext);
869
869
  // The node's OWN resolved input (query + params, topic + payload, the
@@ -897,6 +897,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
897
897
  let shouldMarkPartialSuccess = false;
898
898
  let workflowOutput = undefined;
899
899
  let aiDetails = null;
900
+ let temporalDetails = null;
900
901
  const resetAttemptArtifacts = () => {
901
902
  statusCode = null;
902
903
  response = null;
@@ -924,6 +925,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
924
925
  shouldMarkPartialSuccess = false;
925
926
  workflowOutput = undefined;
926
927
  aiDetails = null;
928
+ temporalDetails = null;
927
929
  };
928
930
  const executeNodeAttempt = async (attempt, maxAttempts) => {
929
931
  throwIfStopped(signal);
@@ -1140,6 +1142,10 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1140
1142
  : null;
1141
1143
  // Thinned emission keeps stored events bounded (~100 tick events).
1142
1144
  const pollEmitStride = Math.max(1, Math.ceil(pollMaxAttempts / 100));
1145
+ // Temporal ticks have no runtime-context slot (unlike response /
1146
+ // db / redis), so the last tick's output rides along on the poll
1147
+ // output as `<Label>.temporal`.
1148
+ let lastTemporalTickOutput = null;
1143
1149
  resolvedInput = {
1144
1150
  actionType: pollConfig.actionType,
1145
1151
  intervalMs: pollBaseIntervalMs,
@@ -1245,6 +1251,39 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1245
1251
  runtimeContext.redisResult = tickResult.redisResult;
1246
1252
  return tickResult.failureMessage ?? null;
1247
1253
  }
1254
+ if (pollConfig.actionType === "temporal") {
1255
+ if (!pollConfig.temporal) {
1256
+ throw new ScenarioNodeFailureError("Poll node has no Temporal configuration.");
1257
+ }
1258
+ if (!runtimeAdapters.temporalExecutor) {
1259
+ throw new ScenarioNodeFailureError("This host does not support Temporal nodes yet.");
1260
+ }
1261
+ const tickResult = await runtimeAdapters.temporalExecutor({
1262
+ runId,
1263
+ stepId,
1264
+ node: {
1265
+ id: node.id,
1266
+ type: "temporal",
1267
+ position: node.position,
1268
+ data: { label: node.data.label, config: pollConfig.temporal },
1269
+ },
1270
+ context: snapshotContext(),
1271
+ signal,
1272
+ });
1273
+ temporalDetails = tickResult.details;
1274
+ assertionResults = tickResult.assertionResults;
1275
+ assertionSummary = tickResult.assertionSummary;
1276
+ lastTemporalTickOutput = tickResult.outputData;
1277
+ // Publish the tick's output under the label so the success
1278
+ // condition reads `<Label>.status` / `<Label>.result.x` exactly
1279
+ // like it would after a standalone temporal node.
1280
+ if (tickResult.outputData &&
1281
+ nodeLabel &&
1282
+ !RESERVED_WORKFLOW_ROOT_LABELS.has(nodeLabel)) {
1283
+ runtimeContext.workflowContext[nodeLabel] = tickResult.outputData;
1284
+ }
1285
+ return tickResult.failureMessage ?? null;
1286
+ }
1248
1287
  if (!pollConfig.script) {
1249
1288
  throw new ScenarioNodeFailureError("Poll node has no script configuration.");
1250
1289
  }
@@ -1341,6 +1380,9 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1341
1380
  timedOut: !pollPassed,
1342
1381
  conditionResult: pollPassed,
1343
1382
  ...(lastTickError ? { lastTickError } : {}),
1383
+ ...(pollConfig.actionType === "temporal" && lastTemporalTickOutput
1384
+ ? { temporal: lastTemporalTickOutput }
1385
+ : {}),
1344
1386
  };
1345
1387
  if (!pollPassed) {
1346
1388
  if ((pollConfig.onTimeout ?? "fail") === "continue") {
@@ -1512,7 +1554,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1512
1554
  if (resolvedMatchMode === "rules" && rules.length > 0) {
1513
1555
  matchedMessage =
1514
1556
  captureResult.messages.find((message) => {
1515
- const kafkaEventValue = buildKafkaEventValue(message);
1557
+ const kafkaEventValue = buildScenarioKafkaEventValue(message, captureResult.topic);
1516
1558
  const scopedValues = buildScenarioTemplateValues({
1517
1559
  environment: runtimeContext.environment,
1518
1560
  globals: runtimeContext.globals,
@@ -1531,7 +1573,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1531
1573
  throw new ScenarioNodeFailureError(buildKafkaFailureMessage(captureResult, Boolean(rules.length)));
1532
1574
  }
1533
1575
  runtimeContext.kafkaCapture = captureResult;
1534
- runtimeContext.kafkaEvent = buildKafkaEventValue(matchedMessage);
1576
+ runtimeContext.kafkaEvent = buildScenarioKafkaEventValue(matchedMessage, captureResult.topic);
1535
1577
  outputData = runtimeContext.kafkaEvent;
1536
1578
  }
1537
1579
  }
@@ -1727,6 +1769,35 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1727
1769
  throw new ScenarioNodeFailureError(aiResult.failureMessage);
1728
1770
  }
1729
1771
  }
1772
+ if (node.type === "temporal") {
1773
+ // Thin dispatch (ai pattern): the adapter resolves templates,
1774
+ // validates the operation, calls the host driver and evaluates the
1775
+ // node's own `expect` rules. A FAILED workflow is data (status +
1776
+ // failure in the output); only infrastructure errors and failed
1777
+ // expectations fail the node.
1778
+ if (!runtimeAdapters.temporalExecutor) {
1779
+ throw new ScenarioNodeFailureError("This host does not support Temporal nodes yet.");
1780
+ }
1781
+ const temporalResult = await runtimeAdapters.temporalExecutor({
1782
+ runId,
1783
+ stepId,
1784
+ node,
1785
+ context: snapshotContext(),
1786
+ signal,
1787
+ });
1788
+ throwIfStopped(signal);
1789
+ temporalDetails = temporalResult.details;
1790
+ outputData = temporalResult.outputData;
1791
+ resolvedInput = temporalResult.resolvedInput;
1792
+ assertionResults = temporalResult.assertionResults;
1793
+ assertionSummary = temporalResult.assertionSummary;
1794
+ if (temporalResult.degraded) {
1795
+ shouldMarkPartialSuccess = true;
1796
+ }
1797
+ if (temporalResult.failureMessage) {
1798
+ throw new ScenarioNodeFailureError(temporalResult.failureMessage);
1799
+ }
1800
+ }
1730
1801
  if (node.type === "condition") {
1731
1802
  const conditionIssues = validateConditionConfig(node.data.config);
1732
1803
  const blockingConditionIssue = conditionIssues.find((issue) => issue.severity === "error");
@@ -2225,15 +2296,34 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
2225
2296
  runtimeContext.workflowContext[nodeLabel] = workflowOutput;
2226
2297
  }
2227
2298
  await ensureMinimumVisualDuration(attemptStartedAtMs);
2299
+ if (expectedFailure) {
2300
+ throw new ScenarioExpectedFailureNotMetError(`Expected this node to fail${expectedFailure.messagePattern
2301
+ ? ` with an error matching /${expectedFailure.messagePattern}/`
2302
+ : ""}, but it succeeded.`);
2303
+ }
2228
2304
  }
2229
2305
  catch (error) {
2230
2306
  if (error instanceof ScenarioStopError) {
2231
2307
  throw error;
2232
2308
  }
2233
2309
  await ensureMinimumVisualDuration(attemptStartedAtMs);
2234
- throw error instanceof ScenarioNodeFailureError
2310
+ const failure = error instanceof ScenarioNodeFailureError
2235
2311
  ? error
2236
2312
  : new ScenarioNodeFailureError(error instanceof Error ? error.message : "Scenario node failed");
2313
+ if (expectedFailure && !(failure instanceof ScenarioExpectedFailureNotMetError)) {
2314
+ const match = matchScenarioExpectedFailure(expectedFailure, failure.message);
2315
+ if (!match.matched) {
2316
+ throw new ScenarioNodeFailureError(`The node failed, but not as expected (${match.mismatchReason}): ${failure.message}`);
2317
+ }
2318
+ // The failure IS the result: the node succeeds and exposes it.
2319
+ outputData = { expectedFailure: { message: failure.message } };
2320
+ workflowOutput = outputData;
2321
+ if (nodeLabel && !RESERVED_WORKFLOW_ROOT_LABELS.has(nodeLabel)) {
2322
+ runtimeContext.workflowContext[nodeLabel] = workflowOutput;
2323
+ }
2324
+ return;
2325
+ }
2326
+ throw failure;
2237
2327
  }
2238
2328
  };
2239
2329
  try {
@@ -2348,6 +2438,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
2348
2438
  conditionDebug,
2349
2439
  activeBranch,
2350
2440
  ai: aiDetails,
2441
+ temporal: temporalDetails,
2351
2442
  inputData,
2352
2443
  resolvedInput: resolvedInputForEvent(),
2353
2444
  outputData,
@@ -2432,6 +2523,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
2432
2523
  conditionDebug,
2433
2524
  activeBranch,
2434
2525
  ai: aiDetails,
2526
+ temporal: temporalDetails,
2435
2527
  inputData,
2436
2528
  resolvedInput: resolvedInputForEvent(),
2437
2529
  outputData,
@@ -2511,6 +2603,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
2511
2603
  conditionDebug,
2512
2604
  activeBranch,
2513
2605
  ai: aiDetails,
2606
+ temporal: temporalDetails,
2514
2607
  inputData,
2515
2608
  resolvedInput: resolvedInputForEvent(),
2516
2609
  outputData,