@n8n/instance-ai 1.4.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/computer-use-prompt.d.ts +5 -0
- package/dist/agent/computer-use-prompt.js +97 -0
- package/dist/agent/computer-use-prompt.js.map +1 -0
- package/dist/agent/instance-agent.js +48 -73
- package/dist/agent/instance-agent.js.map +1 -1
- package/dist/agent/mcp-tool-name-validation.d.ts +15 -0
- package/dist/agent/mcp-tool-name-validation.js +67 -0
- package/dist/agent/mcp-tool-name-validation.js.map +1 -0
- package/dist/agent/sanitize-mcp-schemas.d.ts +56 -2
- package/dist/agent/sanitize-mcp-schemas.js +226 -19
- package/dist/agent/sanitize-mcp-schemas.js.map +1 -1
- package/dist/agent/sub-agent-debriefing.d.ts +2 -2
- package/dist/agent/sub-agent-factory.d.ts +1 -0
- package/dist/agent/sub-agent-factory.js +1 -0
- package/dist/agent/sub-agent-factory.js.map +1 -1
- package/dist/agent/system-prompt.d.ts +1 -1
- package/dist/agent/system-prompt.js +46 -104
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/build.tsbuildinfo +1 -1
- package/dist/constants/model-settings.d.ts +3 -0
- package/dist/constants/model-settings.js +7 -0
- package/dist/constants/model-settings.js.map +1 -0
- package/dist/domain-access/domain-access-tracker.d.ts +3 -0
- package/dist/domain-access/domain-access-tracker.js +16 -0
- package/dist/domain-access/domain-access-tracker.js.map +1 -1
- package/dist/domain-access/domain-gating.d.ts +35 -6
- package/dist/domain-access/domain-gating.js +47 -1
- package/dist/domain-access/domain-gating.js.map +1 -1
- package/dist/domain-access/index.d.ts +1 -1
- package/dist/domain-access/index.js +3 -1
- package/dist/domain-access/index.js.map +1 -1
- package/dist/index.d.ts +14 -6
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +6 -0
- package/dist/mcp/mcp-client-manager.d.ts +18 -2
- package/dist/mcp/mcp-client-manager.js +124 -20
- package/dist/mcp/mcp-client-manager.js.map +1 -1
- package/dist/parsers/docx-parser.d.ts +6 -0
- package/dist/parsers/docx-parser.js +65 -0
- package/dist/parsers/docx-parser.js.map +1 -0
- package/dist/parsers/html-parser.d.ts +7 -0
- package/dist/parsers/html-parser.js +67 -0
- package/dist/parsers/html-parser.js.map +1 -0
- package/dist/parsers/index.d.ts +4 -0
- package/dist/parsers/index.js +16 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/pdf-parser.d.ts +7 -0
- package/dist/parsers/pdf-parser.js +72 -0
- package/dist/parsers/pdf-parser.js.map +1 -0
- package/dist/parsers/structured-file-parser.d.ts +8 -3
- package/dist/parsers/structured-file-parser.js +36 -4
- package/dist/parsers/structured-file-parser.js.map +1 -1
- package/dist/parsers/validate-attachments.d.ts +14 -0
- package/dist/parsers/validate-attachments.js +61 -0
- package/dist/parsers/validate-attachments.js.map +1 -0
- package/dist/parsers/xlsx-parser.d.ts +2 -0
- package/dist/parsers/xlsx-parser.js +75 -0
- package/dist/parsers/xlsx-parser.js.map +1 -0
- package/dist/planned-tasks/planned-task-permissions.d.ts +2 -0
- package/dist/planned-tasks/planned-task-permissions.js +8 -2
- package/dist/planned-tasks/planned-task-permissions.js.map +1 -1
- package/dist/planned-tasks/planned-task-service.d.ts +14 -1
- package/dist/planned-tasks/planned-task-service.js +138 -2
- package/dist/planned-tasks/planned-task-service.js.map +1 -1
- package/dist/runtime/background-task-manager.d.ts +32 -1
- package/dist/runtime/background-task-manager.js +134 -4
- package/dist/runtime/background-task-manager.js.map +1 -1
- package/dist/runtime/builder-sandbox-session-registry.d.ts +45 -0
- package/dist/runtime/builder-sandbox-session-registry.js +159 -0
- package/dist/runtime/builder-sandbox-session-registry.js.map +1 -0
- package/dist/runtime/liveness-policy.d.ts +40 -0
- package/dist/runtime/liveness-policy.js +74 -0
- package/dist/runtime/liveness-policy.js.map +1 -0
- package/dist/runtime/resumable-stream-executor.d.ts +1 -0
- package/dist/runtime/resumable-stream-executor.js +1 -0
- package/dist/runtime/resumable-stream-executor.js.map +1 -1
- package/dist/runtime/run-state-registry.d.ts +21 -5
- package/dist/runtime/run-state-registry.js +109 -7
- package/dist/runtime/run-state-registry.js.map +1 -1
- package/dist/runtime/stream-runner.d.ts +3 -0
- package/dist/runtime/stream-runner.js +3 -0
- package/dist/runtime/stream-runner.js.map +1 -1
- package/dist/runtime/terminal-response-guard.d.ts +30 -0
- package/dist/runtime/terminal-response-guard.js +149 -0
- package/dist/runtime/terminal-response-guard.js.map +1 -0
- package/dist/storage/agent-tree-snapshot.d.ts +2 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.js +3 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/planned-task-storage.js +2 -1
- package/dist/storage/planned-task-storage.js.map +1 -1
- package/dist/storage/terminal-outcome-storage.d.ts +48 -0
- package/dist/storage/terminal-outcome-storage.js +84 -0
- package/dist/storage/terminal-outcome-storage.js.map +1 -0
- package/dist/storage/workflow-loop-storage.d.ts +442 -16
- package/dist/stream/consume-with-hitl.d.ts +2 -0
- package/dist/stream/consume-with-hitl.js +2 -0
- package/dist/stream/consume-with-hitl.js.map +1 -1
- package/dist/stream/map-chunk.js +6 -0
- package/dist/stream/map-chunk.js.map +1 -1
- package/dist/tools/attachments/parse-file.tool.d.ts +46 -34
- package/dist/tools/attachments/parse-file.tool.js +115 -56
- package/dist/tools/attachments/parse-file.tool.js.map +1 -1
- package/dist/tools/credentials.tool.d.ts +120 -1
- package/dist/tools/credentials.tool.js +65 -12
- package/dist/tools/credentials.tool.js.map +1 -1
- package/dist/tools/data-tables.tool.d.ts +1 -0
- package/dist/tools/data-tables.tool.js +64 -24
- package/dist/tools/data-tables.tool.js.map +1 -1
- package/dist/tools/executions.tool.js +4 -1
- package/dist/tools/executions.tool.js.map +1 -1
- package/dist/tools/filesystem/create-tools-from-mcp-server.d.ts +2 -1
- package/dist/tools/filesystem/create-tools-from-mcp-server.js +74 -5
- package/dist/tools/filesystem/create-tools-from-mcp-server.js.map +1 -1
- package/dist/tools/index.d.ts +4 -24
- package/dist/tools/index.js +23 -7
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/nodes.tool.js +15 -15
- package/dist/tools/nodes.tool.js.map +1 -1
- package/dist/tools/orchestration/add-plan-item.tool.js +3 -1
- package/dist/tools/orchestration/add-plan-item.tool.js.map +1 -1
- package/dist/tools/orchestration/blueprint-accumulator.d.ts +4 -1
- package/dist/tools/orchestration/blueprint-accumulator.js +28 -0
- package/dist/tools/orchestration/blueprint-accumulator.js.map +1 -1
- package/dist/tools/orchestration/blueprint.schema.d.ts +61 -16
- package/dist/tools/orchestration/blueprint.schema.js +15 -1
- package/dist/tools/orchestration/blueprint.schema.js.map +1 -1
- package/dist/tools/orchestration/browser-credential-setup.nudge.d.ts +5 -0
- package/dist/tools/orchestration/browser-credential-setup.nudge.js +11 -0
- package/dist/tools/orchestration/browser-credential-setup.nudge.js.map +1 -0
- package/dist/tools/orchestration/browser-credential-setup.prompt.js +2 -1
- package/dist/tools/orchestration/browser-credential-setup.prompt.js.map +1 -1
- package/dist/tools/orchestration/browser-credential-setup.tool.js +55 -7
- package/dist/tools/orchestration/browser-credential-setup.tool.js.map +1 -1
- package/dist/tools/orchestration/build-workflow-agent.prompt.js +86 -60
- package/dist/tools/orchestration/build-workflow-agent.prompt.js.map +1 -1
- package/dist/tools/orchestration/build-workflow-agent.tool.d.ts +56 -3
- package/dist/tools/orchestration/build-workflow-agent.tool.js +1020 -300
- package/dist/tools/orchestration/build-workflow-agent.tool.js.map +1 -1
- package/dist/tools/orchestration/builder-memory-compaction.d.ts +38 -0
- package/dist/tools/orchestration/builder-memory-compaction.js +146 -0
- package/dist/tools/orchestration/builder-memory-compaction.js.map +1 -0
- package/dist/tools/orchestration/complete-checkpoint.tool.d.ts +2 -0
- package/dist/tools/orchestration/complete-checkpoint.tool.js +76 -0
- package/dist/tools/orchestration/complete-checkpoint.tool.js.map +1 -0
- package/dist/tools/orchestration/data-table-agent.tool.js +34 -17
- package/dist/tools/orchestration/data-table-agent.tool.js.map +1 -1
- package/dist/tools/orchestration/delegate.tool.js +34 -16
- package/dist/tools/orchestration/delegate.tool.js.map +1 -1
- package/dist/tools/orchestration/plan-agent-prompt.d.ts +1 -1
- package/dist/tools/orchestration/plan-agent-prompt.js +7 -0
- package/dist/tools/orchestration/plan-agent-prompt.js.map +1 -1
- package/dist/tools/orchestration/plan-with-agent.tool.d.ts +23 -0
- package/dist/tools/orchestration/plan-with-agent.tool.js +314 -13
- package/dist/tools/orchestration/plan-with-agent.tool.js.map +1 -1
- package/dist/tools/orchestration/plan.tool.js +17 -1
- package/dist/tools/orchestration/plan.tool.js.map +1 -1
- package/dist/tools/orchestration/report-verification-verdict.tool.d.ts +40 -2
- package/dist/tools/orchestration/report-verification-verdict.tool.js +85 -6
- package/dist/tools/orchestration/report-verification-verdict.tool.js.map +1 -1
- package/dist/tools/orchestration/research-with-agent.tool.js +33 -16
- package/dist/tools/orchestration/research-with-agent.tool.js.map +1 -1
- package/dist/tools/orchestration/tracing-utils.js +8 -0
- package/dist/tools/orchestration/tracing-utils.js.map +1 -1
- package/dist/tools/orchestration/verify-built-workflow.tool.d.ts +8 -2
- package/dist/tools/orchestration/verify-built-workflow.tool.js +476 -6
- package/dist/tools/orchestration/verify-built-workflow.tool.js.map +1 -1
- package/dist/tools/research.tool.js +56 -10
- package/dist/tools/research.tool.js.map +1 -1
- package/dist/tools/shared/ask-user.tool.d.ts +1 -0
- package/dist/tools/shared/ask-user.tool.js +3 -2
- package/dist/tools/shared/ask-user.tool.js.map +1 -1
- package/dist/tools/templates.tool.js +5 -90
- package/dist/tools/templates.tool.js.map +1 -1
- package/dist/tools/web-research/sanitize-web-content.d.ts +0 -1
- package/dist/tools/web-research/sanitize-web-content.js +0 -9
- package/dist/tools/web-research/sanitize-web-content.js.map +1 -1
- package/dist/tools/workflows/apply-workflow-credentials.tool.d.ts +2 -2
- package/dist/tools/workflows/build-workflow.tool.d.ts +2 -2
- package/dist/tools/workflows/build-workflow.tool.js +6 -4
- package/dist/tools/workflows/build-workflow.tool.js.map +1 -1
- package/dist/tools/workflows/materialize-node-type.tool.d.ts +8 -8
- package/dist/tools/workflows/resolve-credentials.d.ts +9 -4
- package/dist/tools/workflows/resolve-credentials.js +59 -27
- package/dist/tools/workflows/resolve-credentials.js.map +1 -1
- package/dist/tools/workflows/setup-workflow.schema.d.ts +40 -0
- package/dist/tools/workflows/setup-workflow.service.d.ts +5 -1
- package/dist/tools/workflows/setup-workflow.service.js +71 -5
- package/dist/tools/workflows/setup-workflow.service.js.map +1 -1
- package/dist/tools/workflows/submit-workflow-identity.d.ts +25 -3
- package/dist/tools/workflows/submit-workflow-identity.js +92 -5
- package/dist/tools/workflows/submit-workflow-identity.js.map +1 -1
- package/dist/tools/workflows/submit-workflow.tool.d.ts +60 -6
- package/dist/tools/workflows/submit-workflow.tool.js +102 -19
- package/dist/tools/workflows/submit-workflow.tool.js.map +1 -1
- package/dist/tools/workflows/workflow-json-utils.d.ts +4 -0
- package/dist/tools/workflows/workflow-json-utils.js +54 -0
- package/dist/tools/workflows/workflow-json-utils.js.map +1 -0
- package/dist/tools/workflows.tool.d.ts +7 -1
- package/dist/tools/workflows.tool.js +283 -54
- package/dist/tools/workflows.tool.js.map +1 -1
- package/dist/tracing/langsmith-tracing.d.ts +2 -0
- package/dist/tracing/langsmith-tracing.js +12 -3
- package/dist/tracing/langsmith-tracing.js.map +1 -1
- package/dist/tracing/trace-replay.d.ts +3 -0
- package/dist/tracing/trace-replay.js +65 -0
- package/dist/tracing/trace-replay.js.map +1 -1
- package/dist/types.d.ts +92 -16
- package/dist/workflow-builder/parse-validate.d.ts +5 -1
- package/dist/workflow-builder/parse-validate.js +5 -4
- package/dist/workflow-builder/parse-validate.js.map +1 -1
- package/dist/workflow-loop/guidance.js +8 -2
- package/dist/workflow-loop/guidance.js.map +1 -1
- package/dist/workflow-loop/index.d.ts +3 -2
- package/dist/workflow-loop/index.js +11 -1
- package/dist/workflow-loop/index.js.map +1 -1
- package/dist/workflow-loop/remediation.d.ts +13 -0
- package/dist/workflow-loop/remediation.js +36 -0
- package/dist/workflow-loop/remediation.js.map +1 -0
- package/dist/workflow-loop/runtime.js +7 -0
- package/dist/workflow-loop/runtime.js.map +1 -1
- package/dist/workflow-loop/workflow-loop-controller.d.ts +1 -1
- package/dist/workflow-loop/workflow-loop-controller.js +196 -29
- package/dist/workflow-loop/workflow-loop-controller.js.map +1 -1
- package/dist/workflow-loop/workflow-loop-state.d.ts +500 -10
- package/dist/workflow-loop/workflow-loop-state.js +74 -1
- package/dist/workflow-loop/workflow-loop-state.js.map +1 -1
- package/dist/workflow-loop/workflow-task-service.d.ts +2 -1
- package/dist/workflow-loop/workflow-task-service.js +4 -0
- package/dist/workflow-loop/workflow-task-service.js.map +1 -1
- package/dist/workspace/builder-sandbox-factory.d.ts +3 -4
- package/dist/workspace/builder-sandbox-factory.js +74 -35
- package/dist/workspace/builder-sandbox-factory.js.map +1 -1
- package/dist/workspace/create-workspace.d.ts +2 -0
- package/dist/workspace/create-workspace.js.map +1 -1
- package/dist/workspace/n8n-sandbox-filesystem.js +2 -2
- package/dist/workspace/n8n-sandbox-filesystem.js.map +1 -1
- package/dist/workspace/n8n-sandbox-sandbox.d.ts +2 -3
- package/dist/workspace/n8n-sandbox-sandbox.js +3 -7
- package/dist/workspace/n8n-sandbox-sandbox.js.map +1 -1
- package/dist/workspace/sandbox-setup.d.ts +1 -1
- package/dist/workspace/sandbox-setup.js +41 -16
- package/dist/workspace/sandbox-setup.js.map +1 -1
- package/dist/workspace/snapshot-manager.d.ts +14 -2
- package/dist/workspace/snapshot-manager.js +57 -1
- package/dist/workspace/snapshot-manager.js.map +1 -1
- package/package.json +35 -10
- package/dist/tools/templates/template-api.d.ts +0 -21
- package/dist/tools/templates/template-api.js +0 -104
- package/dist/tools/templates/template-api.js.map +0 -1
- package/dist/tools/templates/types.d.ts +0 -52
- package/dist/tools/templates/types.js +0 -37
- package/dist/tools/templates/types.js.map +0 -1
- package/dist/tools/utils/mermaid.utils.d.ts +0 -15
- package/dist/tools/utils/mermaid.utils.js +0 -622
- package/dist/tools/utils/mermaid.utils.js.map +0 -1
- package/dist/tools/utils/node-configuration.utils.d.ts +0 -6
- package/dist/tools/utils/node-configuration.utils.js +0 -77
- package/dist/tools/utils/node-configuration.utils.js.map +0 -1
- package/dist/workspace/n8n-sandbox-client.d.ts +0 -105
- package/dist/workspace/n8n-sandbox-client.js +0 -335
- package/dist/workspace/n8n-sandbox-client.js.map +0 -1
- package/dist/workspace/n8n-sandbox-image-manager.d.ts +0 -5
- package/dist/workspace/n8n-sandbox-image-manager.js +0 -25
- package/dist/workspace/n8n-sandbox-image-manager.js.map +0 -1
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildWorkflowAgentInputSchema = void 0;
|
|
3
|
+
exports.buildWorkflowAgentInputSchema = exports.DETACHED_BUILDER_REQUIREMENTS = void 0;
|
|
4
|
+
exports.buildWarmBuilderFollowUp = buildWarmBuilderFollowUp;
|
|
4
5
|
exports.recordSuccessfulWorkflowBuilds = recordSuccessfulWorkflowBuilds;
|
|
6
|
+
exports.determineVerificationReadiness = determineVerificationReadiness;
|
|
7
|
+
exports.determineSetupRequirement = determineSetupRequirement;
|
|
8
|
+
exports.mergeLatestVerificationIntoOutcome = mergeLatestVerificationIntoOutcome;
|
|
9
|
+
exports.withTerminalLoopState = withTerminalLoopState;
|
|
10
|
+
exports.finalizeBuildResult = finalizeBuildResult;
|
|
11
|
+
exports.supportingWorkflowIdsFromSubmitAttempts = supportingWorkflowIdsFromSubmitAttempts;
|
|
5
12
|
exports.resultFromPostStreamError = resultFromPostStreamError;
|
|
13
|
+
exports.resultFromLaterFailedMainSubmit = resultFromLaterFailedMainSubmit;
|
|
14
|
+
exports.attemptFromAutoResubmit = attemptFromAutoResubmit;
|
|
15
|
+
exports.shouldRecoverSavedWorkflowAfterFailedSubmit = shouldRecoverSavedWorkflowAfterFailedSubmit;
|
|
6
16
|
exports.startBuildWorkflowAgentTask = startBuildWorkflowAgentTask;
|
|
7
17
|
exports.createBuildWorkflowAgentTool = createBuildWorkflowAgentTool;
|
|
8
18
|
const agent_1 = require("@mastra/core/agent");
|
|
@@ -12,29 +22,105 @@ const nanoid_1 = require("nanoid");
|
|
|
12
22
|
const node_crypto_1 = require("node:crypto");
|
|
13
23
|
const zod_1 = require("zod");
|
|
14
24
|
const build_workflow_agent_prompt_1 = require("./build-workflow-agent.prompt");
|
|
25
|
+
const builder_memory_compaction_1 = require("./builder-memory-compaction");
|
|
15
26
|
const display_utils_1 = require("./display-utils");
|
|
16
27
|
const tracing_utils_1 = require("./tracing-utils");
|
|
17
28
|
const verify_built_workflow_tool_1 = require("./verify-built-workflow.tool");
|
|
18
29
|
const register_with_mastra_1 = require("../../agent/register-with-mastra");
|
|
19
30
|
const sub_agent_briefing_1 = require("../../agent/sub-agent-briefing");
|
|
20
31
|
const max_steps_1 = require("../../constants/max-steps");
|
|
32
|
+
const model_settings_1 = require("../../constants/model-settings");
|
|
21
33
|
const resumable_stream_executor_1 = require("../../runtime/resumable-stream-executor");
|
|
22
34
|
const consume_with_hitl_1 = require("../../stream/consume-with-hitl");
|
|
23
35
|
const langsmith_tracing_1 = require("../../tracing/langsmith-tracing");
|
|
24
36
|
const extract_code_1 = require("../../workflow-builder/extract-code");
|
|
37
|
+
const workflow_loop_1 = require("../../workflow-loop");
|
|
25
38
|
const sandbox_fs_1 = require("../../workspace/sandbox-fs");
|
|
26
39
|
const sandbox_setup_1 = require("../../workspace/sandbox-setup");
|
|
40
|
+
const credentials_tool_1 = require("../credentials.tool");
|
|
27
41
|
const resolve_credentials_1 = require("../workflows/resolve-credentials");
|
|
28
42
|
const submit_workflow_identity_1 = require("../workflows/submit-workflow-identity");
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
const workflow_json_utils_1 = require("../workflows/workflow-json-utils");
|
|
44
|
+
const workflows_tool_1 = require("../workflows.tool");
|
|
45
|
+
function createBuilderResourceId(userId) {
|
|
46
|
+
return `${userId}:workflow-builder`;
|
|
47
|
+
}
|
|
48
|
+
const BUILDER_WORKFLOW_ACTIONS = [
|
|
49
|
+
'list',
|
|
50
|
+
'get',
|
|
51
|
+
'get-as-code',
|
|
52
|
+
];
|
|
53
|
+
const BUILDER_CREDENTIAL_ACTIONS = [
|
|
54
|
+
'list',
|
|
55
|
+
'get',
|
|
56
|
+
'search-types',
|
|
57
|
+
'test',
|
|
58
|
+
];
|
|
59
|
+
const BUILDER_SANDBOX_TOOL_NAMES = [
|
|
60
|
+
'nodes',
|
|
61
|
+
'workflows',
|
|
62
|
+
'credentials',
|
|
63
|
+
'executions',
|
|
64
|
+
'data-tables',
|
|
65
|
+
'ask-user',
|
|
66
|
+
];
|
|
67
|
+
const BUILDER_TOOL_MODE_TOOL_NAMES = [
|
|
68
|
+
'build-workflow',
|
|
69
|
+
'nodes',
|
|
70
|
+
'workflows',
|
|
71
|
+
'data-tables',
|
|
72
|
+
'ask-user',
|
|
73
|
+
];
|
|
74
|
+
function createBuilderWorkflowsTool(context) {
|
|
75
|
+
return (0, workflows_tool_1.createWorkflowsTool)(context, {
|
|
76
|
+
allowedActions: BUILDER_WORKFLOW_ACTIONS,
|
|
77
|
+
descriptionPrefix: 'Inspect workflows during build',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function createBuilderCredentialsTool(context) {
|
|
81
|
+
return (0, credentials_tool_1.createCredentialsTool)(context, {
|
|
82
|
+
allowedActions: BUILDER_CREDENTIAL_ACTIONS,
|
|
83
|
+
descriptionPrefix: 'Inspect credentials during build',
|
|
84
|
+
descriptionSuffix: 'Setup is handled after workflow verification.',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function buildWarmBuilderFollowUp(input) {
|
|
88
|
+
const parts = [
|
|
89
|
+
'<builder-follow-up type="fix">',
|
|
90
|
+
`Work item ID: ${input.workItemId}`,
|
|
91
|
+
`Workflow ID: ${input.workflowId}`,
|
|
92
|
+
'Continue from the existing sandbox files and your prior builder messages/tool calls. Apply the requested change, then submit the main workflow file.',
|
|
93
|
+
'',
|
|
94
|
+
exports.DETACHED_BUILDER_REQUIREMENTS,
|
|
95
|
+
];
|
|
96
|
+
if (input.conversationContext) {
|
|
97
|
+
parts.push('', '<conversation-context>', input.conversationContext, '</conversation-context>');
|
|
98
|
+
}
|
|
99
|
+
parts.push('', '<requested-change>', input.task, '</requested-change>', '</builder-follow-up>');
|
|
100
|
+
return parts.join('\n');
|
|
101
|
+
}
|
|
102
|
+
async function ensureBuilderMemoryThread(context, binding) {
|
|
103
|
+
if (!context.memory)
|
|
104
|
+
return false;
|
|
105
|
+
try {
|
|
106
|
+
const existingThread = await context.memory.getThreadById({ threadId: binding.thread });
|
|
107
|
+
if (existingThread)
|
|
108
|
+
return true;
|
|
109
|
+
const now = new Date();
|
|
110
|
+
await context.memory.saveThread({
|
|
111
|
+
thread: {
|
|
112
|
+
id: binding.thread,
|
|
113
|
+
resourceId: binding.resource,
|
|
114
|
+
title: 'Workflow Builder',
|
|
115
|
+
createdAt: now,
|
|
116
|
+
updatedAt: now,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
38
124
|
}
|
|
39
125
|
async function promoteMainWorkflow(context, logger, workflowId) {
|
|
40
126
|
if (!workflowId || !context)
|
|
@@ -64,42 +150,193 @@ function recordSuccessfulWorkflowBuilds(tool, onWorkflowId) {
|
|
|
64
150
|
return result;
|
|
65
151
|
};
|
|
66
152
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
153
|
+
function detectTriggerType(_attempt) {
|
|
154
|
+
return 'manual_or_testable';
|
|
155
|
+
}
|
|
156
|
+
function hasMockedCredentials(outcome) {
|
|
157
|
+
return ((outcome.mockedCredentialTypes?.length ?? 0) > 0 ||
|
|
158
|
+
Object.keys(outcome.mockedCredentialsByNode ?? {}).length > 0);
|
|
159
|
+
}
|
|
160
|
+
function hasCredentialVerificationData(outcome) {
|
|
161
|
+
return (Object.keys(outcome.verificationPinData ?? {}).length > 0 ||
|
|
162
|
+
outcome.usesWorkflowPinDataForVerification === true);
|
|
163
|
+
}
|
|
164
|
+
function hasSuccessfulStructuredVerification(outcome) {
|
|
165
|
+
return (outcome.verification?.attempted === true &&
|
|
166
|
+
outcome.verification.success &&
|
|
167
|
+
!!outcome.verification.executionId);
|
|
168
|
+
}
|
|
169
|
+
function determineVerificationReadiness(outcome) {
|
|
170
|
+
if (hasSuccessfulStructuredVerification(outcome)) {
|
|
171
|
+
return { status: 'already_verified' };
|
|
172
|
+
}
|
|
173
|
+
if (!outcome.submitted) {
|
|
174
|
+
return {
|
|
175
|
+
status: 'not_verifiable',
|
|
176
|
+
reason: 'not-submitted',
|
|
177
|
+
guidance: 'The build did not submit a workflow, so there is nothing to verify.',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (!outcome.workflowId) {
|
|
181
|
+
return {
|
|
182
|
+
status: 'not_verifiable',
|
|
183
|
+
reason: 'missing-workflow-id',
|
|
184
|
+
guidance: 'The build outcome does not include a workflow ID.',
|
|
185
|
+
};
|
|
71
186
|
}
|
|
72
|
-
|
|
73
|
-
|
|
187
|
+
if (outcome.hasUnresolvedPlaceholders) {
|
|
188
|
+
return {
|
|
189
|
+
status: 'needs_setup',
|
|
190
|
+
reason: 'unresolved-placeholders',
|
|
191
|
+
guidance: 'Route the workflow through setup before verification.',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (hasMockedCredentials(outcome) && !hasCredentialVerificationData(outcome)) {
|
|
195
|
+
return {
|
|
196
|
+
status: 'needs_setup',
|
|
197
|
+
reason: 'missing-mocked-credential-pin-data',
|
|
198
|
+
guidance: 'Route the workflow through setup because mocked credentials cannot be verified.',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (outcome.remediation?.category === 'needs_setup') {
|
|
202
|
+
return {
|
|
203
|
+
status: 'needs_setup',
|
|
204
|
+
reason: 'workflow-needs-setup',
|
|
205
|
+
guidance: outcome.remediation.guidance,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (!outcome.triggerNodes?.some((node) => (0, workflow_json_utils_1.isMockableTriggerNodeType)(node.nodeType))) {
|
|
209
|
+
return {
|
|
210
|
+
status: 'not_verifiable',
|
|
211
|
+
reason: 'non-mockable-trigger',
|
|
212
|
+
guidance: 'The workflow does not have a trigger the post-build verifier can exercise.',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return { status: 'ready' };
|
|
74
216
|
}
|
|
75
|
-
function
|
|
76
|
-
if (!
|
|
217
|
+
function determineSetupRequirement(outcome) {
|
|
218
|
+
if (!outcome.submitted || !outcome.workflowId) {
|
|
219
|
+
return { status: 'not_required' };
|
|
220
|
+
}
|
|
221
|
+
if (outcome.hasUnresolvedPlaceholders) {
|
|
222
|
+
return {
|
|
223
|
+
status: 'required',
|
|
224
|
+
reason: 'unresolved-placeholders',
|
|
225
|
+
guidance: 'Route the workflow through setup so the user can fill unresolved values.',
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
if (hasMockedCredentials(outcome)) {
|
|
229
|
+
return {
|
|
230
|
+
status: 'required',
|
|
231
|
+
reason: 'mocked-credentials',
|
|
232
|
+
guidance: 'Route the workflow through setup so the user can add real credentials.',
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (outcome.remediation?.category === 'needs_setup') {
|
|
77
236
|
return {
|
|
237
|
+
status: 'required',
|
|
238
|
+
reason: 'workflow-needs-setup',
|
|
239
|
+
guidance: outcome.remediation.guidance,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return { status: 'not_required' };
|
|
243
|
+
}
|
|
244
|
+
function withDeterministicRouting(outcome) {
|
|
245
|
+
return {
|
|
246
|
+
...outcome,
|
|
247
|
+
verificationReadiness: determineVerificationReadiness(outcome),
|
|
248
|
+
setupRequirement: determineSetupRequirement(outcome),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function buildOutcome(workItemId, runId, taskId, attempt, finalText, supportingWorkflowIds = []) {
|
|
252
|
+
if (!attempt?.success) {
|
|
253
|
+
return withDeterministicRouting({
|
|
78
254
|
workItemId,
|
|
255
|
+
runId,
|
|
79
256
|
taskId,
|
|
80
257
|
submitted: false,
|
|
81
258
|
triggerType: 'manual_or_testable',
|
|
82
259
|
needsUserInput: false,
|
|
83
260
|
failureSignature: attempt?.errors?.join('; '),
|
|
261
|
+
remediation: attempt?.remediation,
|
|
84
262
|
summary: finalText,
|
|
85
|
-
};
|
|
263
|
+
});
|
|
86
264
|
}
|
|
87
|
-
|
|
265
|
+
const placeholderRemediation = attempt.hasUnresolvedPlaceholders
|
|
266
|
+
? (0, workflow_loop_1.createRemediation)({
|
|
267
|
+
category: 'needs_setup',
|
|
268
|
+
shouldEdit: false,
|
|
269
|
+
reason: 'mocked_credentials_or_placeholders',
|
|
270
|
+
guidance: 'Workflow submitted successfully, but unresolved setup values remain. Stop code edits and route to workflows(action="setup").',
|
|
271
|
+
})
|
|
272
|
+
: undefined;
|
|
273
|
+
return withDeterministicRouting({
|
|
88
274
|
workItemId,
|
|
275
|
+
runId,
|
|
89
276
|
taskId,
|
|
90
277
|
workflowId: attempt.workflowId,
|
|
91
278
|
submitted: true,
|
|
92
|
-
triggerType:
|
|
93
|
-
needsUserInput:
|
|
279
|
+
triggerType: detectTriggerType(attempt),
|
|
280
|
+
needsUserInput: Boolean(placeholderRemediation),
|
|
281
|
+
blockingReason: placeholderRemediation?.guidance,
|
|
94
282
|
mockedNodeNames: attempt.mockedNodeNames,
|
|
95
283
|
mockedCredentialTypes: attempt.mockedCredentialTypes,
|
|
96
284
|
mockedCredentialsByNode: attempt.mockedCredentialsByNode,
|
|
285
|
+
triggerNodes: attempt.triggerNodes,
|
|
97
286
|
verificationPinData: attempt.verificationPinData,
|
|
287
|
+
usesWorkflowPinDataForVerification: attempt.usesWorkflowPinDataForVerification,
|
|
288
|
+
supportingWorkflowIds: supportingWorkflowIds.length > 0 ? supportingWorkflowIds : undefined,
|
|
98
289
|
hasUnresolvedPlaceholders: attempt.hasUnresolvedPlaceholders,
|
|
290
|
+
remediation: placeholderRemediation ?? attempt.remediation,
|
|
99
291
|
summary: finalText,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function mergeLatestVerificationIntoOutcome(outcome, latestOutcome) {
|
|
295
|
+
if (!latestOutcome?.verification)
|
|
296
|
+
return outcome;
|
|
297
|
+
if (latestOutcome.workItemId !== outcome.workItemId)
|
|
298
|
+
return outcome;
|
|
299
|
+
if (latestOutcome.taskId !== outcome.taskId)
|
|
300
|
+
return outcome;
|
|
301
|
+
if (outcome.workflowId &&
|
|
302
|
+
latestOutcome.workflowId &&
|
|
303
|
+
latestOutcome.workflowId !== outcome.workflowId) {
|
|
304
|
+
return outcome;
|
|
305
|
+
}
|
|
306
|
+
return withDeterministicRouting({
|
|
307
|
+
...outcome,
|
|
308
|
+
verification: latestOutcome.verification,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
function withTerminalLoopState(outcome, state) {
|
|
312
|
+
const remediation = state?.lastRemediation;
|
|
313
|
+
if (!outcome.submitted || !remediation || remediation.shouldEdit) {
|
|
314
|
+
return outcome;
|
|
315
|
+
}
|
|
316
|
+
return withDeterministicRouting({
|
|
317
|
+
...outcome,
|
|
318
|
+
workflowId: outcome.workflowId ?? state.workflowId,
|
|
319
|
+
needsUserInput: remediation.category === 'needs_setup',
|
|
320
|
+
blockingReason: remediation.guidance,
|
|
321
|
+
remediation,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
async function finalBuildOutcome(context, workItemId, outcome) {
|
|
325
|
+
const latestOutcome = await getLatestBuildOutcome(context, workItemId);
|
|
326
|
+
const loopState = await context.workflowTaskService?.getWorkflowLoopState(workItemId);
|
|
327
|
+
return withTerminalLoopState(mergeLatestVerificationIntoOutcome(outcome, latestOutcome), loopState);
|
|
328
|
+
}
|
|
329
|
+
async function finalizeBuildResult(context, workItemId, result) {
|
|
330
|
+
return {
|
|
331
|
+
text: result.text,
|
|
332
|
+
outcome: await finalBuildOutcome(context, workItemId, result.outcome),
|
|
100
333
|
};
|
|
101
334
|
}
|
|
102
|
-
|
|
335
|
+
async function buildOutcomeWithLatestVerification(context, workItemId, taskId, attempt, finalText, supportingWorkflowIds = []) {
|
|
336
|
+
const outcome = buildOutcome(workItemId, context.runId, taskId, attempt, finalText, supportingWorkflowIds);
|
|
337
|
+
return await finalBuildOutcome(context, workItemId, outcome);
|
|
338
|
+
}
|
|
339
|
+
exports.DETACHED_BUILDER_REQUIREMENTS = `## Detached Task Contract
|
|
103
340
|
|
|
104
341
|
You are running as a detached background task. Do not stop after a successful submit — verify the workflow works.
|
|
105
342
|
|
|
@@ -107,9 +344,13 @@ You are running as a detached background task. Do not stop after a successful su
|
|
|
107
344
|
|
|
108
345
|
Your job is done when ONE of these is true:
|
|
109
346
|
- the workflow is verified (ran successfully)
|
|
110
|
-
- the workflow uses only event triggers (${UNTESTABLE_TRIGGER_LABELS}) and cannot be runtime-tested — stop after a successful submit. Do NOT publish it; the orchestrator will handle setup and publishing.
|
|
111
347
|
- you are blocked after one repair attempt per unique failure
|
|
112
348
|
|
|
349
|
+
Do NOT stop after a successful submit without verifying. Every trigger type is testable:
|
|
350
|
+
manual / schedule via \`executions(action="run")\`; event-based triggers (form, webhook,
|
|
351
|
+
chat, mcp, linear, github, slack, etc.) via \`verify-built-workflow\` with an \`inputData\`
|
|
352
|
+
payload. The pin-data adapter injects it as the trigger node's output.
|
|
353
|
+
|
|
113
354
|
### Submit discipline
|
|
114
355
|
|
|
115
356
|
**Every file edit MUST be followed by submit-workflow before you do anything else.**
|
|
@@ -117,46 +358,202 @@ The system tracks file hashes. If you edit the code and then call \`executions(a
|
|
|
117
358
|
|
|
118
359
|
### Verification
|
|
119
360
|
|
|
120
|
-
- If submit-workflow returned mocked credentials, call verify-built-workflow with the workItemId
|
|
121
|
-
- Otherwise
|
|
122
|
-
-
|
|
123
|
-
-
|
|
361
|
+
- If submit-workflow returned mocked credentials, call \`verify-built-workflow\` with the workItemId.
|
|
362
|
+
- Otherwise pick based on trigger type:
|
|
363
|
+
- **Manual / Schedule** — \`executions(action="run")\`.
|
|
364
|
+
- **Form Trigger** — \`verify-built-workflow\` with \`inputData\` as a flat field map, e.g. \`{name: "Alice", email: "a@b.c"}\`. Do NOT wrap in \`formFields\` — production Form Trigger emits fields directly on \`$json\`, and the adapter rejects wrapped payloads.
|
|
365
|
+
- **Webhook** — \`verify-built-workflow\` with \`inputData\` as the body payload, e.g. \`{event: "signup", userId: "..."}\`. Adapter wraps it under \`body\`; downstream expressions use \`$json.body.<field>\`.
|
|
366
|
+
- **Chat Trigger** — \`verify-built-workflow\` with \`{chatInput: "user message"}\`.
|
|
367
|
+
- **Other event triggers (Linear, GitHub, Slack, MCP, etc.)** — \`verify-built-workflow\` with \`inputData\` matching the trigger's expected payload shape.
|
|
368
|
+
- If verify-built-workflow returns remediation with \`shouldEdit: false\`, stop editing and follow its guidance.
|
|
369
|
+
- If verification fails with \`shouldEdit: true\`, make one batched code repair and re-submit. Never exceed the remaining repair budget in the remediation metadata.
|
|
370
|
+
- If verification fails otherwise, call \`executions(action="debug")\`, fix the code, re-submit, and retry once.
|
|
371
|
+
- If the same failure signature repeats, stop and explain the block.
|
|
124
372
|
|
|
125
373
|
### Resource discovery
|
|
126
374
|
|
|
127
375
|
Before writing code that uses external services, **resolve real resource IDs**:
|
|
128
|
-
- Call \`nodes(action="explore-resources")\` for any parameter with searchListMethod (calendars, spreadsheets, channels, models, etc.)
|
|
376
|
+
- Call \`nodes(action="explore-resources")\` for any parameter with searchListMethod when a matching explicit credential is attached (calendars, spreadsheets, channels, models, etc.)
|
|
129
377
|
- Do NOT use "primary", "default", or any assumed identifier — look up the actual value
|
|
130
378
|
- Call \`nodes(action="suggested")\` early if the workflow fits a known category (web_app, form_input, data_persistence, etc.) — the pattern hints prevent common mistakes
|
|
131
379
|
- Check @builderHint annotations in node type definitions for critical configuration guidance
|
|
132
|
-
|
|
133
|
-
### Publishing
|
|
134
|
-
|
|
135
|
-
Do NOT call \`workflows(action="publish")\` for the main workflow. Publishing is the user's decision after testing. Your job ends at a successful submit. The only exception is sub-workflows in the compositional pattern — those must be published so the parent workflow can reference them.
|
|
136
380
|
`;
|
|
137
381
|
function hashContent(content) {
|
|
138
382
|
return (0, node_crypto_1.createHash)('sha256')
|
|
139
383
|
.update(content ?? '', 'utf8')
|
|
140
384
|
.digest('hex');
|
|
141
385
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
386
|
+
function deterministicSuffix(seed, label, length) {
|
|
387
|
+
return (0, node_crypto_1.createHash)('sha256')
|
|
388
|
+
.update(label)
|
|
389
|
+
.update('\0')
|
|
390
|
+
.update(seed)
|
|
391
|
+
.digest('hex')
|
|
392
|
+
.slice(0, length);
|
|
393
|
+
}
|
|
394
|
+
function shouldUseDeterministicBuilderIds(context) {
|
|
395
|
+
return process.env.E2E_TESTS === 'true' && context.tracing?.replayMode !== 'off';
|
|
396
|
+
}
|
|
397
|
+
function createDeterministicBuilderIds(input) {
|
|
398
|
+
const seed = JSON.stringify({
|
|
399
|
+
task: input.task,
|
|
400
|
+
workflowId: input.workflowId ?? '',
|
|
401
|
+
plannedTaskId: input.plannedTaskId ?? '',
|
|
402
|
+
conversationContext: input.conversationContext ?? '',
|
|
403
|
+
});
|
|
404
|
+
return {
|
|
405
|
+
subAgentId: `agent-builder-${deterministicSuffix(seed, 'agent', 6)}`,
|
|
406
|
+
taskId: `build-${deterministicSuffix(seed, 'task', 8)}`,
|
|
407
|
+
workItemId: `wi_${deterministicSuffix(seed, 'work-item', 8)}`,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function latestSuccessfulMainSubmit(submitAttempts, mainWorkflowPath) {
|
|
411
|
+
for (let i = submitAttempts.length - 1; i >= 0; i--) {
|
|
412
|
+
const attempt = submitAttempts[i];
|
|
413
|
+
if (attempt.filePath === mainWorkflowPath && attempt.success && attempt.workflowId) {
|
|
414
|
+
return attempt;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
function latestMainSubmit(submitAttempts, mainWorkflowPath) {
|
|
420
|
+
for (let i = submitAttempts.length - 1; i >= 0; i--) {
|
|
421
|
+
const attempt = submitAttempts[i];
|
|
422
|
+
if (attempt.filePath === mainWorkflowPath) {
|
|
423
|
+
return attempt;
|
|
149
424
|
}
|
|
150
425
|
}
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
function supportingWorkflowIdsFromSubmitAttempts(submitAttempts, mainWorkflowPath, mainWorkflowId, referencedWorkflowIds = []) {
|
|
429
|
+
const seen = new Set();
|
|
430
|
+
const referencedWorkflowIdSet = new Set(referencedWorkflowIds);
|
|
431
|
+
const supportingWorkflowIds = [];
|
|
432
|
+
for (const attempt of submitAttempts) {
|
|
433
|
+
if (!attempt.success || !attempt.workflowId)
|
|
434
|
+
continue;
|
|
435
|
+
if (attempt.filePath === mainWorkflowPath)
|
|
436
|
+
continue;
|
|
437
|
+
if (attempt.workflowId === mainWorkflowId)
|
|
438
|
+
continue;
|
|
439
|
+
if (!referencedWorkflowIdSet.has(attempt.workflowId))
|
|
440
|
+
continue;
|
|
441
|
+
if (seen.has(attempt.workflowId))
|
|
442
|
+
continue;
|
|
443
|
+
seen.add(attempt.workflowId);
|
|
444
|
+
supportingWorkflowIds.push(attempt.workflowId);
|
|
445
|
+
}
|
|
446
|
+
return supportingWorkflowIds;
|
|
447
|
+
}
|
|
448
|
+
function resultFromPostStreamError(input) {
|
|
449
|
+
const latestAttempt = latestMainSubmit(input.submitAttempts, input.mainWorkflowPath);
|
|
450
|
+
if (!latestAttempt)
|
|
451
|
+
return undefined;
|
|
452
|
+
const attempt = latestAttempt.success
|
|
453
|
+
? latestAttempt
|
|
454
|
+
: shouldRecoverSavedWorkflowAfterFailedSubmit(latestAttempt)
|
|
455
|
+
? latestSuccessfulMainSubmit(input.submitAttempts, input.mainWorkflowPath)
|
|
456
|
+
: undefined;
|
|
151
457
|
if (!attempt)
|
|
152
458
|
return undefined;
|
|
153
459
|
const errorText = input.error instanceof Error ? input.error.message : String(input.error);
|
|
154
460
|
const text = `Workflow ${attempt.workflowId} submitted successfully. A later step failed: ${errorText}`;
|
|
155
461
|
return {
|
|
156
462
|
text,
|
|
157
|
-
outcome: buildOutcome(input.workItemId, input.taskId, attempt, text),
|
|
463
|
+
outcome: buildOutcome(input.workItemId, input.runId, input.taskId, attempt, text, supportingWorkflowIdsFromSubmitAttempts(input.submitAttempts, input.mainWorkflowPath, attempt.workflowId, attempt.referencedWorkflowIds)),
|
|
158
464
|
};
|
|
159
465
|
}
|
|
466
|
+
async function getWorkflowNodeSummaries(context, workflowId) {
|
|
467
|
+
if (!context || !workflowId)
|
|
468
|
+
return undefined;
|
|
469
|
+
try {
|
|
470
|
+
const json = await context.workflowService.getAsWorkflowJSON(workflowId);
|
|
471
|
+
const summaries = [];
|
|
472
|
+
for (const node of json.nodes ?? []) {
|
|
473
|
+
if (!node.name || !node.type)
|
|
474
|
+
continue;
|
|
475
|
+
summaries.push({ name: node.name, type: node.type });
|
|
476
|
+
}
|
|
477
|
+
return summaries;
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
return undefined;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async function getLatestBuildOutcome(context, workItemId) {
|
|
484
|
+
try {
|
|
485
|
+
return await context.workflowTaskService?.getBuildOutcome(workItemId);
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async function compactSuccessfulBuilderMemory(input) {
|
|
492
|
+
if (!input.shouldUseBuilderMemory)
|
|
493
|
+
return;
|
|
494
|
+
try {
|
|
495
|
+
const [nodeSummaries, latestOutcome] = await Promise.all([
|
|
496
|
+
getWorkflowNodeSummaries(input.domainContext, input.workflowId),
|
|
497
|
+
getLatestBuildOutcome(input.context, input.workItemId),
|
|
498
|
+
]);
|
|
499
|
+
await (0, builder_memory_compaction_1.compactBuilderMemoryThread)({
|
|
500
|
+
context: input.context,
|
|
501
|
+
binding: input.binding,
|
|
502
|
+
sessionId: input.activeBuilderSession?.sessionId,
|
|
503
|
+
workflowId: input.workflowId,
|
|
504
|
+
workItemId: input.workItemId,
|
|
505
|
+
sourceFilePath: input.mainWorkflowPath,
|
|
506
|
+
nodeSummaries,
|
|
507
|
+
triggerNodes: input.mainWorkflowAttempt.triggerNodes,
|
|
508
|
+
mockedNodeNames: input.mainWorkflowAttempt.mockedNodeNames,
|
|
509
|
+
mockedCredentialTypes: input.mainWorkflowAttempt.mockedCredentialTypes,
|
|
510
|
+
mockedCredentialsByNode: input.mainWorkflowAttempt.mockedCredentialsByNode,
|
|
511
|
+
verification: latestOutcome?.verification,
|
|
512
|
+
lastRequestedChange: input.lastRequestedChange,
|
|
513
|
+
finalBuilderResult: input.finalText,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
catch {
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function resultFromLaterFailedMainSubmit(input) {
|
|
520
|
+
if (!shouldRecoverSavedWorkflowAfterFailedSubmit(input.failedAttempt))
|
|
521
|
+
return undefined;
|
|
522
|
+
const preservedAttempt = latestSuccessfulMainSubmit(input.submitAttempts, input.mainWorkflowPath);
|
|
523
|
+
if (!preservedAttempt)
|
|
524
|
+
return undefined;
|
|
525
|
+
const errorText = input.failedAttempt.errors?.join(' ') ?? 'Unknown submit-workflow failure.';
|
|
526
|
+
const text = `Workflow ${preservedAttempt.workflowId} was already submitted successfully. ` +
|
|
527
|
+
`A later submit failed: ${errorText}`;
|
|
528
|
+
return {
|
|
529
|
+
text,
|
|
530
|
+
outcome: buildOutcome(input.workItemId, input.runId, input.taskId, preservedAttempt, text, supportingWorkflowIdsFromSubmitAttempts(input.submitAttempts, input.mainWorkflowPath, preservedAttempt.workflowId, preservedAttempt.referencedWorkflowIds)),
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function isFreshAttemptForHash(attempt, sourceHash) {
|
|
534
|
+
return attempt?.sourceHash === sourceHash;
|
|
535
|
+
}
|
|
536
|
+
function attemptFromAutoResubmit(input) {
|
|
537
|
+
if (isFreshAttemptForHash(input.latestAttempt, input.sourceHash)) {
|
|
538
|
+
return input.latestAttempt;
|
|
539
|
+
}
|
|
540
|
+
if (input.resubmit.success)
|
|
541
|
+
return undefined;
|
|
542
|
+
return {
|
|
543
|
+
filePath: input.filePath,
|
|
544
|
+
sourceHash: input.sourceHash,
|
|
545
|
+
success: false,
|
|
546
|
+
errors: input.resubmit.errors,
|
|
547
|
+
remediation: input.resubmit.remediation,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function shouldRecoverSavedWorkflowAfterFailedSubmit(attempt) {
|
|
551
|
+
return attempt.remediation?.shouldEdit === false;
|
|
552
|
+
}
|
|
553
|
+
function formatSubmitWorkflowErrors(output, fallback) {
|
|
554
|
+
const errors = output.errors?.join(' ') ?? '';
|
|
555
|
+
return errors.length > 0 ? errors : fallback;
|
|
556
|
+
}
|
|
160
557
|
async function startBuildWorkflowAgentTask(context, input) {
|
|
161
558
|
if (!context.spawnBackgroundTask) {
|
|
162
559
|
return {
|
|
@@ -165,77 +562,72 @@ async function startBuildWorkflowAgentTask(context, input) {
|
|
|
165
562
|
agentId: '',
|
|
166
563
|
};
|
|
167
564
|
}
|
|
565
|
+
const spawnBackgroundTask = context.spawnBackgroundTask;
|
|
168
566
|
const factory = context.builderSandboxFactory;
|
|
169
567
|
const domainContext = context.domainContext;
|
|
170
568
|
const useSandbox = !!factory && !!domainContext;
|
|
171
569
|
let builderTools;
|
|
172
570
|
let prompt = build_workflow_agent_prompt_1.BUILDER_AGENT_PROMPT;
|
|
173
|
-
let
|
|
571
|
+
let availableCredentials;
|
|
174
572
|
if (useSandbox) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
'credentials',
|
|
180
|
-
'executions',
|
|
181
|
-
'data-tables',
|
|
182
|
-
'templates',
|
|
183
|
-
'ask-user',
|
|
184
|
-
];
|
|
573
|
+
const credentialSnapshot = await (0, resolve_credentials_1.buildCredentialSnapshot)(domainContext.credentialService);
|
|
574
|
+
availableCredentials = credentialSnapshot.list;
|
|
575
|
+
const builderWorkflowsTool = createBuilderWorkflowsTool(domainContext);
|
|
576
|
+
const builderCredentialsTool = createBuilderCredentialsTool(domainContext);
|
|
185
577
|
builderTools = {};
|
|
186
|
-
for (const name of
|
|
578
|
+
for (const name of BUILDER_SANDBOX_TOOL_NAMES) {
|
|
187
579
|
if (context.domainTools[name]) {
|
|
188
580
|
builderTools[name] = context.domainTools[name];
|
|
189
581
|
}
|
|
190
582
|
}
|
|
583
|
+
builderTools.workflows = builderWorkflowsTool;
|
|
584
|
+
builderTools.credentials = builderCredentialsTool;
|
|
191
585
|
if (context.workflowTaskService && context.domainContext) {
|
|
192
586
|
builderTools['verify-built-workflow'] = (0, verify_built_workflow_tool_1.createVerifyBuiltWorkflowTool)(context);
|
|
193
587
|
}
|
|
194
588
|
}
|
|
195
589
|
else {
|
|
196
590
|
builderTools = {};
|
|
197
|
-
const toolNames =
|
|
198
|
-
'
|
|
199
|
-
|
|
200
|
-
'workflows',
|
|
201
|
-
'data-tables',
|
|
202
|
-
'templates',
|
|
203
|
-
'ask-user',
|
|
204
|
-
...(context.researchMode ? ['research'] : []),
|
|
205
|
-
];
|
|
591
|
+
const toolNames = context.researchMode
|
|
592
|
+
? [...BUILDER_TOOL_MODE_TOOL_NAMES, 'research']
|
|
593
|
+
: BUILDER_TOOL_MODE_TOOL_NAMES;
|
|
206
594
|
for (const name of toolNames) {
|
|
207
595
|
if (name in context.domainTools) {
|
|
208
596
|
builderTools[name] = context.domainTools[name];
|
|
209
597
|
}
|
|
210
598
|
}
|
|
599
|
+
if (domainContext) {
|
|
600
|
+
builderTools.workflows = createBuilderWorkflowsTool(domainContext);
|
|
601
|
+
builderTools.credentials = createBuilderCredentialsTool(domainContext);
|
|
602
|
+
}
|
|
211
603
|
if (!builderTools['build-workflow']) {
|
|
212
604
|
return { result: 'Error: build-workflow tool not available.', taskId: '', agentId: '' };
|
|
213
605
|
}
|
|
214
606
|
}
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
parentId: context.orchestratorAgentId,
|
|
224
|
-
role: 'workflow-builder',
|
|
225
|
-
tools: Object.keys(builderTools),
|
|
226
|
-
taskId,
|
|
227
|
-
kind: 'builder',
|
|
228
|
-
title: 'Building workflow',
|
|
229
|
-
subtitle: (0, display_utils_1.truncateLabel)(input.task),
|
|
230
|
-
goal: input.task,
|
|
231
|
-
targetResource: input.workflowId
|
|
232
|
-
? { type: 'workflow', id: input.workflowId }
|
|
233
|
-
: { type: 'workflow' },
|
|
234
|
-
},
|
|
235
|
-
});
|
|
607
|
+
const deterministicIds = shouldUseDeterministicBuilderIds(context)
|
|
608
|
+
? createDeterministicBuilderIds(input)
|
|
609
|
+
: undefined;
|
|
610
|
+
const subAgentId = input.agentId ?? deterministicIds?.subAgentId ?? `agent-builder-${(0, nanoid_1.nanoid)(6)}`;
|
|
611
|
+
const taskId = input.taskId ?? deterministicIds?.taskId ?? `build-${(0, nanoid_1.nanoid)(8)}`;
|
|
612
|
+
const baseWorkItemId = input.workItemId ??
|
|
613
|
+
deterministicIds?.workItemId ??
|
|
614
|
+
(input.workflowId ? `${context.runId}:default` : `wi_${(0, nanoid_1.nanoid)(8)}`);
|
|
236
615
|
const { workflowId } = input;
|
|
616
|
+
const reusedBuilderSession = useSandbox && workflowId
|
|
617
|
+
? context.builderSandboxSessionRegistry?.acquireByWorkflowId(context.threadId, workflowId)
|
|
618
|
+
: undefined;
|
|
619
|
+
const workItemId = reusedBuilderSession?.workItemId ?? baseWorkItemId;
|
|
620
|
+
const builderThreadId = reusedBuilderSession?.builderThreadId ?? (0, node_crypto_1.randomUUID)();
|
|
621
|
+
const builderResourceId = reusedBuilderSession?.builderResourceId ?? createBuilderResourceId(context.userId);
|
|
622
|
+
const builderMemoryBinding = {
|
|
623
|
+
resource: builderResourceId,
|
|
624
|
+
thread: builderThreadId,
|
|
625
|
+
};
|
|
237
626
|
let additionalContext = '';
|
|
238
|
-
if (
|
|
627
|
+
if (reusedBuilderSession && workflowId) {
|
|
628
|
+
additionalContext = '';
|
|
629
|
+
}
|
|
630
|
+
else if (useSandbox && workflowId) {
|
|
239
631
|
additionalContext = `[CONTEXT: Modifying existing workflow ${workflowId}. The current code is pre-loaded in ~/workspace/src/workflow.ts — read it first, then edit. Use workflowId "${workflowId}" when calling submit-workflow.]\n\n[WORK ITEM ID: ${workItemId}]`;
|
|
240
632
|
}
|
|
241
633
|
else if (useSandbox) {
|
|
@@ -244,82 +636,347 @@ async function startBuildWorkflowAgentTask(context, input) {
|
|
|
244
636
|
else if (workflowId) {
|
|
245
637
|
additionalContext = `[CONTEXT: Modifying existing workflow ${workflowId}. Use workflowId "${workflowId}" when calling build-workflow.]`;
|
|
246
638
|
}
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
additionalContext: additionalContext || undefined,
|
|
251
|
-
requirements: useSandbox ? DETACHED_BUILDER_REQUIREMENTS : undefined,
|
|
252
|
-
iteration: context.iterationLog
|
|
253
|
-
? {
|
|
254
|
-
log: context.iterationLog,
|
|
255
|
-
threadId: context.threadId,
|
|
256
|
-
taskKey: `build:${workflowId ?? 'new'}`,
|
|
257
|
-
}
|
|
258
|
-
: undefined,
|
|
259
|
-
runningTasks: context.getRunningTaskSummaries?.(),
|
|
260
|
-
});
|
|
261
|
-
const traceContext = await (0, tracing_utils_1.createDetachedSubAgentTracing)(context, {
|
|
262
|
-
agentId: subAgentId,
|
|
263
|
-
role: 'workflow-builder',
|
|
264
|
-
kind: 'builder',
|
|
265
|
-
taskId,
|
|
266
|
-
plannedTaskId: input.plannedTaskId,
|
|
267
|
-
workItemId,
|
|
268
|
-
inputs: {
|
|
639
|
+
const runningTaskSummaries = context.getRunningTaskSummaries?.();
|
|
640
|
+
const briefing = reusedBuilderSession && workflowId
|
|
641
|
+
? buildWarmBuilderFollowUp({
|
|
269
642
|
task: input.task,
|
|
270
|
-
workflowId: input.workflowId,
|
|
271
643
|
conversationContext: input.conversationContext,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
644
|
+
workflowId,
|
|
645
|
+
workItemId,
|
|
646
|
+
})
|
|
647
|
+
: await (0, sub_agent_briefing_1.buildSubAgentBriefing)({
|
|
648
|
+
task: input.task,
|
|
649
|
+
conversationContext: input.conversationContext,
|
|
650
|
+
additionalContext: additionalContext || undefined,
|
|
651
|
+
requirements: useSandbox ? exports.DETACHED_BUILDER_REQUIREMENTS : undefined,
|
|
652
|
+
iteration: context.iterationLog
|
|
653
|
+
? {
|
|
654
|
+
log: context.iterationLog,
|
|
655
|
+
threadId: context.threadId,
|
|
656
|
+
taskKey: `build:${workflowId ?? 'new'}`,
|
|
657
|
+
}
|
|
658
|
+
: undefined,
|
|
659
|
+
runningTasks: runningTaskSummaries,
|
|
660
|
+
});
|
|
661
|
+
let traceContext;
|
|
662
|
+
try {
|
|
663
|
+
traceContext = await (0, tracing_utils_1.createDetachedSubAgentTracing)(context, {
|
|
664
|
+
agentId: subAgentId,
|
|
665
|
+
role: 'workflow-builder',
|
|
666
|
+
kind: 'builder',
|
|
667
|
+
taskId,
|
|
668
|
+
plannedTaskId: input.plannedTaskId,
|
|
669
|
+
workItemId,
|
|
670
|
+
inputs: {
|
|
671
|
+
task: input.task,
|
|
672
|
+
workflowId: input.workflowId,
|
|
673
|
+
conversationContext: input.conversationContext,
|
|
674
|
+
},
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
catch (error) {
|
|
678
|
+
if (reusedBuilderSession) {
|
|
679
|
+
void context.builderSandboxSessionRegistry?.release(reusedBuilderSession.sessionId, {
|
|
680
|
+
keep: true,
|
|
681
|
+
reason: 'trace_setup_failed',
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
throw error;
|
|
685
|
+
}
|
|
686
|
+
let spawnOutcome;
|
|
687
|
+
try {
|
|
688
|
+
spawnOutcome = spawnBackgroundTask({
|
|
689
|
+
taskId,
|
|
690
|
+
threadId: context.threadId,
|
|
691
|
+
agentId: subAgentId,
|
|
692
|
+
role: 'workflow-builder',
|
|
693
|
+
traceContext,
|
|
694
|
+
plannedTaskId: input.plannedTaskId,
|
|
695
|
+
workItemId,
|
|
696
|
+
dedupeKey: {
|
|
697
|
+
role: 'workflow-builder',
|
|
698
|
+
plannedTaskId: input.plannedTaskId,
|
|
699
|
+
workflowId: input.workflowId,
|
|
700
|
+
},
|
|
701
|
+
parentCheckpointId: context.isCheckpointFollowUp === true ? context.checkpointTaskId : undefined,
|
|
702
|
+
run: async (signal, drainCorrections, waitForCorrection) => await (0, tracing_utils_1.withTraceContextActor)(traceContext, async () => {
|
|
703
|
+
let builderWs;
|
|
704
|
+
let activeBuilderSession = reusedBuilderSession;
|
|
705
|
+
const submitAttempts = new Map();
|
|
706
|
+
const submitAttemptHistory = [];
|
|
707
|
+
try {
|
|
708
|
+
if (useSandbox) {
|
|
709
|
+
let workspace;
|
|
710
|
+
let root;
|
|
711
|
+
if (activeBuilderSession) {
|
|
712
|
+
workspace = activeBuilderSession.workspace;
|
|
713
|
+
root = activeBuilderSession.root;
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
builderWs = await factory.create(subAgentId, domainContext);
|
|
717
|
+
workspace = builderWs.workspace;
|
|
718
|
+
root = await (0, sandbox_setup_1.getWorkspaceRoot)(workspace);
|
|
719
|
+
}
|
|
720
|
+
prompt = (0, build_workflow_agent_prompt_1.createSandboxBuilderAgentPrompt)(root);
|
|
721
|
+
if (!activeBuilderSession && builderWs) {
|
|
722
|
+
activeBuilderSession = context.builderSandboxSessionRegistry?.create({
|
|
723
|
+
threadId: context.threadId,
|
|
724
|
+
workflowId,
|
|
725
|
+
workItemId,
|
|
726
|
+
builderThreadId,
|
|
727
|
+
builderResourceId,
|
|
728
|
+
builderWorkspace: builderWs,
|
|
729
|
+
root,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
if (!reusedBuilderSession && workflowId && domainContext) {
|
|
733
|
+
try {
|
|
734
|
+
const json = await domainContext.workflowService.getAsWorkflowJSON(workflowId);
|
|
735
|
+
const rawCode = (0, workflow_sdk_1.generateWorkflowCode)(json);
|
|
736
|
+
const code = `${extract_code_1.SDK_IMPORT_STATEMENT}\n\n${rawCode}`;
|
|
737
|
+
if (workspace.filesystem) {
|
|
738
|
+
await workspace.filesystem.writeFile(`${root}/src/workflow.ts`, code, {
|
|
739
|
+
recursive: true,
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
catch {
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
const mainWorkflowPath = `${root}/src/workflow.ts`;
|
|
747
|
+
builderTools['submit-workflow'] = (0, submit_workflow_identity_1.createIdentityEnforcedSubmitWorkflowTool)({
|
|
748
|
+
context: domainContext,
|
|
749
|
+
workspace,
|
|
750
|
+
availableCredentials,
|
|
751
|
+
root,
|
|
752
|
+
currentRunId: context.runId,
|
|
753
|
+
getWorkflowLoopState: async () => await context.workflowTaskService?.getWorkflowLoopState(workItemId),
|
|
754
|
+
onGuardFired: (event) => {
|
|
755
|
+
context.trackTelemetry?.('Builder remediation guard fired', {
|
|
756
|
+
thread_id: context.threadId,
|
|
757
|
+
run_id: context.runId,
|
|
758
|
+
work_item_id: workItemId,
|
|
759
|
+
workflow_id: event.workflowId,
|
|
760
|
+
category: event.category,
|
|
761
|
+
attempt_count: event.attemptCount,
|
|
762
|
+
reason: event.reason,
|
|
763
|
+
});
|
|
764
|
+
},
|
|
765
|
+
onAttempt: async (attempt) => {
|
|
766
|
+
submitAttempts.set(attempt.filePath, attempt);
|
|
767
|
+
submitAttemptHistory.push(attempt);
|
|
768
|
+
if (attempt.filePath !== mainWorkflowPath) {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
if (attempt.success && attempt.workflowId && activeBuilderSession) {
|
|
772
|
+
context.builderSandboxSessionRegistry?.aliasWorkflowId(activeBuilderSession.sessionId, attempt.workflowId);
|
|
773
|
+
}
|
|
774
|
+
if (!context.workflowTaskService) {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
await context.workflowTaskService.reportBuildOutcome(buildOutcome(workItemId, context.runId, taskId, attempt, attempt.success
|
|
778
|
+
? 'Workflow submitted and ready for verification.'
|
|
779
|
+
: (attempt.errors?.join(' ') ?? 'Workflow submission failed.'), supportingWorkflowIdsFromSubmitAttempts(submitAttemptHistory, mainWorkflowPath, attempt.workflowId, attempt.referencedWorkflowIds)));
|
|
780
|
+
},
|
|
781
|
+
});
|
|
782
|
+
const tracedBuilderTools = (0, tracing_utils_1.traceSubAgentTools)(context, builderTools, 'workflow-builder');
|
|
783
|
+
const shouldUseBuilderMemory = activeBuilderSession
|
|
784
|
+
? await ensureBuilderMemoryThread(context, builderMemoryBinding)
|
|
785
|
+
: false;
|
|
786
|
+
const subAgent = new agent_1.Agent({
|
|
787
|
+
id: subAgentId,
|
|
788
|
+
name: 'Workflow Builder Agent',
|
|
789
|
+
instructions: {
|
|
790
|
+
role: 'system',
|
|
791
|
+
content: prompt,
|
|
792
|
+
providerOptions: {
|
|
793
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
model: context.modelId,
|
|
797
|
+
tools: tracedBuilderTools,
|
|
798
|
+
workspace,
|
|
799
|
+
memory: shouldUseBuilderMemory ? context.memory : undefined,
|
|
800
|
+
});
|
|
801
|
+
(0, langsmith_tracing_1.mergeTraceRunInputs)(traceContext?.actorRun, (0, langsmith_tracing_1.buildAgentTraceInputs)({
|
|
802
|
+
systemPrompt: prompt,
|
|
803
|
+
tools: tracedBuilderTools,
|
|
804
|
+
modelId: context.modelId,
|
|
805
|
+
}));
|
|
806
|
+
(0, register_with_mastra_1.registerWithMastra)(subAgentId, subAgent, context.storage);
|
|
807
|
+
const traceParent = (0, langsmith_tracing_1.getTraceParentRun)();
|
|
808
|
+
let finalText;
|
|
293
809
|
try {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
810
|
+
const hitlResult = await (0, langsmith_tracing_1.withTraceParentContext)(traceParent, async () => {
|
|
811
|
+
const llmStepTraceHooks = (0, resumable_stream_executor_1.createLlmStepTraceHooks)(traceParent);
|
|
812
|
+
const resumeOptions = {
|
|
813
|
+
modelSettings: { temperature: model_settings_1.TEMPERATURE.BUILDER },
|
|
814
|
+
providerOptions: {
|
|
815
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
816
|
+
},
|
|
817
|
+
...(shouldUseBuilderMemory
|
|
818
|
+
? { memory: builderMemoryBinding, savePerStep: true }
|
|
819
|
+
: {}),
|
|
820
|
+
};
|
|
821
|
+
const stream = await subAgent.stream(briefing, {
|
|
822
|
+
maxSteps: max_steps_1.MAX_STEPS.BUILDER,
|
|
823
|
+
abortSignal: signal,
|
|
824
|
+
modelSettings: { temperature: model_settings_1.TEMPERATURE.BUILDER },
|
|
825
|
+
providerOptions: {
|
|
826
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
827
|
+
},
|
|
828
|
+
...(shouldUseBuilderMemory
|
|
829
|
+
? { memory: builderMemoryBinding, savePerStep: true }
|
|
830
|
+
: {}),
|
|
831
|
+
...(llmStepTraceHooks?.executionOptions ?? {}),
|
|
832
|
+
});
|
|
833
|
+
return await (0, consume_with_hitl_1.consumeStreamWithHitl)({
|
|
834
|
+
agent: subAgent,
|
|
835
|
+
stream: stream,
|
|
836
|
+
runId: context.runId,
|
|
837
|
+
agentId: subAgentId,
|
|
838
|
+
eventBus: context.eventBus,
|
|
839
|
+
logger: context.logger,
|
|
840
|
+
threadId: context.threadId,
|
|
841
|
+
abortSignal: signal,
|
|
842
|
+
waitForConfirmation: context.waitForConfirmation,
|
|
843
|
+
drainCorrections,
|
|
844
|
+
waitForCorrection,
|
|
845
|
+
onActivity: () => context.touchBackgroundTask?.(taskId),
|
|
846
|
+
llmStepTraceHooks,
|
|
847
|
+
maxSteps: max_steps_1.MAX_STEPS.BUILDER,
|
|
848
|
+
resumeOptions,
|
|
301
849
|
});
|
|
850
|
+
});
|
|
851
|
+
finalText = await hitlResult.text;
|
|
852
|
+
}
|
|
853
|
+
catch (error) {
|
|
854
|
+
const recovered = resultFromPostStreamError({
|
|
855
|
+
error,
|
|
856
|
+
submitAttempts: submitAttemptHistory,
|
|
857
|
+
mainWorkflowPath,
|
|
858
|
+
workItemId,
|
|
859
|
+
runId: context.runId,
|
|
860
|
+
taskId,
|
|
861
|
+
});
|
|
862
|
+
if (recovered) {
|
|
863
|
+
await promoteMainWorkflow(domainContext, context.logger, recovered.outcome.workflowId);
|
|
864
|
+
return await finalizeBuildResult(context, workItemId, recovered);
|
|
302
865
|
}
|
|
866
|
+
throw error;
|
|
303
867
|
}
|
|
304
|
-
|
|
868
|
+
const mainWorkflowAttempt = submitAttempts.get(mainWorkflowPath);
|
|
869
|
+
const currentMainWorkflow = await (0, sandbox_fs_1.readFileViaSandbox)(workspace, mainWorkflowPath);
|
|
870
|
+
const currentMainWorkflowHash = hashContent(currentMainWorkflow);
|
|
871
|
+
if (!mainWorkflowAttempt) {
|
|
872
|
+
const text = 'Error: workflow builder finished without submitting /src/workflow.ts.';
|
|
873
|
+
return {
|
|
874
|
+
text,
|
|
875
|
+
outcome: buildOutcome(workItemId, context.runId, taskId, undefined, text),
|
|
876
|
+
};
|
|
305
877
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return;
|
|
878
|
+
if (!mainWorkflowAttempt.success) {
|
|
879
|
+
const recovered = resultFromLaterFailedMainSubmit({
|
|
880
|
+
failedAttempt: mainWorkflowAttempt,
|
|
881
|
+
submitAttempts: submitAttemptHistory,
|
|
882
|
+
mainWorkflowPath,
|
|
883
|
+
workItemId,
|
|
884
|
+
runId: context.runId,
|
|
885
|
+
taskId,
|
|
886
|
+
});
|
|
887
|
+
if (recovered) {
|
|
888
|
+
await promoteMainWorkflow(domainContext, context.logger, recovered.outcome.workflowId);
|
|
889
|
+
return await finalizeBuildResult(context, workItemId, recovered);
|
|
318
890
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
891
|
+
const errorText = mainWorkflowAttempt.errors?.join(' ') ?? 'Unknown submit-workflow failure.';
|
|
892
|
+
const text = `Error: workflow builder stopped after a failed submit-workflow for /src/workflow.ts. ${errorText}`;
|
|
893
|
+
return {
|
|
894
|
+
text,
|
|
895
|
+
outcome: buildOutcome(workItemId, context.runId, taskId, mainWorkflowAttempt, text),
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
if (mainWorkflowAttempt.sourceHash !== currentMainWorkflowHash) {
|
|
899
|
+
const submitTool = tracedBuilderTools['submit-workflow'];
|
|
900
|
+
if (submitTool && 'execute' in submitTool) {
|
|
901
|
+
const resubmit = await submitTool.execute({
|
|
902
|
+
filePath: mainWorkflowPath,
|
|
903
|
+
workflowId: mainWorkflowAttempt.workflowId,
|
|
904
|
+
});
|
|
905
|
+
const refreshedAttempt = attemptFromAutoResubmit({
|
|
906
|
+
latestAttempt: submitAttempts.get(mainWorkflowPath),
|
|
907
|
+
resubmit,
|
|
908
|
+
filePath: mainWorkflowPath,
|
|
909
|
+
sourceHash: currentMainWorkflowHash,
|
|
910
|
+
});
|
|
911
|
+
if (resubmit.success && refreshedAttempt?.success) {
|
|
912
|
+
await promoteMainWorkflow(domainContext, context.logger, refreshedAttempt.workflowId);
|
|
913
|
+
await compactSuccessfulBuilderMemory({
|
|
914
|
+
context,
|
|
915
|
+
binding: builderMemoryBinding,
|
|
916
|
+
activeBuilderSession,
|
|
917
|
+
domainContext,
|
|
918
|
+
workflowId: refreshedAttempt.workflowId,
|
|
919
|
+
workItemId,
|
|
920
|
+
mainWorkflowPath,
|
|
921
|
+
mainWorkflowAttempt: refreshedAttempt,
|
|
922
|
+
lastRequestedChange: input.task,
|
|
923
|
+
finalText,
|
|
924
|
+
shouldUseBuilderMemory,
|
|
925
|
+
});
|
|
926
|
+
const outcome = await buildOutcomeWithLatestVerification(context, workItemId, taskId, refreshedAttempt, finalText, supportingWorkflowIdsFromSubmitAttempts(submitAttemptHistory, mainWorkflowPath, refreshedAttempt.workflowId, refreshedAttempt.referencedWorkflowIds));
|
|
927
|
+
return {
|
|
928
|
+
text: finalText,
|
|
929
|
+
outcome,
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
const resubmitErrors = refreshedAttempt?.errors?.join(' ') ??
|
|
933
|
+
formatSubmitWorkflowErrors(resubmit, 'Auto-re-submit failed.');
|
|
934
|
+
if (refreshedAttempt &&
|
|
935
|
+
!refreshedAttempt.success &&
|
|
936
|
+
shouldRecoverSavedWorkflowAfterFailedSubmit(refreshedAttempt)) {
|
|
937
|
+
const recovered = resultFromLaterFailedMainSubmit({
|
|
938
|
+
failedAttempt: refreshedAttempt,
|
|
939
|
+
submitAttempts: submitAttemptHistory,
|
|
940
|
+
mainWorkflowPath,
|
|
941
|
+
workItemId,
|
|
942
|
+
runId: context.runId,
|
|
943
|
+
taskId,
|
|
944
|
+
});
|
|
945
|
+
if (recovered) {
|
|
946
|
+
await promoteMainWorkflow(domainContext, context.logger, recovered.outcome.workflowId);
|
|
947
|
+
return await finalizeBuildResult(context, workItemId, recovered);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
const text = `Error: auto-re-submit of edited /src/workflow.ts failed. ${resubmitErrors}`;
|
|
951
|
+
return {
|
|
952
|
+
text,
|
|
953
|
+
outcome: buildOutcome(workItemId, context.runId, taskId, refreshedAttempt ?? undefined, text),
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
await promoteMainWorkflow(domainContext, context.logger, mainWorkflowAttempt.workflowId);
|
|
958
|
+
await compactSuccessfulBuilderMemory({
|
|
959
|
+
context,
|
|
960
|
+
binding: builderMemoryBinding,
|
|
961
|
+
activeBuilderSession,
|
|
962
|
+
domainContext,
|
|
963
|
+
workflowId: mainWorkflowAttempt.workflowId,
|
|
964
|
+
workItemId,
|
|
965
|
+
mainWorkflowPath,
|
|
966
|
+
mainWorkflowAttempt,
|
|
967
|
+
lastRequestedChange: input.task,
|
|
968
|
+
finalText,
|
|
969
|
+
shouldUseBuilderMemory,
|
|
970
|
+
});
|
|
971
|
+
const outcome = await buildOutcomeWithLatestVerification(context, workItemId, taskId, mainWorkflowAttempt, finalText, supportingWorkflowIdsFromSubmitAttempts(submitAttemptHistory, mainWorkflowPath, mainWorkflowAttempt.workflowId, mainWorkflowAttempt.referencedWorkflowIds));
|
|
972
|
+
return {
|
|
973
|
+
text: finalText,
|
|
974
|
+
outcome,
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
let fallbackMainWorkflowId;
|
|
978
|
+
recordSuccessfulWorkflowBuilds(builderTools['build-workflow'], (workflowId) => {
|
|
979
|
+
fallbackMainWorkflowId = workflowId;
|
|
323
980
|
});
|
|
324
981
|
const tracedBuilderTools = (0, tracing_utils_1.traceSubAgentTools)(context, builderTools, 'workflow-builder');
|
|
325
982
|
const subAgent = new agent_1.Agent({
|
|
@@ -334,7 +991,6 @@ async function startBuildWorkflowAgentTask(context, input) {
|
|
|
334
991
|
},
|
|
335
992
|
model: context.modelId,
|
|
336
993
|
tools: tracedBuilderTools,
|
|
337
|
-
workspace,
|
|
338
994
|
});
|
|
339
995
|
(0, langsmith_tracing_1.mergeTraceRunInputs)(traceContext?.actorRun, (0, langsmith_tracing_1.buildAgentTraceInputs)({
|
|
340
996
|
systemPrompt: prompt,
|
|
@@ -343,155 +999,111 @@ async function startBuildWorkflowAgentTask(context, input) {
|
|
|
343
999
|
}));
|
|
344
1000
|
(0, register_with_mastra_1.registerWithMastra)(subAgentId, subAgent, context.storage);
|
|
345
1001
|
const traceParent = (0, langsmith_tracing_1.getTraceParentRun)();
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
agentId: subAgentId,
|
|
363
|
-
eventBus: context.eventBus,
|
|
364
|
-
logger: context.logger,
|
|
365
|
-
threadId: context.threadId,
|
|
366
|
-
abortSignal: signal,
|
|
367
|
-
waitForConfirmation: context.waitForConfirmation,
|
|
368
|
-
drainCorrections,
|
|
369
|
-
waitForCorrection,
|
|
370
|
-
llmStepTraceHooks,
|
|
371
|
-
});
|
|
1002
|
+
const hitlResult = await (0, langsmith_tracing_1.withTraceParentContext)(traceParent, async () => {
|
|
1003
|
+
const llmStepTraceHooks = (0, resumable_stream_executor_1.createLlmStepTraceHooks)(traceParent);
|
|
1004
|
+
const resumeOptions = {
|
|
1005
|
+
modelSettings: { temperature: model_settings_1.TEMPERATURE.BUILDER },
|
|
1006
|
+
providerOptions: {
|
|
1007
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
1008
|
+
},
|
|
1009
|
+
};
|
|
1010
|
+
const stream = await subAgent.stream(briefing, {
|
|
1011
|
+
maxSteps: max_steps_1.MAX_STEPS.BUILDER,
|
|
1012
|
+
abortSignal: signal,
|
|
1013
|
+
modelSettings: { temperature: model_settings_1.TEMPERATURE.BUILDER },
|
|
1014
|
+
providerOptions: {
|
|
1015
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
1016
|
+
},
|
|
1017
|
+
...(llmStepTraceHooks?.executionOptions ?? {}),
|
|
372
1018
|
});
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
1019
|
+
return await (0, consume_with_hitl_1.consumeStreamWithHitl)({
|
|
1020
|
+
agent: subAgent,
|
|
1021
|
+
stream: stream,
|
|
1022
|
+
runId: context.runId,
|
|
1023
|
+
agentId: subAgentId,
|
|
1024
|
+
eventBus: context.eventBus,
|
|
1025
|
+
logger: context.logger,
|
|
1026
|
+
threadId: context.threadId,
|
|
1027
|
+
abortSignal: signal,
|
|
1028
|
+
waitForConfirmation: context.waitForConfirmation,
|
|
1029
|
+
drainCorrections,
|
|
1030
|
+
waitForCorrection,
|
|
1031
|
+
onActivity: () => context.touchBackgroundTask?.(taskId),
|
|
1032
|
+
llmStepTraceHooks,
|
|
1033
|
+
maxSteps: max_steps_1.MAX_STEPS.BUILDER,
|
|
1034
|
+
resumeOptions,
|
|
1035
|
+
});
|
|
1036
|
+
});
|
|
1037
|
+
const toolFinalText = await hitlResult.text;
|
|
1038
|
+
await promoteMainWorkflow(domainContext, context.logger, fallbackMainWorkflowId);
|
|
1039
|
+
return { text: toolFinalText };
|
|
1040
|
+
}
|
|
1041
|
+
finally {
|
|
1042
|
+
if (activeBuilderSession && context.builderSandboxSessionRegistry) {
|
|
1043
|
+
await context.builderSandboxSessionRegistry.release(activeBuilderSession.sessionId, {
|
|
1044
|
+
keep: !signal.aborted,
|
|
1045
|
+
reason: signal.aborted ? 'aborted' : 'builder_run_finished',
|
|
382
1046
|
});
|
|
383
|
-
if (recovered)
|
|
384
|
-
return recovered;
|
|
385
|
-
throw error;
|
|
386
|
-
}
|
|
387
|
-
const mainWorkflowAttempt = submitAttempts.get(mainWorkflowPath);
|
|
388
|
-
const currentMainWorkflow = await (0, sandbox_fs_1.readFileViaSandbox)(workspace, mainWorkflowPath);
|
|
389
|
-
const currentMainWorkflowHash = hashContent(currentMainWorkflow);
|
|
390
|
-
if (!mainWorkflowAttempt) {
|
|
391
|
-
const text = 'Error: workflow builder finished without submitting /src/workflow.ts.';
|
|
392
|
-
return {
|
|
393
|
-
text,
|
|
394
|
-
outcome: buildOutcome(workItemId, taskId, undefined, text),
|
|
395
|
-
};
|
|
396
1047
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const text = `Error: workflow builder stopped after a failed submit-workflow for /src/workflow.ts. ${errorText}`;
|
|
400
|
-
return {
|
|
401
|
-
text,
|
|
402
|
-
outcome: buildOutcome(workItemId, taskId, mainWorkflowAttempt, text),
|
|
403
|
-
};
|
|
1048
|
+
else {
|
|
1049
|
+
await builderWs?.cleanup();
|
|
404
1050
|
}
|
|
405
|
-
if (mainWorkflowAttempt.sourceHash !== currentMainWorkflowHash) {
|
|
406
|
-
const submitTool = tracedBuilderTools['submit-workflow'];
|
|
407
|
-
if (submitTool && 'execute' in submitTool) {
|
|
408
|
-
const resubmit = await submitTool.execute({
|
|
409
|
-
filePath: mainWorkflowPath,
|
|
410
|
-
workflowId: mainWorkflowAttempt.workflowId,
|
|
411
|
-
});
|
|
412
|
-
const refreshedAttempt = submitAttempts.get(mainWorkflowPath);
|
|
413
|
-
if (refreshedAttempt?.success) {
|
|
414
|
-
await promoteMainWorkflow(domainContext, context.logger, refreshedAttempt.workflowId);
|
|
415
|
-
return {
|
|
416
|
-
text: finalText,
|
|
417
|
-
outcome: buildOutcome(workItemId, taskId, refreshedAttempt, finalText),
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
const resubmitErrors = refreshedAttempt?.errors?.join(' ') ??
|
|
421
|
-
(typeof resubmit?.errors === 'string'
|
|
422
|
-
? resubmit.errors
|
|
423
|
-
: 'Auto-re-submit failed.');
|
|
424
|
-
const text = `Error: auto-re-submit of edited /src/workflow.ts failed. ${resubmitErrors}`;
|
|
425
|
-
return {
|
|
426
|
-
text,
|
|
427
|
-
outcome: buildOutcome(workItemId, taskId, refreshedAttempt ?? undefined, text),
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
await promoteMainWorkflow(domainContext, context.logger, mainWorkflowAttempt.workflowId);
|
|
432
|
-
return {
|
|
433
|
-
text: finalText,
|
|
434
|
-
outcome: buildOutcome(workItemId, taskId, mainWorkflowAttempt, finalText),
|
|
435
|
-
};
|
|
436
1051
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
await builderWs?.cleanup();
|
|
493
|
-
}
|
|
494
|
-
}),
|
|
1052
|
+
}),
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
catch (error) {
|
|
1056
|
+
if (reusedBuilderSession) {
|
|
1057
|
+
void context.builderSandboxSessionRegistry?.release(reusedBuilderSession.sessionId, {
|
|
1058
|
+
keep: true,
|
|
1059
|
+
reason: 'spawn_failed',
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
throw error;
|
|
1063
|
+
}
|
|
1064
|
+
if (spawnOutcome.status === 'duplicate') {
|
|
1065
|
+
if (reusedBuilderSession) {
|
|
1066
|
+
void context.builderSandboxSessionRegistry?.release(reusedBuilderSession.sessionId, {
|
|
1067
|
+
keep: true,
|
|
1068
|
+
reason: 'spawn_duplicate',
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
return {
|
|
1072
|
+
result: `Workflow build already in progress (task: ${spawnOutcome.existing.taskId}). Acknowledge and wait for the planned-task-follow-up — do not dispatch again.`,
|
|
1073
|
+
taskId: spawnOutcome.existing.taskId,
|
|
1074
|
+
agentId: spawnOutcome.existing.agentId,
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
if (spawnOutcome.status === 'limit-reached') {
|
|
1078
|
+
if (reusedBuilderSession) {
|
|
1079
|
+
void context.builderSandboxSessionRegistry?.release(reusedBuilderSession.sessionId, {
|
|
1080
|
+
keep: true,
|
|
1081
|
+
reason: 'spawn_limit_reached',
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
return {
|
|
1085
|
+
result: 'Could not start build: concurrent background-task limit reached. Wait for an existing task to finish and try again.',
|
|
1086
|
+
taskId: '',
|
|
1087
|
+
agentId: '',
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
context.eventBus.publish(context.threadId, {
|
|
1091
|
+
type: 'agent-spawned',
|
|
1092
|
+
runId: context.runId,
|
|
1093
|
+
agentId: subAgentId,
|
|
1094
|
+
payload: {
|
|
1095
|
+
parentId: context.orchestratorAgentId,
|
|
1096
|
+
role: 'workflow-builder',
|
|
1097
|
+
tools: Object.keys(builderTools),
|
|
1098
|
+
taskId,
|
|
1099
|
+
kind: 'builder',
|
|
1100
|
+
title: 'Building workflow',
|
|
1101
|
+
subtitle: (0, display_utils_1.truncateLabel)(input.task),
|
|
1102
|
+
goal: input.task,
|
|
1103
|
+
targetResource: input.workflowId
|
|
1104
|
+
? { type: 'workflow', id: input.workflowId }
|
|
1105
|
+
: { type: 'workflow' },
|
|
1106
|
+
},
|
|
495
1107
|
});
|
|
496
1108
|
return {
|
|
497
1109
|
result: `Workflow build started (task: ${taskId}). Reply with one short sentence — e.g. name what's being built. Do NOT summarize the plan or list details.`,
|
|
@@ -511,19 +1123,127 @@ exports.buildWorkflowAgentInputSchema = zod_1.z.object({
|
|
|
511
1123
|
.string()
|
|
512
1124
|
.optional()
|
|
513
1125
|
.describe('Brief summary of the conversation so far — what was discussed, decisions made, and information gathered (e.g., which credentials are available). The builder uses this to avoid repeating information the user already knows.'),
|
|
1126
|
+
workItemId: zod_1.z
|
|
1127
|
+
.string()
|
|
1128
|
+
.optional()
|
|
1129
|
+
.describe('Workflow-loop work item ID. Required for repair builds so remediation budgets continue on the same work item.'),
|
|
1130
|
+
bypassPlan: zod_1.z
|
|
1131
|
+
.boolean()
|
|
1132
|
+
.optional()
|
|
1133
|
+
.describe('Set to true for any edit to an existing workflow — adding/removing/rewiring a node, changing an expression, swapping a credential, changing a schedule, fixing a Code node. Requires an existing `workflowId` and a one-sentence `reason`. The orchestrator verifies the result afterwards via `verify-built-workflow` when the trigger is mockable. ' +
|
|
1134
|
+
'A runtime guard rejects direct calls without `bypassPlan: true` outside replan/checkpoint follow-ups: new workflow builds, multi-workflow work, and data-table schema changes must go through `plan` so the build gets its orchestrator-run checkpoint.'),
|
|
1135
|
+
reason: zod_1.z
|
|
1136
|
+
.string()
|
|
1137
|
+
.optional()
|
|
1138
|
+
.describe('One sentence explaining why the planner is being bypassed (e.g. "swap Slack channel on workflow X", "fix Code node shape issue"). Required when bypassPlan is true.'),
|
|
1139
|
+
});
|
|
1140
|
+
const buildWorkflowAgentSuspendSchema = zod_1.z.object({
|
|
1141
|
+
requestId: zod_1.z.string(),
|
|
1142
|
+
message: zod_1.z.string(),
|
|
1143
|
+
severity: zod_1.z.literal('warning'),
|
|
514
1144
|
});
|
|
1145
|
+
const buildWorkflowAgentResumeSchema = zod_1.z.object({
|
|
1146
|
+
approved: zod_1.z.boolean(),
|
|
1147
|
+
});
|
|
1148
|
+
function isPostPlanFollowUp(context) {
|
|
1149
|
+
return context.isReplanFollowUp === true || context.isCheckpointFollowUp === true;
|
|
1150
|
+
}
|
|
1151
|
+
function isBuildViaPlanGuardEnabled() {
|
|
1152
|
+
const raw = process.env.N8N_INSTANCE_AI_ENFORCE_BUILD_VIA_PLAN;
|
|
1153
|
+
if (raw === undefined)
|
|
1154
|
+
return true;
|
|
1155
|
+
return raw.toLowerCase() !== 'false' && raw !== '0';
|
|
1156
|
+
}
|
|
1157
|
+
async function resolveWorkflowNameForEditConfirmation(context, workflowId) {
|
|
1158
|
+
try {
|
|
1159
|
+
const workflow = await context.domainContext?.workflowService.get(workflowId);
|
|
1160
|
+
const workflowName = workflow?.name?.trim();
|
|
1161
|
+
return workflowName && workflowName.length > 0 ? workflowName : workflowId;
|
|
1162
|
+
}
|
|
1163
|
+
catch {
|
|
1164
|
+
return workflowId;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
515
1167
|
function createBuildWorkflowAgentTool(context) {
|
|
516
1168
|
return (0, tools_1.createTool)({
|
|
517
1169
|
id: 'build-workflow-with-agent',
|
|
518
1170
|
description: 'Build or modify an n8n workflow using a specialized builder agent. ' +
|
|
519
|
-
'The agent handles node discovery, schema lookups, code generation, ' +
|
|
520
|
-
'and
|
|
1171
|
+
'The agent handles node discovery, schema lookups, code generation, and validation internally. ' +
|
|
1172
|
+
'For edits to an existing workflow, call directly with `bypassPlan: true`, the existing `workflowId`, and a one-sentence `reason` — the orchestrator runs a lightweight verify afterwards. ' +
|
|
1173
|
+
'For new workflows, multi-workflow builds, or data-table schema changes, go through `plan` — ' +
|
|
1174
|
+
'a runtime guard rejects direct calls without `bypassPlan: true` outside replan/checkpoint follow-ups, because those paths need the orchestrator-run checkpoint for end-to-end verification.',
|
|
521
1175
|
inputSchema: exports.buildWorkflowAgentInputSchema,
|
|
522
1176
|
outputSchema: zod_1.z.object({
|
|
523
1177
|
result: zod_1.z.string(),
|
|
524
1178
|
taskId: zod_1.z.string(),
|
|
525
1179
|
}),
|
|
526
|
-
|
|
1180
|
+
suspendSchema: buildWorkflowAgentSuspendSchema,
|
|
1181
|
+
resumeSchema: buildWorkflowAgentResumeSchema,
|
|
1182
|
+
execute: async (input, ctx) => {
|
|
1183
|
+
const isPostPlanFollowUpRun = isPostPlanFollowUp(context);
|
|
1184
|
+
if (isBuildViaPlanGuardEnabled() && !isPostPlanFollowUpRun) {
|
|
1185
|
+
if (!input.bypassPlan) {
|
|
1186
|
+
context.logger.warn('build-workflow-with-agent called outside plan/replan context — rejecting', {
|
|
1187
|
+
threadId: context.threadId,
|
|
1188
|
+
hasWorkflowId: Boolean(input.workflowId),
|
|
1189
|
+
});
|
|
1190
|
+
return {
|
|
1191
|
+
result: 'Error: direct builder calls require `bypassPlan: true` + an existing ' +
|
|
1192
|
+
'`workflowId` + a one-sentence `reason`. Use that combination for any edit to ' +
|
|
1193
|
+
'an existing workflow. For new workflows, multi-workflow builds, or data-table ' +
|
|
1194
|
+
'schema changes, call `plan` with a `build-workflow` task instead — the planner ' +
|
|
1195
|
+
'discovers credentials, data tables, and best practices, and schedules an ' +
|
|
1196
|
+
'orchestrator-run verification checkpoint.',
|
|
1197
|
+
taskId: '',
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
if (!input.workflowId) {
|
|
1201
|
+
return {
|
|
1202
|
+
result: 'Error: `bypassPlan: true` is for edits to an EXISTING workflow and requires a ' +
|
|
1203
|
+
'`workflowId`. New workflow builds must go through `plan` so an orchestrator-run ' +
|
|
1204
|
+
'verification checkpoint is scheduled. Call `plan` with a `build-workflow` task ' +
|
|
1205
|
+
'instead.',
|
|
1206
|
+
taskId: '',
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
if (!input.reason || input.reason.trim().length === 0) {
|
|
1210
|
+
return {
|
|
1211
|
+
result: 'Error: `bypassPlan: true` requires a one-sentence `reason` describing the edit ' +
|
|
1212
|
+
'(e.g. "swap Slack channel", "fix Code node shape issue").',
|
|
1213
|
+
taskId: '',
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
context.logger.warn('build-workflow-with-agent bypassing plan with bypassPlan=true', {
|
|
1217
|
+
threadId: context.threadId,
|
|
1218
|
+
workflowId: input.workflowId,
|
|
1219
|
+
reason: input.reason,
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
if (input.workflowId && !isPostPlanFollowUpRun && context.domainContext) {
|
|
1223
|
+
const updateWorkflowPermission = context.domainContext.permissions?.updateWorkflow ?? 'require_approval';
|
|
1224
|
+
if (updateWorkflowPermission === 'blocked') {
|
|
1225
|
+
return { result: 'Action blocked by admin', taskId: '' };
|
|
1226
|
+
}
|
|
1227
|
+
const isOwnInFlightWorkflow = context.domainContext.aiCreatedWorkflowIds?.has(input.workflowId) ?? false;
|
|
1228
|
+
if (!isOwnInFlightWorkflow) {
|
|
1229
|
+
const resumeData = ctx?.agent?.resumeData;
|
|
1230
|
+
const suspend = ctx?.agent?.suspend;
|
|
1231
|
+
const needsApproval = updateWorkflowPermission !== 'always_allow';
|
|
1232
|
+
if (needsApproval && (resumeData === undefined || resumeData === null)) {
|
|
1233
|
+
const workflowName = await resolveWorkflowNameForEditConfirmation(context, input.workflowId);
|
|
1234
|
+
const reason = input.reason?.trim();
|
|
1235
|
+
await suspend?.({
|
|
1236
|
+
requestId: (0, nanoid_1.nanoid)(),
|
|
1237
|
+
message: `Edit existing workflow "${workflowName}" (ID: ${input.workflowId})?${reason ? ` Reason: ${reason}` : ''}`,
|
|
1238
|
+
severity: 'warning',
|
|
1239
|
+
});
|
|
1240
|
+
return { result: '', taskId: '' };
|
|
1241
|
+
}
|
|
1242
|
+
if (resumeData !== undefined && resumeData !== null && !resumeData.approved) {
|
|
1243
|
+
return { result: 'User declined the workflow edit.', taskId: '' };
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
527
1247
|
const result = await startBuildWorkflowAgentTask(context, input);
|
|
528
1248
|
return { result: result.result, taskId: result.taskId };
|
|
529
1249
|
},
|