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