@n8n/instance-ai 1.12.2 → 1.13.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 (250) hide show
  1. package/dist/agent/apply-agent-thinking.d.ts +2 -0
  2. package/dist/agent/apply-agent-thinking.js +41 -0
  3. package/dist/agent/apply-agent-thinking.js.map +1 -0
  4. package/dist/agent/instance-agent.js +10 -2
  5. package/dist/agent/instance-agent.js.map +1 -1
  6. package/dist/agent/sanitize-mcp-schemas.js +133 -125
  7. package/dist/agent/sanitize-mcp-schemas.js.map +1 -1
  8. package/dist/agent/shared-prompts.d.ts +0 -1
  9. package/dist/agent/shared-prompts.js +4 -11
  10. package/dist/agent/shared-prompts.js.map +1 -1
  11. package/dist/agent/sub-agent-debriefing.d.ts +4 -4
  12. package/dist/agent/system-prompt.js +3 -10
  13. package/dist/agent/system-prompt.js.map +1 -1
  14. package/dist/build.tsbuildinfo +1 -1
  15. package/dist/debug/run-debug-buffer.d.ts +63 -0
  16. package/dist/debug/run-debug-buffer.js +177 -0
  17. package/dist/debug/run-debug-buffer.js.map +1 -0
  18. package/dist/debug/sanitize-debug-snapshot.d.ts +2 -0
  19. package/dist/debug/sanitize-debug-snapshot.js +88 -0
  20. package/dist/debug/sanitize-debug-snapshot.js.map +1 -0
  21. package/dist/error-formatting.js +3 -5
  22. package/dist/error-formatting.js.map +1 -1
  23. package/dist/index.d.ts +5 -2
  24. package/dist/index.js +8 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/knowledge-base/build-templates-index.d.ts +1 -0
  27. package/dist/knowledge-base/build-templates-index.js +18 -2
  28. package/dist/knowledge-base/build-templates-index.js.map +1 -1
  29. package/dist/knowledge-base/materialize-knowledge-base.d.ts +2 -3
  30. package/dist/knowledge-base/materialize-knowledge-base.js +10 -7
  31. package/dist/knowledge-base/materialize-knowledge-base.js.map +1 -1
  32. package/dist/mcp/mcp-client-manager.d.ts +1 -1
  33. package/dist/mcp/mcp-client-manager.js +13 -9
  34. package/dist/mcp/mcp-client-manager.js.map +1 -1
  35. package/dist/runtime/memory-task-registry.d.ts +8 -0
  36. package/dist/runtime/memory-task-registry.js +42 -0
  37. package/dist/runtime/memory-task-registry.js.map +1 -0
  38. package/dist/runtime/resumable-stream-executor.d.ts +3 -0
  39. package/dist/runtime/resumable-stream-executor.js +171 -90
  40. package/dist/runtime/resumable-stream-executor.js.map +1 -1
  41. package/dist/runtime/run-state-registry.d.ts +2 -0
  42. package/dist/runtime/run-state-registry.js +3 -0
  43. package/dist/runtime/run-state-registry.js.map +1 -1
  44. package/dist/runtime/stream-runner.d.ts +3 -0
  45. package/dist/runtime/stream-runner.js +3 -0
  46. package/dist/runtime/stream-runner.js.map +1 -1
  47. package/dist/skills/materialize-runtime-skills.d.ts +3 -3
  48. package/dist/skills/materialize-runtime-skills.js +1 -1
  49. package/dist/skills/materialize-runtime-skills.js.map +1 -1
  50. package/dist/source-map-filter.js +0 -1
  51. package/dist/source-map-filter.js.map +1 -1
  52. package/dist/storage/terminal-outcome-storage.d.ts +2 -2
  53. package/dist/storage/workflow-loop-storage.d.ts +75 -4
  54. package/dist/stream/map-chunk.js +198 -237
  55. package/dist/stream/map-chunk.js.map +1 -1
  56. package/dist/stream/output-redaction.js +2 -2
  57. package/dist/stream/output-redaction.js.map +1 -1
  58. package/dist/stream/usage-accumulator.d.ts +90 -0
  59. package/dist/stream/usage-accumulator.js +89 -0
  60. package/dist/stream/usage-accumulator.js.map +1 -0
  61. package/dist/stream/work-summary-accumulator.d.ts +6 -6
  62. package/dist/tools/attachments/parse-file.tool.js +1 -1
  63. package/dist/tools/attachments/parse-file.tool.js.map +1 -1
  64. package/dist/tools/credentials.tool.js +4 -1
  65. package/dist/tools/credentials.tool.js.map +1 -1
  66. package/dist/tools/data-tables.tool.js +5 -2
  67. package/dist/tools/data-tables.tool.js.map +1 -1
  68. package/dist/tools/evals/column-ref-utils.d.ts +1 -2
  69. package/dist/tools/evals/column-ref-utils.js +19 -18
  70. package/dist/tools/evals/column-ref-utils.js.map +1 -1
  71. package/dist/tools/evals/describe-metric-for-workflow.service.js +7 -25
  72. package/dist/tools/evals/describe-metric-for-workflow.service.js.map +1 -1
  73. package/dist/tools/evals/detect-tool-refs.service.js +2 -1
  74. package/dist/tools/evals/detect-tool-refs.service.js.map +1 -1
  75. package/dist/tools/evals/eval-data-requirements.service.js +10 -9
  76. package/dist/tools/evals/eval-data-requirements.service.js.map +1 -1
  77. package/dist/tools/evals/evals.tool.js +2 -2
  78. package/dist/tools/evals/evals.tool.js.map +1 -1
  79. package/dist/tools/evals/extract-rows-from-history.service.js +8 -8
  80. package/dist/tools/evals/extract-rows-from-history.service.js.map +1 -1
  81. package/dist/tools/evals/generate-sample-rows.service.d.ts +2 -2
  82. package/dist/tools/evals/generate-sample-rows.service.js +30 -49
  83. package/dist/tools/evals/generate-sample-rows.service.js.map +1 -1
  84. package/dist/tools/evals/generate-tool-ref-pin-data.service.js +10 -23
  85. package/dist/tools/evals/generate-tool-ref-pin-data.service.js.map +1 -1
  86. package/dist/tools/evals/metric-catalog.js +3 -3
  87. package/dist/tools/evals/metric-catalog.js.map +1 -1
  88. package/dist/tools/executions.tool.js +10 -2
  89. package/dist/tools/executions.tool.js.map +1 -1
  90. package/dist/tools/filesystem/create-tools-from-mcp-server.d.ts +1 -1
  91. package/dist/tools/filesystem/create-tools-from-mcp-server.js +8 -10
  92. package/dist/tools/filesystem/create-tools-from-mcp-server.js.map +1 -1
  93. package/dist/tools/index.js +1 -1
  94. package/dist/tools/index.js.map +1 -1
  95. package/dist/tools/nodes/node-search-engine.js +21 -42
  96. package/dist/tools/nodes/node-search-engine.js.map +1 -1
  97. package/dist/tools/nodes/node-search-engine.types.d.ts +2 -0
  98. package/dist/tools/nodes/preferred-chat-model.d.ts +1 -0
  99. package/dist/tools/nodes/preferred-chat-model.js +19 -0
  100. package/dist/tools/nodes/preferred-chat-model.js.map +1 -0
  101. package/dist/tools/nodes/suggested-nodes-data.js +13 -3
  102. package/dist/tools/nodes/suggested-nodes-data.js.map +1 -1
  103. package/dist/tools/nodes.tool.js +32 -7
  104. package/dist/tools/nodes.tool.js.map +1 -1
  105. package/dist/tools/orchestration/complete-checkpoint.tool.js +3 -5
  106. package/dist/tools/orchestration/complete-checkpoint.tool.js.map +1 -1
  107. package/dist/tools/orchestration/eval-data-agent.tool.js +2 -1
  108. package/dist/tools/orchestration/eval-data-agent.tool.js.map +1 -1
  109. package/dist/tools/orchestration/eval-setup-agent.prompt.d.ts +1 -1
  110. package/dist/tools/orchestration/eval-setup-agent.prompt.js +3 -3
  111. package/dist/tools/orchestration/report-verification-verdict.tool.d.ts +2 -2
  112. package/dist/tools/orchestration/report-verification-verdict.tool.js +1 -1
  113. package/dist/tools/orchestration/report-verification-verdict.tool.js.map +1 -1
  114. package/dist/tools/orchestration/verify-built-workflow.tool.js +320 -307
  115. package/dist/tools/orchestration/verify-built-workflow.tool.js.map +1 -1
  116. package/dist/tools/research.tool.js +1 -1
  117. package/dist/tools/research.tool.js.map +1 -1
  118. package/dist/tools/shared/ask-user.tool.d.ts +4 -4
  119. package/dist/tools/shared/ask-user.tool.js +6 -1
  120. package/dist/tools/shared/ask-user.tool.js.map +1 -1
  121. package/dist/tools/tool-ids.d.ts +0 -1
  122. package/dist/tools/tool-ids.js +0 -1
  123. package/dist/tools/tool-ids.js.map +1 -1
  124. package/dist/tools/workflows/build-workflow.tool.d.ts +4 -22
  125. package/dist/tools/workflows/build-workflow.tool.js +373 -317
  126. package/dist/tools/workflows/build-workflow.tool.js.map +1 -1
  127. package/dist/tools/workflows/classify-node-destructiveness.service.d.ts +7 -0
  128. package/dist/tools/workflows/classify-node-destructiveness.service.js +307 -0
  129. package/dist/tools/workflows/classify-node-destructiveness.service.js.map +1 -0
  130. package/dist/tools/workflows/generate-simulation-fixtures.service.d.ts +8 -0
  131. package/dist/tools/workflows/generate-simulation-fixtures.service.js +95 -0
  132. package/dist/tools/workflows/generate-simulation-fixtures.service.js.map +1 -0
  133. package/dist/tools/workflows/plan-verification-simulation.d.ts +15 -0
  134. package/dist/tools/workflows/plan-verification-simulation.js +32 -0
  135. package/dist/tools/workflows/plan-verification-simulation.js.map +1 -0
  136. package/dist/tools/workflows/resolve-credentials.d.ts +0 -2
  137. package/dist/tools/workflows/resolve-credentials.js +0 -13
  138. package/dist/tools/workflows/resolve-credentials.js.map +1 -1
  139. package/dist/tools/workflows/setup-workflow.service.js +259 -215
  140. package/dist/tools/workflows/setup-workflow.service.js.map +1 -1
  141. package/dist/tools/workflows/validate-workflow.service.js +81 -66
  142. package/dist/tools/workflows/validate-workflow.service.js.map +1 -1
  143. package/dist/tools/workflows/workflow-build-context.d.ts +33 -0
  144. package/dist/tools/workflows/workflow-build-context.js +80 -0
  145. package/dist/tools/workflows/workflow-build-context.js.map +1 -0
  146. package/dist/tools/workflows/workflow-build-remediation.d.ts +11 -0
  147. package/dist/tools/workflows/workflow-build-remediation.js +89 -0
  148. package/dist/tools/workflows/workflow-build-remediation.js.map +1 -0
  149. package/dist/tools/workflows/workflow-build-reporting.d.ts +19 -0
  150. package/dist/tools/workflows/workflow-build-reporting.js +78 -0
  151. package/dist/tools/workflows/workflow-build-reporting.js.map +1 -0
  152. package/dist/tools/workflows/workflow-build-routing.d.ts +2 -0
  153. package/dist/tools/workflows/workflow-build-routing.js +83 -0
  154. package/dist/tools/workflows/workflow-build-routing.js.map +1 -0
  155. package/dist/tools/workflows/workflow-build-telemetry.d.ts +18 -0
  156. package/dist/tools/workflows/workflow-build-telemetry.js +33 -0
  157. package/dist/tools/workflows/workflow-build-telemetry.js.map +1 -0
  158. package/dist/tools/workflows/workflow-file-bindings.d.ts +28 -0
  159. package/dist/tools/workflows/workflow-file-bindings.js +104 -0
  160. package/dist/tools/workflows/workflow-file-bindings.js.map +1 -0
  161. package/dist/tools/workflows/workflow-json-utils.d.ts +3 -0
  162. package/dist/tools/workflows/workflow-json-utils.js +54 -5
  163. package/dist/tools/workflows/workflow-json-utils.js.map +1 -1
  164. package/dist/tools/workflows/workflow-source-compiler.d.ts +19 -0
  165. package/dist/tools/workflows/workflow-source-compiler.js +217 -0
  166. package/dist/tools/workflows/workflow-source-compiler.js.map +1 -0
  167. package/dist/tools/workflows/workflow-validation-warnings.d.ts +14 -0
  168. package/dist/tools/workflows/workflow-validation-warnings.js +28 -0
  169. package/dist/tools/workflows/workflow-validation-warnings.js.map +1 -0
  170. package/dist/tools/workflows.tool.js +107 -99
  171. package/dist/tools/workflows.tool.js.map +1 -1
  172. package/dist/tracing/langsmith-tracing.js +7 -7
  173. package/dist/tracing/langsmith-tracing.js.map +1 -1
  174. package/dist/tracing/trace-payloads.d.ts +2 -1
  175. package/dist/tracing/trace-payloads.js +152 -114
  176. package/dist/tracing/trace-payloads.js.map +1 -1
  177. package/dist/types.d.ts +24 -4
  178. package/dist/types.js.map +1 -1
  179. package/dist/utils/eval-agents.d.ts +0 -3
  180. package/dist/utils/eval-agents.js +3 -11
  181. package/dist/utils/eval-agents.js.map +1 -1
  182. package/dist/utils/generate-validated-json.d.ts +16 -0
  183. package/dist/utils/generate-validated-json.js +38 -0
  184. package/dist/utils/generate-validated-json.js.map +1 -0
  185. package/dist/utils/stream-helpers.d.ts +0 -1
  186. package/dist/utils/stream-helpers.js +5 -8
  187. package/dist/utils/stream-helpers.js.map +1 -1
  188. package/dist/workflow-builder/build-failure-tracker.js +25 -1
  189. package/dist/workflow-builder/build-failure-tracker.js.map +1 -1
  190. package/dist/workflow-builder/index.d.ts +0 -4
  191. package/dist/workflow-builder/index.js +1 -11
  192. package/dist/workflow-builder/index.js.map +1 -1
  193. package/dist/workflow-loop/guidance.js +14 -15
  194. package/dist/workflow-loop/guidance.js.map +1 -1
  195. package/dist/workflow-loop/runtime.js +1 -0
  196. package/dist/workflow-loop/runtime.js.map +1 -1
  197. package/dist/workflow-loop/verification-obligation.js +8 -1
  198. package/dist/workflow-loop/verification-obligation.js.map +1 -1
  199. package/dist/workflow-loop/workflow-loop-controller.js +17 -1
  200. package/dist/workflow-loop/workflow-loop-controller.js.map +1 -1
  201. package/dist/workflow-loop/workflow-loop-state.d.ts +90 -6
  202. package/dist/workflow-loop/workflow-loop-state.js +13 -1
  203. package/dist/workflow-loop/workflow-loop-state.js.map +1 -1
  204. package/dist/workspace/builder-templates-service.d.ts +5 -5
  205. package/dist/workspace/builder-templates-service.js +17 -14
  206. package/dist/workspace/builder-templates-service.js.map +1 -1
  207. package/dist/workspace/create-workspace.js +8 -1
  208. package/dist/workspace/create-workspace.js.map +1 -1
  209. package/dist/workspace/prebaked-workspace-bundle.d.ts +2 -2
  210. package/dist/workspace/prebaked-workspace-bundle.js +5 -5
  211. package/dist/workspace/prebaked-workspace-bundle.js.map +1 -1
  212. package/dist/workspace/sandbox-setup.d.ts +1 -1
  213. package/dist/workspace/sandbox-setup.js +10 -37
  214. package/dist/workspace/sandbox-setup.js.map +1 -1
  215. package/dist/workspace/snapshot-manager.d.ts +1 -1
  216. package/dist/workspace/template-telemetry.js +2 -5
  217. package/dist/workspace/template-telemetry.js.map +1 -1
  218. package/dist/workspace/workspace-files.d.ts +1 -1
  219. package/dist/workspace/workspace-files.js +3 -3
  220. package/dist/workspace/workspace-files.js.map +1 -1
  221. package/dist/workspace/workspace-manifest.js +2 -2
  222. package/dist/workspace/workspace-manifest.js.map +1 -1
  223. package/dist/workspace/workspace-paths.d.ts +4 -0
  224. package/dist/workspace/workspace-paths.js +25 -0
  225. package/dist/workspace/workspace-paths.js.map +1 -0
  226. package/knowledge-base/reference/open-ai-output-shape.md +79 -0
  227. package/knowledge-base/reference/workflow-builder-guardrails.md +15 -0
  228. package/package.json +13 -12
  229. package/skills/planned-task-runtime/SKILL.md +3 -2
  230. package/skills/planning/SKILL.md +5 -0
  231. package/skills/post-build-flow/SKILL.md +25 -9
  232. package/skills/workflow-builder/SKILL.md +89 -134
  233. package/dist/tools/workflows/submit-workflow-identity.d.ts +0 -39
  234. package/dist/tools/workflows/submit-workflow-identity.js +0 -181
  235. package/dist/tools/workflows/submit-workflow-identity.js.map +0 -1
  236. package/dist/tools/workflows/submit-workflow.tool.d.ts +0 -223
  237. package/dist/tools/workflows/submit-workflow.tool.js +0 -491
  238. package/dist/tools/workflows/submit-workflow.tool.js.map +0 -1
  239. package/dist/workflow-builder/extract-code.d.ts +0 -5
  240. package/dist/workflow-builder/extract-code.js +0 -105
  241. package/dist/workflow-builder/extract-code.js.map +0 -1
  242. package/dist/workflow-builder/parse-validate.d.ts +0 -10
  243. package/dist/workflow-builder/parse-validate.js +0 -50
  244. package/dist/workflow-builder/parse-validate.js.map +0 -1
  245. package/dist/workflow-builder/patch-code.d.ts +0 -14
  246. package/dist/workflow-builder/patch-code.js +0 -117
  247. package/dist/workflow-builder/patch-code.js.map +0 -1
  248. package/dist/workflow-builder/types.d.ts +0 -11
  249. package/dist/workflow-builder/types.js +0 -3
  250. package/dist/workflow-builder/types.js.map +0 -1
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.verifyBuiltWorkflowInputSchema = void 0;
4
4
  exports.createVerifyBuiltWorkflowTool = createVerifyBuiltWorkflowTool;
