@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
@@ -5,22 +5,23 @@ exports.createBuildWorkflowTool = createBuildWorkflowTool;
5
5
  const agents_1 = require("@n8n/agents");
6
6
  const api_types_1 = require("@n8n/api-types");
7
7
  const utils_1 = require("@n8n/utils");
8
- const workflow_sdk_1 = require("@n8n/workflow-sdk");
9
8
  const nanoid_1 = require("nanoid");
10
9
  const zod_1 = require("zod");
10
+ const plan_verification_simulation_1 = require("./plan-verification-simulation");
11
11
  const resolve_credentials_1 = require("./resolve-credentials");
12
12
  const setup_workflow_service_1 = require("./setup-workflow.service");
13
- const submit_workflow_tool_1 = require("./submit-workflow.tool");
13
+ const workflow_build_context_1 = require("./workflow-build-context");
14
+ const workflow_build_remediation_1 = require("./workflow-build-remediation");
15
+ const workflow_build_reporting_1 = require("./workflow-build-reporting");
16
+ const workflow_build_routing_1 = require("./workflow-build-routing");
17
+ const workflow_build_telemetry_1 = require("./workflow-build-telemetry");
18
+ const workflow_file_bindings_1 = require("./workflow-file-bindings");
14
19
  const workflow_json_utils_1 = require("./workflow-json-utils");
15
- const workflow_builder_1 = require("../../workflow-builder");
20
+ const workflow_source_compiler_1 = require("./workflow-source-compiler");
21
+ const workflow_validation_warnings_1 = require("./workflow-validation-warnings");
16
22
  const build_failure_tracker_1 = require("../../workflow-builder/build-failure-tracker");
17
- const extract_code_1 = require("../../workflow-builder/extract-code");
18
- const patch_code_1 = require("../../workflow-builder/patch-code");
19
23
  const remediation_1 = require("../../workflow-loop/remediation");
20
- const patchSchema = zod_1.z.object({
21
- old_str: zod_1.z.string().describe('Exact string to find in the code'),
22
- new_str: zod_1.z.string().describe('Replacement string'),
23
- });
24
+ const workflow_loop_state_1 = require("../../workflow-loop/workflow-loop-state");
24
25
  const confirmationSuspendSchema = zod_1.z.object({
25
26
  requestId: zod_1.z.string(),
26
27
  message: zod_1.z.string(),
@@ -29,27 +30,25 @@ const confirmationSuspendSchema = zod_1.z.object({
29
30
  const confirmationResumeSchema = zod_1.z.object({
30
31
  approved: zod_1.z.boolean(),
31
32
  });
32
- function coercePatches(value) {
33
- if (typeof value !== 'string')
34
- return value;
35
- try {
36
- return JSON.parse(value);
37
- }
38
- catch {
39
- return value;
40
- }
41
- }
42
- exports.buildWorkflowInputSchema = zod_1.z.object({
43
- code: zod_1.z
33
+ exports.buildWorkflowInputSchema = zod_1.z
34
+ .object({
35
+ filePath: zod_1.z
36
+ .string()
37
+ .min(1)
38
+ .refine((value) => {
39
+ try {
40
+ (0, workflow_file_bindings_1.normalizeWorkflowSourceFilePath)(value);
41
+ return true;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }, { message: 'Workflow source file path must stay within the workspace root.' })
47
+ .describe('Workspace path to the workflow source file to build. Supports TypeScript SDK files and WorkflowJSON .json files.'),
48
+ workflowId: zod_1.z
44
49
  .string()
45
50
  .optional()
46
- .describe('Full TypeScript workflow code using @n8n/workflow-sdk. Required for new workflows.'),
47
- patches: zod_1.z
48
- .preprocess(coercePatches, zod_1.z.array(patchSchema))
49
- .optional()
50
- .describe('Array of {old_str, new_str} replacements to apply to existing workflow code. ' +
51
- 'Requires workflowId. More efficient than resending full code for small fixes.'),
52
- workflowId: zod_1.z.string().optional().describe('Existing workflow ID to update (omit to create new)'),
51
+ .describe('Existing workflow ID to bind this file to on the first update. Once bound, omit this on retries.'),
53
52
  projectId: zod_1.z
54
53
  .string()
55
54
  .optional()
@@ -58,13 +57,14 @@ exports.buildWorkflowInputSchema = zod_1.z.object({
58
57
  workItemId: zod_1.z
59
58
  .string()
60
59
  .optional()
61
- .describe('Existing workflow-loop work item ID when patching a workflow from verification guidance.'),
60
+ .describe('Optional workflow-loop work item ID when repairing a workflow.'),
62
61
  isSupportingWorkflow: zod_1.z
63
62
  .boolean()
64
63
  .optional()
65
64
  .describe('Set true when saving a supporting sub-workflow that will be referenced by the main workflow. ' +
66
65
  'In a planned build task, this completes the task only when the task itself is marked isSupportingWorkflow; otherwise save the main workflow later.'),
67
- });
66
+ })
67
+ .strict();
68
68
  const triggerNodeOutputSchema = zod_1.z.object({
69
69
  nodeName: zod_1.z.string(),
70
70
  nodeType: zod_1.z.string(),
@@ -95,165 +95,17 @@ const setupRequirementOutputSchema = zod_1.z.discriminatedUnion('status', [
95
95
  guidance: zod_1.z.string(),
96
96
  }),
97
97
  ]);
98
- function hasMockedCredentials(outcome) {
99
- return ((outcome.mockedCredentialTypes?.length ?? 0) > 0 ||
100
- Object.keys(outcome.mockedCredentialsByNode ?? {}).length > 0);
101
- }
102
- function hasCredentialVerificationData(outcome) {
103
- return (Object.keys(outcome.verificationPinData ?? {}).length > 0 ||
104
- outcome.usesWorkflowPinDataForVerification === true);
105
- }
106
- function getBuildFailureTrackingKey({ workItemId, workflowId, workflowName, isAuxiliarySupportingWorkflow, buildContext, runId, }) {
107
- if (workItemId)
108
- return workItemId;
109
- if (isAuxiliarySupportingWorkflow) {
110
- return [
111
- 'supporting-workflow',
112
- buildContext?.taskId ?? (runId ? `run:${runId}` : 'unknown-run'),
113
- workflowId ?? workflowName ?? 'new',
114
- ].join(':');
115
- }
116
- return (buildContext?.workItemId ??
117
- buildContext?.taskId ??
118
- workflowId ??
119
- workflowName ??
120
- `run:${runId ?? 'unknown'}`);
121
- }
122
- function determineVerificationReadiness(outcome) {
123
- if (!outcome.submitted) {
124
- return {
125
- status: 'not_verifiable',
126
- reason: 'not-submitted',
127
- guidance: 'The build did not submit a workflow, so there is nothing to verify.',
128
- };
129
- }
130
- if (!outcome.workflowId) {
131
- return {
132
- status: 'not_verifiable',
133
- reason: 'missing-workflow-id',
134
- guidance: 'The build outcome does not include a workflow ID.',
135
- };
136
- }
137
- if (outcome.hasUnresolvedPlaceholders) {
138
- return {
139
- status: 'needs_setup',
140
- reason: 'unresolved-placeholders',
141
- guidance: 'Route the workflow through setup before verification.',
142
- };
143
- }
144
- if (hasMockedCredentials(outcome) && !hasCredentialVerificationData(outcome)) {
145
- return {
146
- status: 'needs_setup',
147
- reason: 'missing-mocked-credential-pin-data',
148
- guidance: 'Route the workflow through setup because mocked credentials cannot be verified.',
149
- };
150
- }
151
- if (!outcome.triggerNodes?.some((node) => (0, workflow_json_utils_1.isMockableTriggerNodeType)(node.nodeType))) {
152
- return {
153
- status: 'not_verifiable',
154
- reason: 'non-mockable-trigger',
155
- guidance: 'The workflow does not have a trigger the post-build verifier can exercise.',
156
- };
157
- }
158
- return { status: 'ready' };
159
- }
160
- function determineSetupRequirement(outcome) {
161
- if (!outcome.submitted || !outcome.workflowId) {
162
- return { status: 'not_required' };
163
- }
164
- if (outcome.hasUnresolvedPlaceholders) {
165
- return {
166
- status: 'required',
167
- reason: 'unresolved-placeholders',
168
- guidance: 'Route the workflow through setup so the user can fill unresolved values.',
169
- };
170
- }
171
- if (hasMockedCredentials(outcome)) {
172
- return {
173
- status: 'required',
174
- reason: 'mocked-credentials',
175
- guidance: 'Route the workflow through setup so the user can add real credentials.',
176
- };
177
- }
178
- return { status: 'not_required' };
179
- }
180
- function withDeterministicRouting(outcome) {
181
- return {
182
- ...outcome,
183
- verificationReadiness: determineVerificationReadiness(outcome),
184
- setupRequirement: determineSetupRequirement(outcome),
185
- };
186
- }
187
- function isApprovedBuildContext(context) {
188
- const buildContext = context.workflowBuildContext;
189
- return Boolean(buildContext?.plannedTaskService ?? buildContext?.allowPostPlanWorkflowCreate);
190
- }
191
- async function resolveWorkflowName(context, workflowId) {
192
- try {
193
- return (await context.workflowService.getAsWorkflowJSON(workflowId)).name || 'workflow';
194
- }
195
- catch {
196
- return 'workflow';
197
- }
198
- }
199
- async function reportWorkflowBuildOutcome(context, outcome, options = {}) {
200
- const buildContext = context.workflowBuildContext;
201
- if (!buildContext)
202
- return;
203
- if (options.storeOnRunContext !== false) {
204
- try {
205
- await buildContext.onBuildOutcome?.(outcome);
206
- }
207
- catch (error) {
208
- context.logger?.warn('Failed to store workflow build outcome on run context', {
209
- error: error instanceof Error ? error.message : String(error),
210
- });
211
- }
212
- }
213
- try {
214
- await buildContext.workflowTaskService?.reportBuildOutcome(outcome);
215
- }
216
- catch (error) {
217
- context.logger?.warn('Failed to report workflow build outcome to workflow loop', {
218
- workItemId: outcome.workItemId,
219
- error: error instanceof Error ? error.message : String(error),
220
- });
221
- }
222
- if (options.markPlannedTaskSucceeded === false)
223
- return;
224
- try {
225
- await buildContext.plannedTaskService?.markSucceeded(buildContext.threadId, buildContext.taskId, {
226
- result: outcome.summary,
227
- outcome,
228
- });
229
- }
230
- catch (error) {
231
- context.logger?.warn('Failed to mark planned workflow build task succeeded', {
232
- taskId: buildContext.taskId,
233
- error: error instanceof Error ? error.message : String(error),
234
- });
235
- }
236
- }
237
- async function promoteMainWorkflow(context, workflowId) {
238
- try {
239
- await context.workflowService.clearAiTemporary(workflowId);
240
- }
241
- catch (error) {
242
- context.logger?.warn(`Failed to clear AI-builder temporary marker on main workflow ${workflowId}: ${error instanceof Error ? error.message : String(error)}`);
243
- }
244
- }
245
98
  function createBuildWorkflowTool(context) {
246
- let lastCode = null;
247
- let lastCodeVersionId = null;
248
99
  const failureTracker = new build_failure_tracker_1.BuildFailureTracker();
249
100
  return new agents_1.Tool('build-workflow')
250
- .description('Build a workflow from TypeScript SDK code. Two modes:\n' +
251
- '1. Full code: pass `code` to create/update a workflow from scratch.\n' +
252
- '2. Patch mode: pass `patches` (+ optional `workflowId`) to apply str_replace fixes. ' +
253
- 'Patches apply to last submitted code, or auto-fetch from saved workflow if workflowId given.')
101
+ .description('Build and save a workflow from a workspace source file. ' +
102
+ 'Use TypeScript SDK source for new workflows, or WorkflowJSON .json source for existing workflow edits. ' +
103
+ 'Write or edit the file with workspace file tools, then pass its filePath here.')
254
104
  .input(exports.buildWorkflowInputSchema)
255
105
  .output(zod_1.z.object({
256
106
  success: zod_1.z.boolean(),
107
+ filePath: zod_1.z.string(),
108
+ sourceHash: zod_1.z.string().optional(),
257
109
  workflowId: zod_1.z.string().optional(),
258
110
  workflowName: zod_1.z.string().optional(),
259
111
  workItemId: zod_1.z.string().optional(),
@@ -264,53 +116,189 @@ function createBuildWorkflowTool(context) {
264
116
  mockedNodeNames: zod_1.z.array(zod_1.z.string()).optional(),
265
117
  mockedCredentialTypes: zod_1.z.array(zod_1.z.string()).optional(),
266
118
  mockedCredentialsByNode: zod_1.z.record(zod_1.z.array(zod_1.z.string())).optional(),
267
- verificationPinData: zod_1.z.record(zod_1.z.array(zod_1.z.record(zod_1.z.unknown()))).optional(),
268
- usesWorkflowPinDataForVerification: zod_1.z.boolean().optional(),
269
119
  referencedWorkflowIds: zod_1.z.array(zod_1.z.string()).optional(),
270
120
  hasUnresolvedPlaceholders: zod_1.z.boolean().optional(),
271
121
  denied: zod_1.z.boolean().optional(),
272
122
  reason: zod_1.z.string().optional(),
123
+ remediation: workflow_loop_state_1.remediationMetadataSchema.optional(),
273
124
  errors: zod_1.z.array(zod_1.z.string()).optional(),
274
125
  warnings: zod_1.z.array(zod_1.z.string()).optional(),
275
126
  }))
276
127
  .suspend(confirmationSuspendSchema)
277
128
  .resume(confirmationResumeSchema)
278
129
  .handler(async (input, ctx) => {
279
- const permKey = input.workflowId ? 'updateWorkflow' : 'createWorkflow';
130
+ const filePath = (0, workflow_file_bindings_1.normalizeWorkflowSourceFilePath)(input.filePath);
131
+ let binding = (await (0, workflow_file_bindings_1.getWorkflowSourceFileBinding)(context, filePath)) ?? { filePath };
132
+ if (input.workflowId && binding.workflowId && input.workflowId !== binding.workflowId) {
133
+ const remediation = (0, remediation_1.createRemediation)({
134
+ category: 'blocked',
135
+ shouldEdit: false,
136
+ reason: 'source_file_workflow_mismatch',
137
+ guidance: 'This source file is already bound to a different workflow. Use the bound workflow or start from a different filePath.',
138
+ });
139
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
140
+ result: 'blocked',
141
+ stage: 'permission',
142
+ binding,
143
+ targetWorkflowId: binding.workflowId,
144
+ remediation,
145
+ errorCount: 1,
146
+ });
147
+ return {
148
+ success: false,
149
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
150
+ workflowId: binding.workflowId,
151
+ errors: [
152
+ `Source file ${filePath} is already bound to workflow ${binding.workflowId}; cannot bind it to ${input.workflowId}.`,
153
+ ],
154
+ remediation,
155
+ };
156
+ }
157
+ if (input.workflowId && !binding.workflowId) {
158
+ binding = await (0, workflow_file_bindings_1.saveWorkflowSourceFileBinding)(context, {
159
+ ...binding,
160
+ workflowId: input.workflowId,
161
+ });
162
+ }
163
+ const targetWorkflowId = binding.workflowId;
164
+ const permKey = targetWorkflowId ? 'updateWorkflow' : 'createWorkflow';
280
165
  if (context.permissions?.[permKey] === 'blocked') {
281
- return { success: false, errors: ['Action blocked by admin'] };
166
+ const remediation = (0, remediation_1.createRemediation)({
167
+ category: 'blocked',
168
+ shouldEdit: false,
169
+ reason: 'permission_blocked',
170
+ guidance: 'The requested workflow save action is blocked by admin policy.',
171
+ });
172
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
173
+ result: 'blocked',
174
+ stage: 'permission',
175
+ binding,
176
+ targetWorkflowId,
177
+ isSupportingWorkflow: input.isSupportingWorkflow,
178
+ remediation,
179
+ errorCount: 1,
180
+ });
181
+ return {
182
+ success: false,
183
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
184
+ workflowId: targetWorkflowId,
185
+ errors: ['Action blocked by admin'],
186
+ remediation,
187
+ };
282
188
  }
283
- if (input.workflowId &&
284
- !isApprovedBuildContext(context) &&
189
+ if (targetWorkflowId &&
190
+ !(0, workflow_build_context_1.isApprovedBuildContext)(context) &&
285
191
  context.permissions?.updateWorkflow !== 'always_allow') {
286
192
  if (ctx.resumeData && !ctx.resumeData.approved) {
193
+ const remediation = (0, remediation_1.createRemediation)({
194
+ category: 'blocked',
195
+ shouldEdit: false,
196
+ reason: 'user_denied',
197
+ guidance: 'The user denied permission to edit this workflow.',
198
+ });
199
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
200
+ result: 'denied',
201
+ stage: 'hitl',
202
+ binding,
203
+ targetWorkflowId,
204
+ isSupportingWorkflow: input.isSupportingWorkflow,
205
+ remediation,
206
+ errorCount: 1,
207
+ });
287
208
  return {
288
209
  success: false,
210
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
211
+ workflowId: targetWorkflowId,
289
212
  denied: true,
290
213
  reason: 'User denied the action',
291
214
  errors: ['User denied the action'],
215
+ remediation,
292
216
  };
293
217
  }
294
218
  if (!ctx.resumeData) {
295
219
  if (!ctx.suspend) {
296
- return { success: false, errors: ['Workflow edit approval is required.'] };
220
+ const remediation = (0, remediation_1.createRemediation)({
221
+ category: 'blocked',
222
+ shouldEdit: false,
223
+ reason: 'approval_required',
224
+ guidance: 'Workflow edit approval is required before saving this source file.',
225
+ });
226
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
227
+ result: 'blocked',
228
+ stage: 'hitl',
229
+ binding,
230
+ targetWorkflowId,
231
+ isSupportingWorkflow: input.isSupportingWorkflow,
232
+ remediation,
233
+ errorCount: 1,
234
+ });
235
+ return {
236
+ success: false,
237
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
238
+ workflowId: targetWorkflowId,
239
+ errors: ['Workflow edit approval is required.'],
240
+ remediation,
241
+ };
297
242
  }
298
- const workflowName = await resolveWorkflowName(context, input.workflowId);
243
+ const workflowName = await (0, workflow_build_context_1.resolveWorkflowName)(context, targetWorkflowId);
244
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
245
+ result: 'suspended',
246
+ stage: 'hitl',
247
+ binding,
248
+ targetWorkflowId,
249
+ isSupportingWorkflow: input.isSupportingWorkflow,
250
+ });
299
251
  return await ctx.suspend({
300
252
  requestId: (0, nanoid_1.nanoid)(),
301
- message: `Edit ${workflowName} (ID: ${input.workflowId})?`,
253
+ message: `Edit ${workflowName} (ID: ${targetWorkflowId})?`,
302
254
  severity: 'warning',
303
255
  });
304
256
  }
305
257
  }
306
- const { code, patches, workflowId, projectId, name, workItemId } = input;
258
+ let sourceCode;
259
+ let sourceHash;
260
+ try {
261
+ ({ source: sourceCode, sourceHash } = await (0, workflow_file_bindings_1.readWorkflowSourceFile)(context, filePath));
262
+ }
263
+ catch (error) {
264
+ const remediation = (0, workflow_build_remediation_1.createCodeFixableRemediation)({
265
+ reason: 'workflow_source_read_failed',
266
+ guidance: 'The workflow source file could not be read. Recreate or edit the returned filePath, then call build-workflow again with the same filePath.',
267
+ });
268
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
269
+ result: 'failure',
270
+ stage: 'source_read',
271
+ binding,
272
+ targetWorkflowId,
273
+ isSupportingWorkflow: input.isSupportingWorkflow,
274
+ remediation,
275
+ errorCount: 1,
276
+ });
277
+ return {
278
+ success: false,
279
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
280
+ workflowId: targetWorkflowId,
281
+ errors: [error instanceof Error ? error.message : String(error)],
282
+ remediation,
283
+ };
284
+ }
285
+ if (sourceHash !== binding.sourceHash) {
286
+ binding = await (0, workflow_file_bindings_1.saveWorkflowSourceFileBinding)(context, { ...binding, sourceHash });
287
+ }
288
+ const { projectId, name } = input;
307
289
  const isSupportingWorkflow = input.isSupportingWorkflow === true;