5
5
  const agents_1 = require("@n8n/agents");
6
+ const utils_1 = require("@n8n/utils");
6
7
  const zod_1 = require("zod");
7
8
  const remediation_1 = require("../../workflow-loop/remediation");
8
9
  const DEFAULT_NODE_PREVIEW_CHARS = 600;
@@ -16,9 +17,6 @@ function stringifyForToolOutput(value) {
16
17
  return String(value);
17
18
  }
18
19
  }
19
- function isRecord(value) {
20
- return typeof value === 'object' && value !== null && !Array.isArray(value);
21
- }
22
20
  function unwrapUntrustedData(value) {
23
21
  const match = /^<untrusted_data\b[^>]*>\n([\s\S]*)\n<\/untrusted_data>$/i.exec(value);
24
22
  if (!match)
@@ -37,67 +35,8 @@ function unwrapUntrustedData(value) {
37
35
  function outputForInspection(nodeOutput) {
38
36
  return typeof nodeOutput === 'string' ? unwrapUntrustedData(nodeOutput) : nodeOutput;
39
37
  }
40
- async function extractDataTableWriteNodes(workflowService, workflowId) {
41
- try {
42
- const json = await workflowService.getAsWorkflowJSON(workflowId);
43
- const out = [];
44
- for (const node of json.nodes ?? []) {
45
- if (node.type !== 'n8n-nodes-base.dataTable')
46
- continue;
47
- const params = node.parameters;
48
- const operation = params?.operation;
49
- if (operation !== 'insert' && operation !== 'upsert' && operation !== 'update')
50
- continue;
51
- const ref = params?.dataTableId;
52
- let dataTableId;
53
- if (typeof ref === 'string' && ref.length > 0) {
54
- dataTableId = ref;
55
- }
56
- else if (ref &&
57
- typeof ref === 'object' &&
58
- 'value' in ref &&
59
- typeof ref.value === 'string') {
60
- const value = ref.value;
61
- if (value.length > 0)
62
- dataTableId = value;
63
- }
64
- if (!dataTableId || !node.name)
65
- continue;
66
- out.push({ nodeName: node.name, dataTableId, operation });
67
- }
68
- return out;
69
- }
70
- catch {
71
- return [];
72
- }
73
- }
74
- function extractRowIdsFromNodeOutput(nodeOutput) {
75
- const ids = [];
76
- const visit = (value) => {
77
- const inspected = outputForInspection(value);
78
- if (!inspected)
79
- return;
80
- if (Array.isArray(inspected)) {
81
- for (const item of inspected)
82
- visit(item);
83
- return;
84
- }
85
- if (!isRecord(inspected))
86
- return;
87
- const row = inspected;
88
- if (row.json !== undefined) {
89
- visit(row.json);
90
- return;
91
- }
92
- const id = row.id;
93
- if (typeof id === 'number' && Number.isFinite(id))
94
- ids.push(id);
95
- };
96
- visit(nodeOutput);
97
- return ids;
98
- }
99
38
  function getCountFromMetadata(value) {
100
- if (!isRecord(value))
39
+ if (!(0, utils_1.isRecord)(value))
101
40
  return undefined;
102
41
  for (const key of ['totalItems', '_itemCount']) {
103
42
  const count = value[key];
@@ -128,7 +67,7 @@ function previewValue(value, maxChars) {
128
67
  }
129
68
  return { preview: `${serialized.slice(0, maxChars)}...`, truncated: true };
130
69
  }
131
- function buildNodePreviews(resultData, maxChars) {
70
+ function buildNodePreviews(resultData, maxChars, simulatedNodeNames) {
132
71
  if (!resultData)
133
72
  return [];
134
73
  return Object.entries(resultData).map(([nodeName, nodeOutput]) => {
@@ -140,9 +79,26 @@ function buildNodePreviews(resultData, maxChars) {
140
79
  preview: preview.preview,
141
80
  truncated: preview.truncated,
142
81
  chars: serialized.length,
82
+ ...(simulatedNodeNames?.has(nodeName) ? { simulated: true } : {}),
143
83
  };
144
84
  });
145
85
  }
86
+ function buildVerificationPinData(buildOutcome) {
87
+ const merged = { ...(buildOutcome.verificationPinData ?? {}) };
88
+ const fixtures = buildOutcome.simulationFixtures ?? {};
89
+ const simulatedNodes = [];
90
+ for (const verdict of buildOutcome.nodeSimulationPlan ?? []) {
91
+ if (verdict.verdict !== 'simulate')
92
+ continue;
93
+ simulatedNodes.push({ nodeName: verdict.nodeName, reason: verdict.reason });
94
+ const items = fixtures[verdict.nodeName];
95
+ merged[verdict.nodeName] = items?.length ? items : [{}];
96
+ }
97
+ return {
98
+ pinData: Object.keys(merged).length > 0 ? merged : undefined,
99
+ simulatedNodes,
100
+ };
101
+ }
146
102
  function countProducedOutputRows(resultData) {
147
103
  if (!resultData)
148
104
  return undefined;
@@ -154,98 +110,6 @@ function countProducedOutputRows(resultData) {
154
110
  }
155
111
  return count;
156
112
  }
157
- const SNAPSHOT_PAGE_SIZE = 1000;
158
- const SNAPSHOT_MAX_ROWS = 100_000;
159
- async function cleanupInsertedRowsByNodeOutput(dataTableService, writeNodes, resultData, preIdsByTable, logger) {
160
- if (!resultData)
161
- return 0;
162
- const createdIdsByTable = new Map();
163
- for (const { nodeName, dataTableId, operation } of writeNodes) {
164
- if (operation !== 'insert')
165
- continue;
166
- const output = resultData[nodeName];
167
- if (!output)
168
- continue;
169
- const ids = extractRowIdsFromNodeOutput(output);
170
- if (ids.length === 0)
171
- continue;
172
- let bucket = createdIdsByTable.get(dataTableId);
173
- if (!bucket) {
174
- bucket = new Set();
175
- createdIdsByTable.set(dataTableId, bucket);
176
- }
177
- for (const id of ids)
178
- bucket.add(id);
179
- }
180
- let total = 0;
181
- for (const [dataTableId, ids] of createdIdsByTable) {
182
- const preIds = preIdsByTable.get(dataTableId);
183
- if (preIds === undefined || preIds === null) {
184
- logger.warn('Skipping data-table cleanup: pre-verify snapshot unavailable. Rows left in place to avoid deleting existing data.', { dataTableId, candidateIds: ids.size });
185
- continue;
186
- }
187
- const toDelete = [...ids].filter((id) => !preIds.has(id));
188
- if (toDelete.length === 0)
189
- continue;
190
- try {
191
- await dataTableService.deleteRows(dataTableId, {
192
- type: 'or',
193
- filters: toDelete.map((id) => ({
194
- columnName: 'id',
195
- condition: 'eq',
196
- value: id,
197
- })),
198
- });
199
- total += toDelete.length;
200
- }
201
- catch {
202
- }
203
- }
204
- return total;
205
- }
206
- async function snapshotRowIdsPerTable(dataTableService, dataTableIds, logger) {
207
- const out = new Map();
208
- for (const id of dataTableIds) {
209
- try {
210
- const bucket = new Set();
211
- let offset = 0;
212
- let truncated = false;
213
- for (;;) {
214
- const { data } = await dataTableService.queryRows(id, {
215
- limit: SNAPSHOT_PAGE_SIZE,
216
- offset,
217
- });
218
- for (const row of data) {
219
- const rid = row.id;
220
- if (typeof rid === 'number')
221
- bucket.add(rid);
222
- }
223
- if (data.length < SNAPSHOT_PAGE_SIZE)
224
- break;
225
- offset += SNAPSHOT_PAGE_SIZE;
226
- if (offset >= SNAPSHOT_MAX_ROWS) {
227
- truncated = true;
228
- break;
229
- }
230
- }
231
- if (truncated) {
232
- logger.warn('Data-table pre-verify snapshot exceeded row cap — cleanup disabled for this table', { dataTableId: id, cap: SNAPSHOT_MAX_ROWS });
233
- out.set(id, null);
234
- }
235
- else {
236
- out.set(id, bucket);
237
- }
238
- }
239
- catch (error) {
240
- logger.warn('Data-table pre-verify snapshot failed — cleanup disabled for this table', {
241
- dataTableId: id,
242
- error: error instanceof Error ? error.message : String(error),
243
- });
244
- out.set(id, null);
245
- }
246
- }
247
- return out;
248
- }
249
113
  exports.verifyBuiltWorkflowInputSchema = zod_1.z.object({
250
114
  workItemId: zod_1.z.string().describe('The work item ID from the build (wi_XXXXXXXX)'),
251
115
  workflowId: zod_1.z.string().describe('The workflow ID to verify'),
@@ -288,6 +152,19 @@ const remediationOutputSchema = zod_1.z
288
152
  attemptCount: zod_1.z.number().int().min(0).optional(),
289
153
  })
290
154
  .optional();
155
+ const CREDENTIAL_FAILURE_KEYWORDS = [
156
+ 'credential',
157
+ 'unauthorized',
158
+ 'forbidden',
159
+ '401',
160
+ '403',
161
+ 'free tier',
162
+ 'quota',
163
+ ];
164
+ const TRANSIENT_FAILURE_KEYWORDS = ['429', 'rate limit', '502', 'bad gateway', 'timed out'];
165
+ function messageMatchesAny(normalized, keywords) {
166
+ return keywords.some((keyword) => normalized.includes(keyword));
167
+ }
291
168
  function classifyVerificationFailure(error, status, buildOutcome) {
292
169
  if (buildOutcome.hasUnresolvedPlaceholders) {
293
170
  return (0, remediation_1.createRemediation)({
@@ -298,24 +175,24 @@ function classifyVerificationFailure(error, status, buildOutcome) {
298
175
  });
299
176
  }
300
177
  if (status === 'waiting') {
178
+ const hasSimulationPlan = (buildOutcome.nodeSimulationPlan?.length ?? 0) > 0;
301
179
  return (0, remediation_1.createRemediation)({
302
180
  category: 'needs_setup',
303
181
  shouldEdit: false,
304
- reason: 'execution_waiting',
305
- guidance: 'Workflow verification is waiting for user action or setup. Stop code edits and ask the user to complete setup.',
182
+ reason: hasSimulationPlan ? 'unsimulated_user_action_node' : 'execution_waiting',
183
+ guidance: hasSimulationPlan
184
+ ? 'Verification paused on a node that waits for user action and was not simulated — ' +
185
+ 'nodes downstream of it were not verified. This is not a workflow bug: do not edit ' +
186
+ 'the code. Report to the user which node paused and that the rest of the workflow ' +
187
+ 'needs a manual test.'
188
+ : 'Workflow verification is waiting for user action or setup. Stop code edits and ask the user to complete setup.',
306
189
  });
307
190
  }
308
191
  const normalized = (error ?? '').toLowerCase();
309
192
  const mockedCredentialTypeCount = buildOutcome.mockedCredentialTypes?.length ?? 0;
310
193
  const mockedNodeCount = buildOutcome.mockedNodeNames?.length ?? 0;
311
194
  const hasMockedCredentialContext = Boolean(mockedCredentialTypeCount > 0 || mockedNodeCount > 0);
312
- if (normalized.includes('credential') ||
313
- normalized.includes('unauthorized') ||
314
- normalized.includes('forbidden') ||
315
- normalized.includes('401') ||
316
- normalized.includes('403') ||
317
- normalized.includes('free tier') ||
318
- normalized.includes('quota')) {
195
+ if (messageMatchesAny(normalized, CREDENTIAL_FAILURE_KEYWORDS)) {
319
196
  return (0, remediation_1.createRemediation)({
320
197
  category: 'needs_setup',
321
198
  shouldEdit: false,
@@ -327,11 +204,7 @@ function classifyVerificationFailure(error, status, buildOutcome) {
327
204
  : 'Workflow submitted successfully, but verification requires credential or account setup. Stop code edits and route to workflows(action="setup").',
328
205
  });
329
206
  }
330
- if (normalized.includes('429') ||
331
- normalized.includes('rate limit') ||
332
- normalized.includes('502') ||
333
- normalized.includes('bad gateway') ||
334
- normalized.includes('timed out')) {
207
+ if (messageMatchesAny(normalized, TRANSIENT_FAILURE_KEYWORDS)) {
335
208
  return (0, remediation_1.createRemediation)({
336
209
  category: 'blocked',
337
210
  shouldEdit: false,
@@ -346,94 +219,281 @@ function classifyVerificationFailure(error, status, buildOutcome) {
346
219
  guidance: 'Verification found a workflow runtime failure. Diagnose it and apply one batched workflow-code repair if the guard allows it.',
347
220
  });
348
221
  }
349
- function createVerifyBuiltWorkflowTool(context) {
350
- return new agents_1.Tool('verify-built-workflow')
351
- .description('Run a built workflow using sidecar verification context from the build outcome. ' +
352
- 'Use this as the standard verifier for workflows produced by the workflow-builder. ' +
353
- 'It supports manual, schedule, form, webhook, chat, and other event triggers with build-outcome pin data, mocked credential context, or trigger-shaped inputData. ' +
354
- 'Use `executions(action="run")` only for ad hoc runs outside build verification. ' +
355
- 'CRITICAL: `inputData` shape depends on the trigger type — see the per-trigger guidance on the inputData field. ' +
356
- 'Passing the wrong shape (e.g. wrapping form fields under `formFields`) produces null downstream values that ' +
357
- 'look like an expression bug but are not — do not patch the workflow, re-run verify with the correct shape.')
358
- .input(exports.verifyBuiltWorkflowInputSchema)
359
- .output(zod_1.z.object({
360
- executionId: zod_1.z.string().optional(),
361
- success: zod_1.z.boolean(),
362
- status: zod_1.z.enum(['running', 'success', 'error', 'waiting', 'unknown']).optional(),
363
- nodesExecuted: zod_1.z.array(zod_1.z.string()).optional(),
364
- nodePreviews: zod_1.z
365
- .array(zod_1.z.object({
366
- nodeName: zod_1.z.string(),
367
- itemCount: zod_1.z.number().optional(),
368
- preview: zod_1.z.string(),
369
- truncated: zod_1.z.boolean(),
370
- chars: zod_1.z.number(),
371
- }))
372
- .optional(),
373
- data: zod_1.z.record(zod_1.z.unknown()).optional(),
374
- error: zod_1.z.string().optional(),
375
- remediation: remediationOutputSchema,
376
- guidance: zod_1.z.string().optional(),
222
+ const verifyBuiltWorkflowOutputSchema = zod_1.z.object({
223
+ executionId: zod_1.z.string().optional(),
224
+ success: zod_1.z.boolean(),
225
+ status: zod_1.z.enum(['running', 'success', 'error', 'waiting', 'unknown']).optional(),
226
+ nodesExecuted: zod_1.z.array(zod_1.z.string()).optional(),
227
+ nodePreviews: zod_1.z
228
+ .array(zod_1.z.object({
229
+ nodeName: zod_1.z.string(),
230
+ itemCount: zod_1.z.number().optional(),
231
+ preview: zod_1.z.string(),
232
+ truncated: zod_1.z.boolean(),
233
+ chars: zod_1.z.number(),
234
+ simulated: zod_1.z.boolean().optional(),
377
235
  }))
378
- .handler(async (input) => {
379
- if (!context.workflowTaskService || !context.domainContext) {
380
- const remediation = (0, remediation_1.createRemediation)({
381
- category: 'blocked',
382
- shouldEdit: false,
383
- reason: 'verification_support_unavailable',
384
- guidance: 'Verification support is not available. Stop code edits and explain the blocker.',
385
- });
386
- return { success: false, error: 'Verification support not available.', remediation };
387
- }
388
- const stateBefore = await context.workflowTaskService.getWorkflowLoopState(input.workItemId);
389
- const terminalRemediation = stateBefore?.lastRemediation && !stateBefore.lastRemediation.shouldEdit
390
- ? (0, remediation_1.terminalRemediationFromState)(stateBefore, context.runId)
391
- : undefined;
392
- if (terminalRemediation) {
393
- return {
236
+ .optional(),
237
+ simulatedNodes: zod_1.z.array(zod_1.z.object({ nodeName: zod_1.z.string(), reason: zod_1.z.string() })).optional(),
238
+ simulationNote: zod_1.z.string().optional(),
239
+ lastNodeExecuted: zod_1.z.string().optional(),
240
+ nodesNotReached: zod_1.z.array(zod_1.z.string()).optional(),
241
+ coverageNote: zod_1.z.string().optional(),
242
+ data: zod_1.z.record(zod_1.z.unknown()).optional(),
243
+ error: zod_1.z.string().optional(),
244
+ remediation: remediationOutputSchema,
245
+ guidance: zod_1.z.string().optional(),
246
+ });
247
+ function namesOrDataKeys(reachedNames, data) {
248
+ if (reachedNames.size > 0)
249
+ return [...reachedNames];
250
+ return data ? Object.keys(data) : undefined;
251
+ }
252
+ async function resolveVerifyPreconditions(input, context) {
253
+ if (!context.workflowTaskService || !context.domainContext) {
254
+ const remediation = (0, remediation_1.createRemediation)({
255
+ category: 'blocked',
256
+ shouldEdit: false,
257
+ reason: 'verification_support_unavailable',
258
+ guidance: 'Verification support is not available. Stop code edits and explain the blocker.',
259
+ });
260
+ return {
261
+ result: { success: false, error: 'Verification support not available.', remediation },
262
+ };
263
+ }
264
+ const stateBefore = await context.workflowTaskService.getWorkflowLoopState(input.workItemId);
265
+ const terminalRemediation = stateBefore?.lastRemediation && !stateBefore.lastRemediation.shouldEdit
266
+ ? (0, remediation_1.terminalRemediationFromState)(stateBefore, context.runId)
267
+ : undefined;
268
+ if (terminalRemediation) {
269
+ return {
270
+ result: {
394
271
  success: false,
395
272
  error: terminalRemediation.guidance,
396
273
  remediation: terminalRemediation,
397
274
  guidance: terminalRemediation.guidance,
398
- };
399
- }
400
- const buildOutcome = await context.workflowTaskService.getBuildOutcome(input.workItemId);
401
- if (!buildOutcome) {
402
- const remediation = (0, remediation_1.createRemediation)({
403
- category: 'blocked',
404
- shouldEdit: false,
405
- reason: 'missing_build_outcome',
406
- guidance: `No build outcome found for work item ${input.workItemId}. Stop code edits and explain the blocker.`,
407
- });
408
- return {
275
+ },
276
+ };
277
+ }
278
+ const buildOutcome = await context.workflowTaskService.getBuildOutcome(input.workItemId);
279
+ if (!buildOutcome) {
280
+ const remediation = (0, remediation_1.createRemediation)({
281
+ category: 'blocked',
282
+ shouldEdit: false,
283
+ reason: 'missing_build_outcome',
284
+ guidance: `No build outcome found for work item ${input.workItemId}. Stop code edits and explain the blocker.`,
285
+ });
286
+ return {
287
+ result: {
409
288
  success: false,
410
289
  error: `No build outcome found for work item ${input.workItemId}.`,
411
290
  remediation,
412
291
  guidance: remediation.guidance,
413
- };
414
- }
415
- if (!buildOutcome.workflowId) {
416
- return {
292
+ },
293
+ };
294
+ }
295
+ if (!buildOutcome.workflowId) {
296
+ return {
297
+ result: {
417
298
  success: false,
418
299
  error: `Build outcome ${input.workItemId} does not include a workflow ID.`,
419
- };
420
- }
421
- if (buildOutcome.workflowId !== input.workflowId) {
422
- return {
300
+ },
301
+ };
302
+ }
303
+ if (buildOutcome.workflowId !== input.workflowId) {
304
+ return {
305
+ result: {
423
306
  success: false,
424
307
  error: `Build outcome ${input.workItemId} belongs to workflow ${buildOutcome.workflowId}, ` +
425
308
  `but verification was requested for workflow ${input.workflowId}.`,
426
- };
309
+ },
310
+ };
311
+ }
312
+ return {
313
+ buildOutcome,
314
+ workflowId: buildOutcome.workflowId,
315
+ stateBefore,
316
+ workflowTaskService: context.workflowTaskService,
317
+ domainContext: context.domainContext,
318
+ };
319
+ }
320
+ async function handleMissingSimulationPlan(input, context, workflowTaskService, workflowId) {
321
+ const guidance = 'Verification was not run because the build outcome has no simulation plan. ' +
322
+ 'Rebuild or resubmit the workflow so destructive nodes can be classified before verification.';
323
+ const remediation = (0, remediation_1.createRemediation)({
324
+ category: 'blocked',
325
+ shouldEdit: false,
326
+ reason: 'missing_simulation_plan',
327
+ guidance,
328
+ });
329
+ context.logger.warn('verify-built-workflow: build outcome has no simulation plan — refusing to run without simulation safeguards', { workItemId: input.workItemId, workflowId });
330
+ try {
331
+ await workflowTaskService.updateBuildOutcome(input.workItemId, {
332
+ remediation,
333
+ verification: {
334
+ attempted: true,
335
+ success: false,
336
+ status: 'unknown',
337
+ failureSignature: 'missing_simulation_plan',
338
+ evidence: { errorMessage: guidance },
339
+ verifiedAt: new Date().toISOString(),
340
+ },
341
+ });
342
+ }
343
+ catch {
344
+ }
345
+ try {
346
+ await workflowTaskService.reportVerificationVerdict({
347
+ workItemId: input.workItemId,
348
+ runId: context.runId,
349
+ workflowId,
350
+ verdict: 'failed_terminal',
351
+ failureSignature: 'missing_simulation_plan',
352
+ diagnosis: guidance,
353
+ remediation,
354
+ summary: guidance,
355
+ });
356
+ }
357
+ catch (error) {
358
+ context.logger.warn('verify-built-workflow: failed to persist terminal verdict', {
359
+ workItemId: input.workItemId,
360
+ workflowId,
361
+ error: error instanceof Error ? error.message : String(error),
362
+ });
363
+ }
364
+ return { success: false, status: 'unknown', error: guidance, remediation, guidance };
365
+ }
366
+ async function persistVerificationRecord(input, workflowTaskService, args) {
367
+ const { success, result, reachedNames, nodesNotReached } = args;
368
+ try {
369
+ const executedForEvidence = namesOrDataKeys(reachedNames, result.data);
370
+ await workflowTaskService.updateBuildOutcome(input.workItemId, {
371
+ verification: {
372
+ attempted: true,
373
+ success,
374
+ executionId: result.executionId || undefined,
375
+ status: result.status,
376
+ failureSignature: success ? undefined : result.error,
377
+ evidence: {
378
+ nodesExecuted: executedForEvidence && executedForEvidence.length > 0 ? executedForEvidence : undefined,
379
+ nodesNotReached: nodesNotReached.length > 0 ? nodesNotReached : undefined,
380
+ producedOutputRows: countProducedOutputRows(result.data),
381
+ errorMessage: success ? undefined : result.error,
382
+ },
383
+ verifiedAt: new Date().toISOString(),
384
+ },
385
+ });
386
+ }
387
+ catch {
388
+ }
389
+ }
390
+ async function reportTerminalRemediation(input, context, workflowTaskService, args) {
391
+ const { remediation, workflowId, executionId } = args;
392
+ try {
393
+ await workflowTaskService.reportVerificationVerdict({
394
+ workItemId: input.workItemId,
395
+ runId: context.runId,
396
+ workflowId,
397
+ executionId,
398
+ verdict: remediation.category === 'needs_setup' ? 'needs_user_input' : 'failed_terminal',
399
+ failureSignature: remediation.reason,
400
+ diagnosis: remediation.guidance,
401
+ remediation,
402
+ summary: remediation.guidance,
403
+ });
404
+ }
405
+ catch (error) {
406
+ context.logger.warn('verify-built-workflow: failed to persist terminal verdict', {
407
+ workItemId: input.workItemId,
408
+ workflowId,
409
+ error: error instanceof Error ? error.message : String(error),
410
+ });
411
+ }
412
+ try {
413
+ context.trackTelemetry?.('Builder remediation guard fired', {
414
+ thread_id: context.threadId,
415
+ run_id: context.runId,
416
+ work_item_id: input.workItemId,
417
+ workflow_id: workflowId,
418
+ category: remediation.category,
419
+ attempt_count: remediation.attemptCount,
420
+ reason: remediation.reason,
421
+ });
422
+ }
423
+ catch (error) {
424
+ context.logger.warn('verify-built-workflow: failed to emit remediation telemetry', {
425
+ workItemId: input.workItemId,
426
+ workflowId,
427
+ error: error instanceof Error ? error.message : String(error),
428
+ });
429
+ }
430
+ }
431
+ function buildSimulationNote(reachedSimulatedNodes, planMissing) {
432
+ if (reachedSimulatedNodes.length > 0) {
433
+ return (`Simulated ${reachedSimulatedNodes.length} node(s) during verification — no real external writes happened: ` +
434
+ reachedSimulatedNodes.map((n) => `${n.nodeName} (${n.reason})`).join('; ') +
435
+ '. Relay this to the user when presenting the result.');
436
+ }
437
+ if (planMissing) {
438
+ return ('No simulation plan was available for this verification run — nodes were NOT ' +
439
+ 'simulated and may have performed real external writes (sent messages, created or ' +
440
+ 'modified records). Relay this to the user when presenting the result.');
441
+ }
442
+ return undefined;
443
+ }
444
+ function buildCoverageNote(nodesNotReached, result, success) {
445
+ if (nodesNotReached.length === 0)
446
+ return undefined;
447
+ const ending = result.lastNodeExecuted
448
+ ? `. Execution ended at "${result.lastNodeExecuted}"${success ? ' because it produced no output items (empty item lists stop downstream nodes)' : ''}.`
449
+ : '.';
450
+ const guidance = success
451
+ ? ' This usually means a lookup or query returned nothing. Seed matching test data and re-run verification, or tell the user the unreached part needs a manual test. Do NOT report the workflow as fully verified.'
452
+ : '';
453
+ return (`Partial coverage: ${nodesNotReached.length} node(s) were never reached and remain UNVERIFIED: ` +
454
+ nodesNotReached.join(', ') +
455
+ ending +
456
+ guidance);
457
+ }
458
+ function createVerifyBuiltWorkflowTool(context) {
459
+ return new agents_1.Tool('verify-built-workflow')
460
+ .description('Run a built workflow using sidecar verification context from the build outcome. ' +
461
+ 'Call when the current turn is responsible for post-build verification. ' +
462
+ 'Use this as the standard verifier for workflows produced by the workflow-builder. ' +
463
+ 'It supports manual, schedule, form, webhook, chat, and other event triggers with build-outcome pin data, mocked credential context, or trigger-shaped inputData. ' +
464
+ 'Use `executions(action="run")` only for ad hoc runs outside build verification. ' +
465
+ 'CRITICAL: `inputData` shape depends on the trigger type — see the per-trigger guidance on the inputData field. ' +
466
+ 'Passing the wrong shape (e.g. wrapping form fields under `formFields`) produces null downstream values that ' +
467
+ 'look like an expression bug but are not — do not patch the workflow, re-run verify with the correct shape.')
468
+ .input(exports.verifyBuiltWorkflowInputSchema)
469
+ .output(verifyBuiltWorkflowOutputSchema)
470
+ .handler(async (input) => {
471
+ const preconditions = await resolveVerifyPreconditions(input, context);
472
+ if ('result' in preconditions)
473
+ return preconditions.result;
474
+ const { buildOutcome, workflowId, stateBefore, workflowTaskService, domainContext } = preconditions;
475
+ const planMissing = buildOutcome.nodeSimulationPlan === undefined;
476
+ if (planMissing) {
477
+ return await handleMissingSimulationPlan(input, context, workflowTaskService, workflowId);
427
478
  }
428
- const workflowId = buildOutcome.workflowId;
429
- const writeNodes = await extractDataTableWriteNodes(context.domainContext.workflowService, workflowId);
430
- const preSnapshots = await snapshotRowIdsPerTable(context.domainContext.dataTableService, new Set(writeNodes.map((n) => n.dataTableId)), context.logger);
431
- const result = await context.domainContext.executionService.run(workflowId, input.inputData, {
479
+ const { pinData: verificationPinData, simulatedNodes } = buildVerificationPinData(buildOutcome);
480
+ const simulationMap = simulatedNodes.length > 0
481
+ ? Object.fromEntries(simulatedNodes.map((n) => [n.nodeName, { reason: n.reason }]))
482
+ : undefined;
483
+ const result = await domainContext.executionService.run(workflowId, input.inputData, {
432
484
  timeout: input.timeout,
433
- pinData: buildOutcome.verificationPinData,
485
+ pinData: verificationPinData,
486
+ simulation: simulationMap,
434
487
  });
488
+ const reachedNames = new Set(result.executedNodeNames ?? (result.data ? Object.keys(result.data) : []));
489
+ const reachedSimulatedNodes = simulatedNodes.filter((n) => reachedNames.has(n.nodeName));
490
+ const nodesNotReached = (buildOutcome.nodeSimulationPlan ?? [])
491
+ .map((verdict) => verdict.nodeName)
492
+ .filter((name) => !reachedNames.has(name));
493
+ const hasSimulationPlan = (buildOutcome.nodeSimulationPlan?.length ?? 0) > 0;
435
494
  const hasOutput = result.data ? Object.keys(result.data).length > 0 : false;
436
- const success = result.status === 'success' || (result.status === 'waiting' && !result.error && hasOutput);
495
+ const success = result.status === 'success' ||
496
+ (!hasSimulationPlan && result.status === 'waiting' && !result.error && hasOutput);
437
497
  const failureRemediation = success
438
498
  ? undefined
439
499
  : classifyVerificationFailure(result.error, result.status, buildOutcome);
@@ -441,82 +501,35 @@ function createVerifyBuiltWorkflowTool(context) {
441
501
  ? (0, remediation_1.terminalRemediationFromState)(stateBefore, context.runId)
442
502
  : undefined;
443
503
  const remediation = budgetRemediation ?? failureRemediation;
444
- const cleanedRows = await cleanupInsertedRowsByNodeOutput(context.domainContext.dataTableService, writeNodes, result.data, preSnapshots, context.logger);
445
- try {
446
- const nodesExecuted = result.data ? Object.keys(result.data) : undefined;
447
- await context.workflowTaskService.updateBuildOutcome(input.workItemId, {
448
- verification: {
449
- attempted: true,
450
- success,
451
- executionId: result.executionId || undefined,
452
- status: result.status,
453
- failureSignature: success ? undefined : result.error,
454
- evidence: {
455
- nodesExecuted: nodesExecuted && nodesExecuted.length > 0 ? nodesExecuted : undefined,
456
- producedOutputRows: countProducedOutputRows(result.data),
457
- errorMessage: success ? undefined : result.error,
458
- },
459
- verifiedAt: new Date().toISOString(),
460
- },
461
- });
462
- }
463
- catch {
464
- }
465
- if (cleanedRows > 0) {
466
- context.logger.debug?.('verify-built-workflow: cleaned up inserted rows', {
467
- workItemId: input.workItemId,
504
+ await persistVerificationRecord(input, workflowTaskService, {
505
+ success,
506
+ result,
507
+ reachedNames,
508
+ nodesNotReached,
509
+ });
510
+ if (remediation && !remediation.shouldEdit) {
511
+ await reportTerminalRemediation(input, context, workflowTaskService, {
512
+ remediation,
468
513
  workflowId,
469
- cleanedRows,
514
+ executionId: result.executionId || undefined,
470
515
  });
471
516
  }
472
- if (remediation && !remediation.shouldEdit) {
473
- try {
474
- await context.workflowTaskService.reportVerificationVerdict({
475
- workItemId: input.workItemId,
476
- runId: context.runId,
477
- workflowId,
478
- executionId: result.executionId || undefined,
479
- verdict: remediation.category === 'needs_setup' ? 'needs_user_input' : 'failed_terminal',
480
- failureSignature: remediation.reason,
481
- diagnosis: remediation.guidance,
482
- remediation,
483
- summary: remediation.guidance,
484
- });
485
- }
486
- catch (error) {
487
- context.logger.warn('verify-built-workflow: failed to persist terminal verdict', {
488
- workItemId: input.workItemId,
489
- workflowId,
490
- error: error instanceof Error ? error.message : String(error),
491
- });
492
- }
493
- try {
494
- context.trackTelemetry?.('Builder remediation guard fired', {
495
- thread_id: context.threadId,
496
- run_id: context.runId,
497
- work_item_id: input.workItemId,
498
- workflow_id: workflowId,
499
- category: remediation.category,
500
- attempt_count: remediation.attemptCount,
501
- reason: remediation.reason,
502
- });
503
- }
504
- catch (error) {
505
- context.logger.warn('verify-built-workflow: failed to emit remediation telemetry', {
506
- workItemId: input.workItemId,
507
- workflowId,
508
- error: error instanceof Error ? error.message : String(error),
509
- });
510
- }
511
- }
512
517
  const maxDataChars = input.maxDataChars ?? DEFAULT_NODE_PREVIEW_CHARS;
513
- const nodesExecuted = result.data ? Object.keys(result.data) : undefined;
518
+ const nodesExecuted = namesOrDataKeys(reachedNames, result.data);
519
+ const simulatedNames = new Set(reachedSimulatedNodes.map((n) => n.nodeName));
520
+ const simulationNote = buildSimulationNote(reachedSimulatedNodes, planMissing);
521
+ const coverageNote = buildCoverageNote(nodesNotReached, result, success);
514
522
  return {
515
523
  executionId: result.executionId || undefined,
516
524
  success,
517
525
  status: result.status,
518
526
  nodesExecuted,
519
- nodePreviews: buildNodePreviews(result.data, maxDataChars),
527
+ lastNodeExecuted: result.lastNodeExecuted,
528
+ nodePreviews: buildNodePreviews(result.data, maxDataChars, simulatedNames),
529
+ simulatedNodes: reachedSimulatedNodes.length > 0 ? reachedSimulatedNodes : undefined,
530
+ simulationNote,
531
+ nodesNotReached: nodesNotReached.length > 0 ? nodesNotReached : undefined,
532
+ coverageNote,
520
533
  ...(input.includeData ? { data: result.data } : {}),
521
534
  error: result.error,
522
535
  remediation,