308
290
  const buildContext = context.workflowBuildContext;
309
- const isAuxiliarySupportingWorkflow = isSupportingWorkflow && buildContext?.isSupportingWorkflowTask !== true;
310
- const workItemKey = getBuildFailureTrackingKey({
311
- workItemId,
312
- workflowId,
291
+ const { isAuxiliarySupportingWorkflow, plannedTaskId, owner, resolvedWorkItemId, resolvedTaskId, } = (0, workflow_build_context_1.resolveBuildIdentifiers)({
292
+ context,
293
+ filePath,
294
+ inputWorkItemId: input.workItemId,
295
+ isSupportingWorkflow,
296
+ });
297
+ const workItemKey = (0, workflow_build_context_1.getBuildFailureTrackingKey)({
298
+ workItemId: resolvedWorkItemId,
299
+ workflowId: targetWorkflowId,
313
300
  workflowName: name,
301
+ filePath,
314
302
  isAuxiliarySupportingWorkflow,
315
303
  buildContext,
316
304
  runId: context.runId,
@@ -319,101 +307,139 @@ function createBuildWorkflowTool(context) {
319
307
  const escalation = failureTracker.record(workItemKey, errors, options);
320
308
  return escalation ? [...errors, escalation] : errors;
321
309
  };
322
- let finalCode;
323
- if (patches) {
324
- if (lastCode && lastCodeVersionId && workflowId) {
325
- try {
326
- const head = await context.workflowService.getWorkflowHead(workflowId);
327
- if (head.versionId !== lastCodeVersionId) {
328
- lastCode = null;
329
- lastCodeVersionId = null;
330
- }
331
- }
332
- catch {
333
- }
334
- }
335
- let baseCode = lastCode;
336
- if (!baseCode && workflowId) {
337
- try {
338
- const snapshot = await context.workflowService.getWorkflowSnapshot(workflowId);
339
- baseCode = (0, workflow_sdk_1.generateWorkflowCode)(snapshot.json);
340
- lastCode = baseCode;
341
- lastCodeVersionId = snapshot.versionId;
342
- }
343
- catch {
344
- return {
345
- success: false,
346
- errors: [
347
- 'Patch mode: no previous code and could not fetch workflow. Send full code instead.',
348
- ],
349
- };
350
- }
351
- }
352
- if (!baseCode) {
353
- return {
354
- success: false,
355
- errors: [
356
- 'Patch mode requires either a previous build-workflow call or a workflowId to fetch from.',
357
- ],
358
- };
359
- }
360
- const patchResult = (0, patch_code_1.applyPatches)(baseCode, patches);
361
- if (!patchResult.success) {
362
- return { success: false, errors: [patchResult.error] };
363
- }
364
- finalCode = patchResult.code;
365
- }
366
- else if (code) {
367
- finalCode = (0, extract_code_1.extractWorkflowCode)(code);
368
- }
369
- else {
370
- return {
371
- success: false,
372
- errors: ['Either `code` (full code) or `patches` (to fix previous code) is required.'],
373
- };
374
- }
375
- lastCode = finalCode;
376
- let result;
377
- try {
378
- result = (0, workflow_builder_1.parseAndValidate)(finalCode, {
379
- nodeTypesProvider: context.nodeTypesProvider,
310
+ let informational = [];
311
+ const compiled = await (0, workflow_source_compiler_1.compileWorkflowSource)(context, filePath, sourceCode);
312
+ if (!compiled.success) {
313
+ const errors = compiled.editable ? withEscalation(compiled.errors) : compiled.errors;
314
+ const remediation = (0, workflow_build_remediation_1.createSourceCompileRemediation)({
315
+ reason: compiled.reason,
316
+ editable: compiled.editable,
317
+ });
318
+ binding = await (0, workflow_build_context_1.markSourceBuildFailed)(context, binding, sourceHash);
319
+ await (0, workflow_build_reporting_1.reportFailedWorkflowBuildOutcome)(context, {
320
+ targetWorkflowId,
321
+ sourceFilePath: filePath,
322
+ workItemId: resolvedWorkItemId,
323
+ taskId: resolvedTaskId,
324
+ plannedTaskId,
325
+ owner,
326
+ remediation,
327
+ errors,
328
+ summary: compiled.summary,
329
+ storeOnRunContext: !isAuxiliarySupportingWorkflow,
330
+ });
331
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
332
+ result: remediation.category === 'blocked' ? 'blocked' : 'failure',
333
+ stage: 'parse',
334
+ binding,
335
+ targetWorkflowId,
336
+ isSupportingWorkflow,
337
+ isAuxiliarySupportingWorkflow,
338
+ remediation,
339
+ errorCount: errors.length,
380
340
  });
381
- }
382
- catch (error) {
383
341
  return {
384
342
  success: false,
385
- errors: withEscalation([error instanceof Error ? error.message : 'Failed to parse workflow code'], {
386
- includeSdkLanguageGuidance: true,
387
- }),
343
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
344
+ workflowId: targetWorkflowId,
345
+ workItemId: resolvedWorkItemId,
346
+ errors,
347
+ remediation,
388
348
  };
389
349
  }
390
- const { errors, informational } = (0, workflow_builder_1.partitionWarnings)(result.warnings);
391
- if (errors.length > 0) {
350
+ const partitionedWarnings = (0, workflow_validation_warnings_1.partitionWarnings)(compiled.warnings);
351
+ informational = partitionedWarnings.informational;
352
+ if (partitionedWarnings.errors.length > 0) {
353
+ const formattedErrors = withEscalation(partitionedWarnings.errors.map((e) => `[${e.code}]${e.nodeName ? ` (${e.nodeName})` : ''}: ${e.message}`));
354
+ const remediation = (0, workflow_build_remediation_1.createCodeFixableRemediation)({
355
+ reason: 'workflow_source_validation_failed',
356
+ guidance: 'Edit the workspace source file using the validation diagnostics, then call build-workflow again with the same filePath.',
357
+ });
358
+ binding = await (0, workflow_build_context_1.markSourceBuildFailed)(context, binding, sourceHash);
359
+ await (0, workflow_build_reporting_1.reportFailedWorkflowBuildOutcome)(context, {
360
+ targetWorkflowId,
361
+ sourceFilePath: filePath,
362
+ workItemId: resolvedWorkItemId,
363
+ taskId: resolvedTaskId,
364
+ plannedTaskId,
365
+ owner,
366
+ remediation,
367
+ errors: formattedErrors,
368
+ summary: 'Workflow source failed validation.',
369
+ storeOnRunContext: !isAuxiliarySupportingWorkflow,
370
+ });
371
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
372
+ result: 'failure',
373
+ stage: 'validation',
374
+ binding,
375
+ targetWorkflowId,
376
+ isSupportingWorkflow,
377
+ isAuxiliarySupportingWorkflow,
378
+ remediation,
379
+ errorCount: formattedErrors.length,
380
+ warningCount: informational.length,
381
+ });
392
382
  return {
393
383
  success: false,
394
- errors: withEscalation(errors.map((e) => `[${e.code}]${e.nodeName ? ` (${e.nodeName})` : ''}: ${e.message}`)),
395
- warnings: informational.length > 0
396
- ? informational.map((w) => `[${w.code}]: ${w.message}`)
397
- : undefined,
384
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
385
+ workflowId: targetWorkflowId,
386
+ workItemId: resolvedWorkItemId,
387
+ errors: formattedErrors,
388
+ remediation,
389
+ warnings: (0, workflow_build_context_1.combineWarnings)(informational.map((w) => (0, workflow_build_context_1.formatWarning)(w.code, w.message))),
398
390
  };
399
391
  }
400
- const json = result.workflow;
392
+ const json = compiled.workflow;
401
393
  if (name) {
402
394
  json.name = name;
403
395
  }
404
- else if (!json.name && !workflowId) {
396
+ else if (!json.name && !targetWorkflowId) {
397
+ const remediation = (0, workflow_build_remediation_1.createCodeFixableRemediation)({
398
+ reason: 'workflow_name_missing',
399
+ guidance: 'Add a workflow name in the workspace source file or pass the name parameter, then call build-workflow again with the same filePath.',
400
+ });
401
+ binding = await (0, workflow_build_context_1.markSourceBuildFailed)(context, binding, sourceHash);
402
+ await (0, workflow_build_reporting_1.reportFailedWorkflowBuildOutcome)(context, {
403
+ targetWorkflowId,
404
+ sourceFilePath: filePath,
405
+ workItemId: resolvedWorkItemId,
406
+ taskId: resolvedTaskId,
407
+ plannedTaskId,
408
+ owner,
409
+ remediation,
410
+ errors: [
411
+ 'Workflow name is required for new workflows. Provide a name parameter or set it in the SDK code.',
412
+ ],
413
+ summary: 'Workflow source is missing a workflow name.',
414
+ storeOnRunContext: !isAuxiliarySupportingWorkflow,
415
+ });
416
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
417
+ result: 'failure',
418
+ stage: 'name',
419
+ binding,
420
+ targetWorkflowId,
421
+ isSupportingWorkflow,
422
+ isAuxiliarySupportingWorkflow,
423
+ remediation,
424
+ errorCount: 1,
425
+ });
405
426
  return {
406
427
  success: false,
428
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
429
+ workflowId: targetWorkflowId,
430
+ workItemId: resolvedWorkItemId,
407
431
  errors: [
408
432
  'Workflow name is required for new workflows. Provide a name parameter or set it in the SDK code.',
409
433
  ],
434
+ remediation,
410
435
  };
411
436
  }
412
437
  const credentialMap = await (0, resolve_credentials_1.buildCredentialMap)(context.credentialService);
413
- const mockResult = await (0, resolve_credentials_1.resolveCredentials)(json, workflowId, context, credentialMap);
438
+ const mockResult = await (0, resolve_credentials_1.resolveCredentials)(json, targetWorkflowId, context, credentialMap);
414
439
  await (0, setup_workflow_service_1.stripStaleCredentialsFromWorkflow)(context, json);
415
- await (0, submit_workflow_tool_1.ensureWebhookIds)(json, workflowId, context);
416
440
  try {
441
+ await (0, workflow_json_utils_1.ensureWebhookIds)(json, targetWorkflowId, context);
442
+ await (0, workflow_json_utils_1.preserveExistingNodeGroupIds)(json, targetWorkflowId, context);
417
443
  const hasMockedCredentialNodes = mockResult.mockedNodeNames.length > 0;
418
444
  const referencedWorkflowIds = (0, workflow_json_utils_1.getReferencedWorkflowIds)(json);
419
445
  const triggerNodes = (json.nodes ?? [])
@@ -421,23 +447,16 @@ function createBuildWorkflowTool(context) {
421
447
  .map((n) => ({ nodeName: n.name, nodeType: n.type }))
422
448
  .filter((t) => Boolean(t.nodeName) && Boolean(t.nodeType));
423
449
  const hasPlaceholders = (json.nodes ?? []).some((n) => (0, utils_1.hasPlaceholderDeep)(n.parameters));
424
- const plannedTaskId = buildContext?.plannedTaskService && !isAuxiliarySupportingWorkflow
425
- ? buildContext.taskId
426
- : undefined;
427
- const owner = plannedTaskId
428
- ? { type: 'planned', taskId: plannedTaskId }
429
- : { type: 'direct' };
430
- const resolvedWorkItemId = workItemId ??
431
- (isAuxiliarySupportingWorkflow ? undefined : buildContext?.workItemId) ??
432
- `wi_${(0, nanoid_1.nanoid)(8)}`;
433
- const resolvedTaskId = isAuxiliarySupportingWorkflow
434
- ? `${buildContext?.taskId ?? (context.runId ? `build-${context.runId}` : 'build')}:supporting-${(0, nanoid_1.nanoid)(6)}`
435
- : (buildContext?.taskId ??
436
- (context.runId ? `build-${context.runId}` : `build-${(0, nanoid_1.nanoid)(8)}`));
437
- const createSuccessResponse = async (savedId) => {
450
+ const createSuccessResponse = async (saved, operation) => {
451
+ const { nodeSimulationPlan, simulationFixtures } = await (0, plan_verification_simulation_1.planVerificationSimulation)({
452
+ workflow: json,
453
+ mockedNodeNames: mockResult.mockedNodeNames,
454
+ workflowId: saved.id,
455
+ logger: context.logger,
456
+ });
438
457
  const runId = buildContext?.runId ?? context.runId;
439
458
  const workflowName = json.name || 'workflow';
440
- const summary = `${workflowId ? 'Updated' : 'Created'} ${isSupportingWorkflow ? 'supporting ' : ''}workflow "${workflowName}" (${savedId}).`;
459
+ const summary = `${operation === 'update' ? 'Updated' : 'Created'} ${isSupportingWorkflow ? 'supporting ' : ''}workflow "${workflowName}" (${saved.id}).`;
441
460
  const placeholderRemediation = hasPlaceholders
442
461
  ? (0, remediation_1.createRemediation)({
443
462
  category: 'needs_setup',
@@ -446,13 +465,20 @@ function createBuildWorkflowTool(context) {
446
465
  guidance: 'Workflow submitted successfully, but unresolved setup values remain. Stop code edits and route to workflows(action="setup").',
447
466
  })
448
467
  : undefined;
449
- const outcome = withDeterministicRouting({
468
+ binding = await (0, workflow_file_bindings_1.saveWorkflowSourceFileBinding)(context, {
469
+ ...binding,
470
+ workflowId: saved.id,
471
+ workflowVersionId: saved.versionId,
472
+ sourceHash,
473
+ });
474
+ const outcome = (0, workflow_build_routing_1.withDeterministicRouting)({
450
475
  workItemId: resolvedWorkItemId,
451
476
  ...(runId ? { runId } : {}),
452
477
  taskId: resolvedTaskId,
453
478
  owner,
454
479
  plannedTaskId,
455
- workflowId: savedId,
480
+ workflowId: saved.id,
481
+ sourceFilePath: filePath,
456
482
  submitted: true,
457
483
  triggerType: 'manual_or_testable',
458
484
  triggerNodes,
@@ -465,24 +491,35 @@ function createBuildWorkflowTool(context) {
465
491
  mockedCredentialsByNode: hasMockedCredentialNodes
466
492
  ? mockResult.mockedCredentialsByNode
467
493
  : undefined,
468
- verificationPinData: hasMockedCredentialNodes && Object.keys(mockResult.verificationPinData).length > 0
469
- ? mockResult.verificationPinData
470
- : undefined,
471
- usesWorkflowPinDataForVerification: mockResult.usesWorkflowPinDataForVerification || undefined,
494
+ nodeSimulationPlan,
495
+ simulationFixtures,
472
496
  supportingWorkflowIds: referencedWorkflowIds.length > 0 ? referencedWorkflowIds : undefined,
473
497
  hasUnresolvedPlaceholders: hasPlaceholders || undefined,
474
498
  remediation: placeholderRemediation,
475
499
  summary,
476
500
  });
477
- await promoteMainWorkflow(context, savedId);
478
- await reportWorkflowBuildOutcome(context, outcome, {
501
+ await (0, workflow_build_reporting_1.promoteMainWorkflow)(context, saved.id);
502
+ await (0, workflow_build_reporting_1.reportWorkflowBuildOutcome)(context, outcome, {
479
503
  storeOnRunContext: !isAuxiliarySupportingWorkflow,
480
504
  markPlannedTaskSucceeded: !isAuxiliarySupportingWorkflow,
481
505
  });
482
506
  failureTracker.clear(workItemKey);
507
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
508
+ result: 'success',
509
+ stage: 'save',
510
+ binding,
511
+ targetWorkflowId,
512
+ savedWorkflowId: saved.id,
513
+ saveOperation: operation,
514
+ isSupportingWorkflow,
515
+ isAuxiliarySupportingWorkflow,
516
+ remediation: placeholderRemediation,
517
+ warningCount: informational.length,
518
+ });
483
519
  return {
484
520
  success: true,
485
- workflowId: savedId,
521
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
522
+ workflowId: saved.id,
486
523
  workflowName: json.name || undefined,
487
524
  workItemId: resolvedWorkItemId,
488
525
  isSupportingWorkflow: isSupportingWorkflow || undefined,
@@ -496,38 +533,57 @@ function createBuildWorkflowTool(context) {
496
533
  mockedCredentialsByNode: hasMockedCredentialNodes
497
534
  ? mockResult.mockedCredentialsByNode
498
535
  : undefined,
499
- verificationPinData: hasMockedCredentialNodes && Object.keys(mockResult.verificationPinData).length > 0
500
- ? mockResult.verificationPinData
501
- : undefined,
502
- usesWorkflowPinDataForVerification: mockResult.usesWorkflowPinDataForVerification || undefined,
503
536
  referencedWorkflowIds: referencedWorkflowIds.length > 0 ? referencedWorkflowIds : undefined,
504
537
  hasUnresolvedPlaceholders: hasPlaceholders || undefined,
505
- warnings: informational.length > 0
506
- ? informational.map((w) => `[${w.code}]: ${w.message}`)
507
- : undefined,
538
+ warnings: (0, workflow_build_context_1.combineWarnings)(informational.map((w) => (0, workflow_build_context_1.formatWarning)(w.code, w.message))),
508
539
  };
509
540
  };
510
- if (workflowId) {
511
- const updated = await context.workflowService.updateFromWorkflowJSON(workflowId, json, projectId ? { projectId } : undefined);
512
- lastCodeVersionId = updated.versionId;
513
- return await createSuccessResponse(updated.id);
514
- }
515
- else {
516
- const created = await context.workflowService.createFromWorkflowJSON(json, {
517
- ...(projectId ? { projectId } : {}),
518
- markAsAiTemporary: true,
519
- });
520
- (context.aiCreatedWorkflowIds ??= new Set()).add(created.id);
521
- lastCodeVersionId = created.versionId;
522
- return await createSuccessResponse(created.id);
541
+ if (targetWorkflowId) {
542
+ const updated = await context.workflowService.updateFromWorkflowJSON(targetWorkflowId, json, projectId ? { projectId } : undefined);
543
+ return await createSuccessResponse(updated, 'update');
523
544
  }
545
+ const created = await context.workflowService.createFromWorkflowJSON(json, {
546
+ ...(projectId ? { projectId } : {}),
547
+ markAsAiTemporary: true,
548
+ });
549
+ (context.aiCreatedWorkflowIds ??= new Set()).add(created.id);
550
+ return await createSuccessResponse(created, 'create');
524
551
  }
525
552
  catch (error) {
553
+ const message = error instanceof Error ? error.message : 'Unknown error';
554
+ const remediation = (0, workflow_build_remediation_1.createSaveFailureRemediation)(error, Boolean(binding.workflowId));
555
+ binding = await (0, workflow_build_context_1.markSourceBuildFailed)(context, binding, sourceHash);
556
+ await (0, workflow_build_reporting_1.reportFailedWorkflowBuildOutcome)(context, {
557
+ targetWorkflowId,
558
+ sourceFilePath: filePath,
559
+ workItemId: resolvedWorkItemId,
560
+ taskId: resolvedTaskId,
561
+ plannedTaskId,
562
+ owner,
563
+ remediation,
564
+ errors: [`Workflow save failed: ${message}`],
565
+ summary: 'Workflow source parsed but did not save.',
566
+ storeOnRunContext: !isAuxiliarySupportingWorkflow,
567
+ });
568
+ (0, workflow_build_telemetry_1.trackWorkflowSourceBuild)(context, {
569
+ result: remediation.category === 'blocked' ? 'blocked' : 'failure',
570
+ stage: 'save',
571
+ binding,
572
+ targetWorkflowId,
573
+ saveOperation: targetWorkflowId ? 'update' : 'create',
574
+ isSupportingWorkflow,
575
+ isAuxiliarySupportingWorkflow,
576
+ remediation,
577
+ errorCount: 1,
578
+ });
526
579
  return {
527
580
  success: false,
528
- errors: [
529
- `Workflow save failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
530
- ],
581
+ ...(0, workflow_build_context_1.sourceResponseBase)(binding),
582
+ workflowId: targetWorkflowId,
583
+ workflowName: json.name || undefined,
584
+ workItemId: resolvedWorkItemId,
585
+ errors: [`Workflow save failed: ${message}`],
586
+ remediation,
531
587
  };
532
588
  }
533
589
  })