@desplega.ai/agent-swarm 1.168.0 → 1.169.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/{acp-adapter-zhejfecx.js → acp-adapter-m47zfx4a.js} +2 -2
- package/dist/{actions-7jg840nr.js → actions-ws7k3tvq.js} +25 -19
- package/dist/{amp-adapter-ye6rnetm.js → amp-adapter-nvv8k8j5.js} +1 -1
- package/dist/{app-ffes2kyf.js → app-80w6qspx.js} +5 -5
- package/dist/{assistant-fhm5smyz.js → assistant-jtq439kw.js} +29 -20
- package/dist/{boot-reembed-20q3nzvg.js → boot-reembed-3nc0aaed.js} +4 -4
- package/dist/{boot-reembed-jvfadhsk.js → boot-reembed-feda8wn8.js} +4 -4
- package/dist/{boot-scrub-sweep-jtfajzve.js → boot-scrub-sweep-grsa2er0.js} +5 -5
- package/dist/{boot-seeds-9qt0j8j2.js → boot-seeds-gcfgq44f.js} +7 -7
- package/dist/{claude-adapter-a8znhthz.js → claude-adapter-pjv2q7t8.js} +7 -7
- package/dist/{claude-managed-adapter-tzwxy803.js → claude-managed-adapter-0wa8se82.js} +2 -2
- package/dist/{claude-managed-setup-nrhfx5b0.js → claude-managed-setup-zxa9p9tx.js} +1 -1
- package/dist/{claude-sdk-session-7x7chdrs.js → claude-sdk-session-c9j6k9kx.js} +7810 -7713
- package/dist/{cli-jb931ba2.js → cli-3ry5gx4f.js} +1 -1
- package/dist/{cli-ee0402n1.js → cli-61tbnjpf.js} +124 -2459
- package/dist/{cli-aj1e0h0d.js → cli-7vz49g7h.js} +101 -34
- package/dist/{cli-47zfzshm.js → cli-8xjst8q2.js} +34 -22
- package/dist/{cli-43jg9xdw.js → cli-91r17hb1.js} +3 -3
- package/dist/{cli-g6gx2dn1.js → cli-a8vps458.js} +156 -2207
- package/dist/{cli-tc5zec7v.js → cli-b7y8113e.js} +1 -1
- package/dist/{cli-69egj654.js → cli-bqrs5ycj.js} +1 -1
- package/dist/{cli-rr5z1j4p.js → cli-dv76vq5d.js} +28 -26
- package/dist/{cli-g5erf0zt.js → cli-e2prcx5m.js} +31 -0
- package/dist/{cli-j05x7pnb.js → cli-e5qk3m2a.js} +209 -31
- package/dist/{cli-bcspch6h.js → cli-eqfbsxsn.js} +3 -3
- package/dist/{cli-fhj4y16f.js → cli-fyeft04q.js} +1 -1
- package/dist/{cli-5qkcdv8c.js → cli-gmdajfw5.js} +3 -3
- package/dist/{cli-0jngabtb.js → cli-jkfxpg1b.js} +1 -1
- package/dist/{cli-mxvnnv5q.js → cli-jv36y8wz.js} +1 -1
- package/dist/{cli-fqq06kkb.js → cli-km8559dm.js} +6 -6
- package/dist/{cli-9q0bh411.js → cli-kp3vfknv.js} +1 -1
- package/dist/{cli-ng1pmdx6.js → cli-m23r88re.js} +8 -5
- package/dist/{cli-ewhwpyrb.js → cli-n4z21pt8.js} +1 -1
- package/dist/{cli-nctfv2yy.js → cli-qg2zhjfc.js} +3 -3
- package/dist/{cli-svah6b6w.js → cli-rbr91whd.js} +4 -4
- package/dist/{cli-74ez9b66.js → cli-rg1c8ccw.js} +92 -7
- package/dist/cli-rh44xx46.js +2659 -0
- package/dist/cli-s0k2j7q9.js +2084 -0
- package/dist/{cli-ew0c3a1j.js → cli-sajr098b.js} +1 -1
- package/dist/{cli-p5jqk8fr.js → cli-swrz3mj8.js} +1 -1
- package/dist/{cli-9q24yyvs.js → cli-vejds37t.js} +2 -2
- package/dist/{cli-jbzbr36n.js → cli-w6xa5nf5.js} +1 -1
- package/dist/{cli-9q3wt23f.js → cli-x6z90nq9.js} +1 -1
- package/dist/{cli-pgehz8e1.js → cli-ysnkvdx6.js} +102 -9
- package/dist/{cli-ytmmyj7f.js → cli-yvxfaem2.js} +2 -2
- package/dist/{cli-6bpvv8h4.js → cli-zzev32jw.js} +1 -1
- package/dist/cli.js +20 -17
- package/dist/{codex-adapter-n63k33d3.js → codex-adapter-0kpg09ca.js} +3 -3
- package/dist/{codex-hook-15dgc4cb.js → codex-hook-295jpbfa.js} +2 -2
- package/dist/{codex-session-runner-xkxfd81a.js → codex-session-runner-8n672hxh.js} +3 -3
- package/dist/{commands-q4mfhn51.js → commands-mhmg5n0x.js} +4 -4
- package/dist/{cursor-adapter-v8mm6bp5.js → cursor-adapter-wn8n5zzk.js} +2 -2
- package/dist/{db-1t0bj3pc.js → db-wa6s1mwv.js} +4 -4
- package/dist/{dispatcher-k1fhavcf.js → dispatcher-nhs2pkr8.js} +16 -14
- package/dist/{grok-adapter-phrndeb8.js → grok-adapter-28pkdt80.js} +3 -3
- package/dist/{handlers-59ywqwzh.js → handlers-5rtf9nng.js} +19 -17
- package/dist/{hook-mew1chq9.js → hook-krjfkpzy.js} +4 -4
- package/dist/{hook-atj0swc2.js → hook-qkm73zk6.js} +5 -5
- package/dist/{http-ef2fnn9v.js → http-a397xcn4.js} +4310 -3582
- package/dist/{index-gr69c43q.js → index-ac0v8v9d.js} +4 -4
- package/dist/{index-9fc2h7ww.js → index-kqre45fr.js} +16 -14
- package/dist/{index-w2yhye23.js → index-rtxg15ah.js} +17 -15
- package/dist/{index-48pb9erw.js → index-vvsq7v45.js} +26 -23
- package/dist/{index-h26wjcfy.js → index-ytdhtdry.js} +40 -11
- package/dist/{keepalive-qhcqz56v.js → keepalive-psfh8ysx.js} +6 -6
- package/dist/{lead-hpk5r3b1.js → lead-m6rppjn7.js} +28 -26
- package/dist/{maintenance-nvkyp2xe.js → maintenance-41f837ke.js} +4 -4
- package/dist/{node-qm9eb6z0.js → node-fkajaq96.js} +1 -1
- package/dist/{oauth-refresh-sweep-ngskthwh.js → oauth-refresh-sweep-6hr55hm8.js} +5 -5
- package/dist/{onboard-yfnkqhxc.js → onboard-ktrtd7cz.js} +2 -2
- package/dist/{otel-impl-hhm64r6m.js → otel-impl-dq4f2k3m.js} +1 -1
- package/dist/{pi-mono-adapter-0eg8kw2c.js → pi-mono-adapter-e5v9w0hv.js} +3 -2
- package/dist/{pricing-refresh-35f91nx2.js → pricing-refresh-kmk7j1ra.js} +6 -6
- package/dist/{rbac-roles-z6sje3gy.js → rbac-roles-9w1dkkxd.js} +4 -4
- package/dist/{rbac-roles-97w370qj.js → rbac-roles-9ykx9tc5.js} +5 -5
- package/dist/{render-v2-n4v66bj3.js → render-v2-ycqm53np.js} +5 -5
- package/dist/resume-cq2pkp8q.js +70 -0
- package/dist/{setup-m3hhyv5f.js → setup-0dcmdvbn.js} +2 -2
- package/dist/{virtual-modules-ftr9kwe8.js → virtual-modules-1x9k4jcs.js} +1 -1
- package/dist/{work-objects-mxf2c842.js → work-objects-x01x2fh8.js} +19 -17
- package/dist/{worker-9t07z1gq.js → worker-py1ssb1h.js} +28 -26
- package/openapi.json +15627 -15118
- package/package.json +8 -5
- package/src/apps/store.ts +1 -0
- package/src/artifact-sdk/feedback-overlay.ts +528 -0
- package/src/be/db/workflow-runs.ts +95 -0
- package/src/be/db-queries/slack-inbound.ts +489 -0
- package/src/be/db.ts +9 -2
- package/src/be/extensions/assets.ts +1 -1
- package/src/be/migrations/203_workflow_run_parent_step.sql +10 -0
- package/src/be/migrations/204_slack_inbound_receipts.sql +51 -0
- package/src/be/script-connections.ts +1 -1
- package/src/be/scripts/run-saved.ts +40 -20
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/extensions/catalog.ts +1 -1
- package/src/extensions/dispatcher.ts +3 -3
- package/src/heartbeat/heartbeat.ts +152 -2
- package/src/heartbeat/templates.ts +16 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/index.ts +2 -0
- package/src/http/integrations.ts +3 -1
- package/src/http/mcp-bridge.ts +1 -0
- package/src/http/pages-public.ts +7 -2
- package/src/http/pages.ts +178 -0
- package/src/http/slack-inbound.ts +107 -0
- package/src/http/tasks.ts +45 -27
- package/src/http/users.ts +1 -1
- package/src/http/webhooks.ts +20 -20
- package/src/prompts/session-templates.ts +32 -0
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +2 -2
- package/src/providers/claude-managed-adapter.ts +2 -11
- package/src/providers/claude-sdk-session.ts +1 -1
- package/src/providers/opencode-adapter.ts +1 -2
- package/src/providers/pi-mono-adapter.ts +3 -3
- package/src/realtime/browser.generated.txt +8 -8
- package/src/realtime/browser.ts +3 -3
- package/src/scripts-runtime/executors/quickjs-pool-config.ts +26 -0
- package/src/scripts-runtime/executors/quickjs.ts +9 -2
- package/src/server.ts +1 -0
- package/src/slack/actions.ts +6 -2
- package/src/slack/app.ts +5 -0
- package/src/slack/assistant.ts +15 -5
- package/src/slack/egress-scrub.ts +3 -2
- package/src/slack/handlers.ts +36 -18
- package/src/slack/inbound-dispatch.ts +549 -0
- package/src/slack/inbound-files.ts +2 -0
- package/src/telemetry-context.ts +1 -0
- package/src/tests/codex-oauth-device.test.ts +1 -1
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/heartbeat-checklist.test.ts +243 -0
- package/src/tests/pages-feedback.test.ts +218 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/script-executor-quickjs-pool.test.ts +106 -0
- package/src/tests/script-executor-registry.test.ts +38 -0
- package/src/tests/slack-inbound-diagnostics-auth.test.ts +93 -0
- package/src/tests/slack-inbound-receipts.test.ts +684 -0
- package/src/tests/slack-session-status.test.ts +4 -2
- package/src/tests/ui-e2e-ingest-payload.test.ts +5 -1
- package/src/tests/user-token-routes.test.ts +1 -1
- package/src/tests/workflow-executors.test.ts +3 -2
- package/src/tests/workflow-sub-workflow.test.ts +363 -0
- package/src/utils/internal-ai/complete-structured.ts +6 -3
- package/src/utils/runtime-model-catalog.ts +1 -1
- package/src/workflows/engine.ts +17 -1
- package/src/workflows/executors/agent-task.ts +2 -2
- package/src/workflows/executors/human-in-the-loop.ts +3 -3
- package/src/workflows/executors/registry.ts +2 -0
- package/src/workflows/executors/script.ts +1 -1
- package/src/workflows/executors/sub-workflow.ts +135 -0
- package/src/workflows/executors/wait.ts +3 -3
- package/src/workflows/recovery.ts +11 -1
- package/src/workflows/resume.ts +77 -0
- package/src/workflows/wait-filter.ts +5 -7
- package/templates/skills/heartbeat-runbook/content.md +3 -1
- package/templates/skills/pages/content.md +25 -0
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import {
|
|
21
21
|
ensurePricingSeeded,
|
|
22
22
|
ensureRbacSeeded
|
|
23
|
-
} from "./cli-
|
|
23
|
+
} from "./cli-91r17hb1.js";
|
|
24
24
|
import {
|
|
25
25
|
buildCatalogEntries,
|
|
26
26
|
getCatalogGeneration,
|
|
@@ -30,84 +30,67 @@ import {
|
|
|
30
30
|
requestModelCatalogRefresh,
|
|
31
31
|
startPricingRefreshLoop,
|
|
32
32
|
upsertModelCatalogOverlay
|
|
33
|
-
} from "./cli-
|
|
33
|
+
} from "./cli-vejds37t.js";
|
|
34
34
|
import {
|
|
35
35
|
loadModelsDevCache
|
|
36
|
-
} from "./cli-
|
|
36
|
+
} from "./cli-n4z21pt8.js";
|
|
37
37
|
import {
|
|
38
38
|
AppDefinitionSchema,
|
|
39
39
|
AppElementsSchema,
|
|
40
40
|
AppNameSchema,
|
|
41
|
-
BaseExecutor,
|
|
42
41
|
ColumnKindSchema,
|
|
43
42
|
ConnectionAuthInputSchema,
|
|
44
43
|
DEFAULT_SCRIPT_RESOURCES,
|
|
45
|
-
HumanInTheLoopExecutor,
|
|
46
44
|
MAX_SCRIPT_WALL_CLOCK_MS,
|
|
47
45
|
MIN_SCRIPT_WALL_CLOCK_MS,
|
|
48
46
|
SCRIPT_LONG_TIMEOUT_HINT_MS,
|
|
49
47
|
SORTABLE_SYSTEM_DATE_COLUMNS,
|
|
50
48
|
SYSTEM_COLUMN_KINDS,
|
|
51
|
-
SYSTEM_ONE_DECISION_NODE_TYPE,
|
|
52
|
-
SystemOneDecisionExecutor,
|
|
53
49
|
appDefinitionNeedsRepair,
|
|
54
50
|
applyAppDefinitionPatch,
|
|
55
|
-
applyDefinitionPatch,
|
|
56
51
|
assertUrlSafe,
|
|
57
52
|
buildSandboxedCommand,
|
|
58
53
|
buildScriptCredentialBindings,
|
|
59
54
|
buildScriptCredentialBindingsWithFailures,
|
|
60
55
|
connectionAuthInputFromFlat,
|
|
61
56
|
createApp,
|
|
62
|
-
createApprovalFollowUpTask,
|
|
63
57
|
createCappedStreamState,
|
|
64
58
|
decodeAppDefinition,
|
|
65
|
-
definitionNodeIds,
|
|
66
59
|
deleteApp,
|
|
67
60
|
deleteScriptConnection,
|
|
68
61
|
disableCredentialBinding,
|
|
69
62
|
extractArgsJsonSchema,
|
|
70
63
|
extractScriptSignature,
|
|
71
|
-
findEntryNodes,
|
|
72
|
-
generateEdges,
|
|
73
64
|
getApp,
|
|
74
|
-
getNextTargets,
|
|
75
65
|
getOAuthBindingTokenStatus,
|
|
76
66
|
getOAuthProviderConfig,
|
|
77
67
|
getSavedScriptOwnerAgentId,
|
|
78
68
|
getScriptApiConnectionDescriptors,
|
|
79
69
|
getScriptConnectionById,
|
|
80
70
|
getScriptMcpConnectionDescriptors,
|
|
81
|
-
getSuccessors,
|
|
82
71
|
isIso8601Date,
|
|
83
72
|
listApps,
|
|
84
73
|
listRelationalCredentialBindings,
|
|
85
74
|
listScriptConnections,
|
|
86
75
|
parseAppDefinition,
|
|
87
|
-
postApprovalCancellationUpdates,
|
|
88
76
|
publicEndpointSsrfOptions,
|
|
89
77
|
readStreamCapped,
|
|
90
78
|
refreshScriptConnection,
|
|
91
79
|
require_dist as require_dist2,
|
|
92
80
|
resolveSyncRunAs,
|
|
93
|
-
resolveValidationPort,
|
|
94
|
-
resolveWorkflowLlmConfig,
|
|
95
81
|
runSavedScriptAsAgent,
|
|
96
82
|
runScript,
|
|
97
83
|
sandboxSpawnEnv,
|
|
98
84
|
setScriptConnectionEnabled,
|
|
99
85
|
snapshotCapped,
|
|
100
|
-
systemOneRetryViolations,
|
|
101
|
-
systemOneUnresolvedError,
|
|
102
86
|
typecheckScript,
|
|
103
87
|
updateApp,
|
|
104
88
|
upgradeAppDefinition,
|
|
105
89
|
upsertCredentialBinding,
|
|
106
90
|
upsertScriptConnection,
|
|
107
|
-
validateDefinition,
|
|
108
91
|
validateScriptImports,
|
|
109
92
|
writeBareImportShim
|
|
110
|
-
} from "./cli-
|
|
93
|
+
} from "./cli-a8vps458.js";
|
|
111
94
|
import {
|
|
112
95
|
getApiKey
|
|
113
96
|
} from "./cli-f14fvzag.js";
|
|
@@ -122,12 +105,77 @@ import {
|
|
|
122
105
|
ensureToken,
|
|
123
106
|
ensureTokenOrThrow,
|
|
124
107
|
onAuthorizationRefreshed
|
|
125
|
-
} from "./cli-
|
|
108
|
+
} from "./cli-fyeft04q.js";
|
|
126
109
|
import {
|
|
127
110
|
AgentFsProvider,
|
|
128
111
|
getFileStorageProvider,
|
|
129
112
|
providerPath
|
|
130
113
|
} from "./cli-mzfth0bp.js";
|
|
114
|
+
import {
|
|
115
|
+
FAILED_TASK_OUTPUT_PREFIX,
|
|
116
|
+
FOREACH_TERMINAL_STEP_STATUSES,
|
|
117
|
+
SubWorkflowExecutor,
|
|
118
|
+
TriggerSchemaError,
|
|
119
|
+
WaitExecutor,
|
|
120
|
+
buildForeachAggregate,
|
|
121
|
+
buildNodeInterpolationCtx,
|
|
122
|
+
cancelWorkflowRun,
|
|
123
|
+
cancelWorkflowRunRows,
|
|
124
|
+
checkpointPortStepAndResolveSuccessors,
|
|
125
|
+
checkpointStep,
|
|
126
|
+
checkpointStepFailure,
|
|
127
|
+
checkpointStepWaiting,
|
|
128
|
+
completeTaskStepAndResolveSuccessors,
|
|
129
|
+
failRunOnUnreadableReplay,
|
|
130
|
+
failStepAndRunIfWaiting,
|
|
131
|
+
finalizeOrWait,
|
|
132
|
+
findJsonSchemaShapeErrors,
|
|
133
|
+
findReadyNodes,
|
|
134
|
+
getAutomationSetupStates,
|
|
135
|
+
getMaxWorkflowStepsPerRun,
|
|
136
|
+
getSecretInputKeys,
|
|
137
|
+
holdWorkflowRun,
|
|
138
|
+
initWaitBusSubscriptions,
|
|
139
|
+
interpolateNodeConfig,
|
|
140
|
+
isNeedsSetupFailure,
|
|
141
|
+
isWorkflowRunActive,
|
|
142
|
+
loadCompletedStepRouting,
|
|
143
|
+
notifyAutomationPreflightFailure,
|
|
144
|
+
parseSyntheticNodeId,
|
|
145
|
+
preflightAutomation,
|
|
146
|
+
recordSchedulePreflightFailure,
|
|
147
|
+
rehydrateCompletedStepOutputs,
|
|
148
|
+
renderAutomationTokens,
|
|
149
|
+
resolveInputValue,
|
|
150
|
+
resumeFromChildRun,
|
|
151
|
+
resumeWaitState,
|
|
152
|
+
retryFailedRun,
|
|
153
|
+
runStepValidation,
|
|
154
|
+
schedulePreflightInput,
|
|
155
|
+
scheduleTaskStepRetry,
|
|
156
|
+
scriptBodyInterpolationError,
|
|
157
|
+
setupWorkflowResumeListener,
|
|
158
|
+
shapeApprovalResolution,
|
|
159
|
+
startWorkflowExecution,
|
|
160
|
+
validateJsonSchema,
|
|
161
|
+
walkGraph,
|
|
162
|
+
withSaveWarnings,
|
|
163
|
+
workflowSaveWarnings
|
|
164
|
+
} from "./cli-rh44xx46.js";
|
|
165
|
+
import {
|
|
166
|
+
BaseExecutor,
|
|
167
|
+
HumanInTheLoopExecutor,
|
|
168
|
+
SystemOneDecisionExecutor,
|
|
169
|
+
applyDefinitionPatch,
|
|
170
|
+
createApprovalFollowUpTask,
|
|
171
|
+
definitionNodeIds,
|
|
172
|
+
generateEdges,
|
|
173
|
+
getSuccessors,
|
|
174
|
+
postApprovalCancellationUpdates,
|
|
175
|
+
resolveValidationPort,
|
|
176
|
+
resolveWorkflowLlmConfig,
|
|
177
|
+
validateDefinition
|
|
178
|
+
} from "./cli-s0k2j7q9.js";
|
|
131
179
|
import {
|
|
132
180
|
ZodFirstPartyTypeKind,
|
|
133
181
|
objectType
|
|
@@ -148,6 +196,7 @@ import {
|
|
|
148
196
|
getTaskUrl,
|
|
149
197
|
hasTaskCitationCheckRefusal,
|
|
150
198
|
markdownToSlack,
|
|
199
|
+
noteSlackInboundSideEffect,
|
|
151
200
|
renderTaskCitations,
|
|
152
201
|
startSlackApp,
|
|
153
202
|
stopSlackApp,
|
|
@@ -157,11 +206,11 @@ import {
|
|
|
157
206
|
taskCitationWarnings,
|
|
158
207
|
upsertTaskCitations,
|
|
159
208
|
withSpan
|
|
160
|
-
} from "./cli-
|
|
209
|
+
} from "./cli-e5qk3m2a.js";
|
|
161
210
|
import {
|
|
162
211
|
getUserGrant,
|
|
163
212
|
init_rbac_roles
|
|
164
|
-
} from "./cli-
|
|
213
|
+
} from "./cli-jkfxpg1b.js";
|
|
165
214
|
import {
|
|
166
215
|
can,
|
|
167
216
|
decideAdmission,
|
|
@@ -183,7 +232,6 @@ import {
|
|
|
183
232
|
MEMORY_KEY_PATTERN,
|
|
184
233
|
MEMORY_KEY_PATTERN_MESSAGE,
|
|
185
234
|
SteeringRequestError,
|
|
186
|
-
WorkflowReplayStateError,
|
|
187
235
|
acceptTask,
|
|
188
236
|
agentContextKey,
|
|
189
237
|
agentmailContextKey,
|
|
@@ -193,7 +241,6 @@ import {
|
|
|
193
241
|
buildJiraContextKey,
|
|
194
242
|
buildRoutingAffinityFromAgent,
|
|
195
243
|
cancelApprovalRequestById,
|
|
196
|
-
cancelPendingApprovalRequestsForRun,
|
|
197
244
|
cancelTask,
|
|
198
245
|
checkDependencies,
|
|
199
246
|
checkSlackRoutingCoherence,
|
|
@@ -221,8 +268,6 @@ import {
|
|
|
221
268
|
createTaskExtended,
|
|
222
269
|
createUser,
|
|
223
270
|
createWorkflow,
|
|
224
|
-
createWorkflowRun,
|
|
225
|
-
createWorkflowRunStep,
|
|
226
271
|
createWorkflowVersion,
|
|
227
272
|
deleteAgentMailInboxMapping,
|
|
228
273
|
deleteChannel,
|
|
@@ -238,7 +283,6 @@ import {
|
|
|
238
283
|
deleteSwarmConfig,
|
|
239
284
|
deleteUser,
|
|
240
285
|
deleteWorkflow,
|
|
241
|
-
detachTaskFromWorkflowRunStep,
|
|
242
286
|
embedScript,
|
|
243
287
|
emitMcpServerConnectedTelemetry,
|
|
244
288
|
exports_db,
|
|
@@ -275,7 +319,6 @@ import {
|
|
|
275
319
|
getCompletedStepNodeIds,
|
|
276
320
|
getContextVersion,
|
|
277
321
|
getContextVersionHistory,
|
|
278
|
-
getCurrentStepForNode,
|
|
279
322
|
getDailySpendForAgent,
|
|
280
323
|
getDailySpendForUser,
|
|
281
324
|
getDailySpendGlobal,
|
|
@@ -290,9 +333,6 @@ import {
|
|
|
290
333
|
getInboxSummary,
|
|
291
334
|
getInjectableGlobalConfigs,
|
|
292
335
|
getKv,
|
|
293
|
-
getLastRunStart,
|
|
294
|
-
getLastSuccessfulRun,
|
|
295
|
-
getLatestStepForNode,
|
|
296
336
|
getLeadAgent,
|
|
297
337
|
getLogsByTaskIdAndEventType,
|
|
298
338
|
getLogsByTaskIdChronological,
|
|
@@ -311,9 +351,7 @@ import {
|
|
|
311
351
|
getPage,
|
|
312
352
|
getPageBySlug,
|
|
313
353
|
getPageVersions,
|
|
314
|
-
getPendingEventWaitNames,
|
|
315
354
|
getPendingTaskForAgent,
|
|
316
|
-
getPendingWaitsByEvent,
|
|
317
355
|
getPromptTemplateById,
|
|
318
356
|
getPromptTemplateHistory,
|
|
319
357
|
getPromptTemplates,
|
|
@@ -330,14 +368,13 @@ import {
|
|
|
330
368
|
getServiceByAgentAndName,
|
|
331
369
|
getServiceById,
|
|
332
370
|
getSessionLogsByTaskId,
|
|
371
|
+
getSettledChildRunParentSteps,
|
|
333
372
|
getSkillById,
|
|
334
373
|
getSkillByName,
|
|
335
374
|
getSkillFile,
|
|
336
375
|
getSkillFiles,
|
|
337
376
|
getSlackTreeMessageByThread,
|
|
338
377
|
getSteeringMessageById,
|
|
339
|
-
getStepByIdempotencyKey,
|
|
340
|
-
getStepCountForNode,
|
|
341
378
|
getStuckApprovalRuns,
|
|
342
379
|
getStuckWaitRuns,
|
|
343
380
|
getStuckWorkflowRuns,
|
|
@@ -348,11 +385,9 @@ import {
|
|
|
348
385
|
getSwarmRepos,
|
|
349
386
|
getTaskAttachments,
|
|
350
387
|
getTaskById,
|
|
351
|
-
getTaskByWorkflowRunStepId,
|
|
352
388
|
getUnassignedTasksCount,
|
|
353
389
|
getUnreadMessages,
|
|
354
390
|
getUserById,
|
|
355
|
-
getWaitStateById,
|
|
356
391
|
getWorkflow,
|
|
357
392
|
getWorkflowRun,
|
|
358
393
|
getWorkflowRunStep,
|
|
@@ -391,7 +426,7 @@ import {
|
|
|
391
426
|
init_telemetry,
|
|
392
427
|
init_telemetry_identity,
|
|
393
428
|
init_templates1 as init_templates,
|
|
394
|
-
|
|
429
|
+
init_workflow_runs,
|
|
395
430
|
insertTaskAttachment,
|
|
396
431
|
installMcpServer,
|
|
397
432
|
installSkill,
|
|
@@ -445,13 +480,11 @@ import {
|
|
|
445
480
|
resolveApprovalRequest,
|
|
446
481
|
resolveSqliteVecExtensionPath,
|
|
447
482
|
resolveStoreKey,
|
|
448
|
-
resolveWaitState,
|
|
449
483
|
restoreScratchScriptLastUsedIfUnchanged,
|
|
450
484
|
rowToCandidate,
|
|
451
485
|
runWithoutRequestAuth,
|
|
452
486
|
scheduleContextKey,
|
|
453
487
|
scheduleOrgDomainRecompute,
|
|
454
|
-
scrubJsonValue,
|
|
455
488
|
searchSkills,
|
|
456
489
|
setBudgetRefusalFollowUpTaskId,
|
|
457
490
|
setRequestAuth,
|
|
@@ -480,7 +513,6 @@ import {
|
|
|
480
513
|
updateUser,
|
|
481
514
|
updateWorkflow,
|
|
482
515
|
updateWorkflowRun,
|
|
483
|
-
updateWorkflowRunStep,
|
|
484
516
|
upsertKv,
|
|
485
517
|
upsertOAuthApp,
|
|
486
518
|
upsertPromptTemplate,
|
|
@@ -490,7 +522,7 @@ import {
|
|
|
490
522
|
upsertSwarmConfig,
|
|
491
523
|
validateConfigValue,
|
|
492
524
|
workflowContextKey
|
|
493
|
-
} from "./cli-
|
|
525
|
+
} from "./cli-rg1c8ccw.js";
|
|
494
526
|
import {
|
|
495
527
|
$OpenAPIRegistry,
|
|
496
528
|
$OpenApiGeneratorV31,
|
|
@@ -525,7 +557,6 @@ import {
|
|
|
525
557
|
ReasoningEffortSchema,
|
|
526
558
|
RepoGuidelinesInputSchema,
|
|
527
559
|
RepoHooksSchema,
|
|
528
|
-
RetryPolicySchema,
|
|
529
560
|
RoutingReasonSchema,
|
|
530
561
|
ScheduledTaskTargetTypeSchema,
|
|
531
562
|
ScriptRunStatusSchema,
|
|
@@ -559,11 +590,10 @@ import {
|
|
|
559
590
|
init_task_attachment_links,
|
|
560
591
|
taskAttachmentDisplayUrl,
|
|
561
592
|
taskAttachmentFetchCommand
|
|
562
|
-
} from "./cli-
|
|
593
|
+
} from "./cli-jv36y8wz.js";
|
|
563
594
|
import {
|
|
564
595
|
getSlackConfiguration,
|
|
565
|
-
init_config
|
|
566
|
-
isSlackConfigured
|
|
596
|
+
init_config
|
|
567
597
|
} from "./cli-dmtrxqhg.js";
|
|
568
598
|
import {
|
|
569
599
|
deepInterpolate,
|
|
@@ -654,7 +684,7 @@ import {
|
|
|
654
684
|
import {
|
|
655
685
|
init_package,
|
|
656
686
|
package_default
|
|
657
|
-
} from "./cli-
|
|
687
|
+
} from "./cli-m23r88re.js";
|
|
658
688
|
import {
|
|
659
689
|
init_event_bus,
|
|
660
690
|
workflowEventBus
|
|
@@ -665,7 +695,6 @@ import {
|
|
|
665
695
|
} from "./cli-zf8jjwex.js";
|
|
666
696
|
import {
|
|
667
697
|
init_secret_scrubber,
|
|
668
|
-
isSensitiveKey,
|
|
669
698
|
refreshSecretScrubberCache,
|
|
670
699
|
registerSensitiveKeyName,
|
|
671
700
|
registerVolatileSecret,
|
|
@@ -2466,8 +2495,8 @@ var require_keyword = __commonJS(function(exports) {
|
|
|
2466
2495
|
var _a;
|
|
2467
2496
|
const { gen, keyword, schema, parentSchema, $data, it } = cxt;
|
|
2468
2497
|
checkAsyncKeyword(it, def);
|
|
2469
|
-
const
|
|
2470
|
-
const validateRef = useKeyword(gen, keyword,
|
|
2498
|
+
const validate = !$data && def.compile ? def.compile.call(it.self, schema, parentSchema, it) : def.validate;
|
|
2499
|
+
const validateRef = useKeyword(gen, keyword, validate);
|
|
2471
2500
|
const valid = gen.let("valid");
|
|
2472
2501
|
cxt.block$data(valid, validateKeyword);
|
|
2473
2502
|
cxt.ok((_a = def.valid) !== null && _a !== undefined ? _a : valid);
|
|
@@ -3526,28 +3555,28 @@ var require_compile = __commonJS(function(exports) {
|
|
|
3526
3555
|
if (this.opts.code.process)
|
|
3527
3556
|
sourceCode = this.opts.code.process(sourceCode, sch);
|
|
3528
3557
|
const makeValidate = new Function(`${names_1.default.self}`, `${names_1.default.scope}`, sourceCode);
|
|
3529
|
-
const
|
|
3530
|
-
this.scope.value(validateName, { ref:
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3558
|
+
const validate = makeValidate(this, this.scope.get());
|
|
3559
|
+
this.scope.value(validateName, { ref: validate });
|
|
3560
|
+
validate.errors = null;
|
|
3561
|
+
validate.schema = sch.schema;
|
|
3562
|
+
validate.schemaEnv = sch;
|
|
3534
3563
|
if (sch.$async)
|
|
3535
|
-
|
|
3564
|
+
validate.$async = true;
|
|
3536
3565
|
if (this.opts.code.source === true) {
|
|
3537
|
-
|
|
3566
|
+
validate.source = { validateName, validateCode, scopeValues: gen._values };
|
|
3538
3567
|
}
|
|
3539
3568
|
if (this.opts.unevaluated) {
|
|
3540
3569
|
const { props, items } = schemaCxt;
|
|
3541
|
-
|
|
3570
|
+
validate.evaluated = {
|
|
3542
3571
|
props: props instanceof codegen_1.Name ? undefined : props,
|
|
3543
3572
|
items: items instanceof codegen_1.Name ? undefined : items,
|
|
3544
3573
|
dynamicProps: props instanceof codegen_1.Name,
|
|
3545
3574
|
dynamicItems: items instanceof codegen_1.Name
|
|
3546
3575
|
};
|
|
3547
|
-
if (
|
|
3548
|
-
|
|
3576
|
+
if (validate.source)
|
|
3577
|
+
validate.source.evaluated = (0, codegen_1.stringify)(validate.evaluated);
|
|
3549
3578
|
}
|
|
3550
|
-
sch.validate =
|
|
3579
|
+
sch.validate = validate;
|
|
3551
3580
|
return sch;
|
|
3552
3581
|
} catch (e) {
|
|
3553
3582
|
delete sch.validate;
|
|
@@ -6862,8 +6891,8 @@ var require_ajv = __commonJS(function(exports, module) {
|
|
|
6862
6891
|
var require_formats = __commonJS(function(exports) {
|
|
6863
6892
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6864
6893
|
exports.formatNames = exports.fastFormats = exports.fullFormats = undefined;
|
|
6865
|
-
function fmtDef(
|
|
6866
|
-
return { validate
|
|
6894
|
+
function fmtDef(validate, compare) {
|
|
6895
|
+
return { validate, compare };
|
|
6867
6896
|
}
|
|
6868
6897
|
exports.fullFormats = {
|
|
6869
6898
|
date: fmtDef(date2, compareDate),
|
|
@@ -7303,10 +7332,10 @@ var require_validate2 = __commonJS(function(exports) {
|
|
|
7303
7332
|
function _interopRequireDefault(e) {
|
|
7304
7333
|
return e && e.__esModule ? e : { default: e };
|
|
7305
7334
|
}
|
|
7306
|
-
function
|
|
7335
|
+
function validate(uuid2) {
|
|
7307
7336
|
return typeof uuid2 === "string" && _regex.default.test(uuid2);
|
|
7308
7337
|
}
|
|
7309
|
-
var _default = exports.default =
|
|
7338
|
+
var _default = exports.default = validate;
|
|
7310
7339
|
});
|
|
7311
7340
|
|
|
7312
7341
|
// node_modules/uuid/dist/parse.js
|
|
@@ -14384,2393 +14413,27 @@ var require_dist5 = __commonJS(function(exports) {
|
|
|
14384
14413
|
}
|
|
14385
14414
|
exports.Svix = Svix;
|
|
14386
14415
|
});
|
|
14387
|
-
// src/automation-preflight-alert.ts
|
|
14388
|
-
init_constants();
|
|
14389
|
-
init_secret_scrubber();
|
|
14390
|
-
async function notifyAutomationPreflightFailure(preflight) {
|
|
14391
|
-
const channel = process.env.SLACK_ALERTS_CHANNEL?.trim();
|
|
14392
|
-
if (!channel) {
|
|
14393
|
-
console.warn("[Automation Preflight] SLACK_ALERTS_CHANNEL not set; skipping alert");
|
|
14394
|
-
return;
|
|
14395
|
-
}
|
|
14396
|
-
try {
|
|
14397
|
-
const { getSlackApp: getSlackApp2 } = await import("./app-ffes2kyf.js");
|
|
14398
|
-
const app = getSlackApp2();
|
|
14399
|
-
if (!app) {
|
|
14400
|
-
console.warn("[Automation Preflight] Slack not available, cannot send notification");
|
|
14401
|
-
return;
|
|
14402
|
-
}
|
|
14403
|
-
await app.client.chat.postMessage({
|
|
14404
|
-
channel,
|
|
14405
|
-
text: scrubSecrets(`⚠️ Automation needs setup
|
|
14406
|
-
` + `Automation: ${preflight.kind} "${preflight.name}"
|
|
14407
|
-
Missing params: ${preflight.missing.params.join(", ") || "none"}
|
|
14408
|
-
Missing integrations: ${preflight.missing.integrations.join(", ") || "none"}
|
|
14409
|
-
Fire SKIPPED. It was not retried or queued.
|
|
14410
|
-
Fix setup: ${getAppUrl()}${preflight.fixUrl}`),
|
|
14411
|
-
unfurl_links: false,
|
|
14412
|
-
unfurl_media: false
|
|
14413
|
-
});
|
|
14414
|
-
} catch (error) {
|
|
14415
|
-
console.error("[Automation Preflight] Failed to send Slack notification:", scrubSecrets(error instanceof Error ? error.message : String(error)));
|
|
14416
|
-
}
|
|
14417
|
-
}
|
|
14418
|
-
|
|
14419
|
-
// src/be/automation-preflight.ts
|
|
14420
|
-
init_config();
|
|
14421
|
-
init_env_flag();
|
|
14422
|
-
init_secret_scrubber();
|
|
14423
|
-
init_db2();
|
|
14424
|
-
init_oauth();
|
|
14425
|
-
init_scrub_json();
|
|
14426
|
-
var NEEDS_SETUP_PREFIX = "needs_setup:";
|
|
14427
|
-
function hasValue(value) {
|
|
14428
|
-
if (value == null)
|
|
14429
|
-
return false;
|
|
14430
|
-
if (typeof value === "string")
|
|
14431
|
-
return value.trim().length > 0;
|
|
14432
|
-
if (Array.isArray(value))
|
|
14433
|
-
return value.length > 0;
|
|
14434
|
-
return true;
|
|
14435
|
-
}
|
|
14436
|
-
var GITHUB_REPOSITORY_SLUG = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\/[A-Za-z0-9][A-Za-z0-9._-]{0,99}(?:\.git)?$/;
|
|
14437
|
-
var SAFE_SHELL_DATA = /^[A-Za-z0-9._~%/:@ -]+$/;
|
|
14438
|
-
var DNS_NAME = /^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}$/;
|
|
14439
|
-
var SAFE_EXTERNAL_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|
|
14440
|
-
var SAFE_GIT_REF = /^(?!-)(?!.*(?:\.\.|@\{|\/\/))[A-Za-z0-9._/-]+$/;
|
|
14441
|
-
var SAFE_TAG_PATTERN = /^(?!-)(?!.*(?:\.\.|\/\/))[A-Za-z0-9._*?/-]+$/;
|
|
14442
|
-
var SAFE_RELATIVE_PATH = /^(?![-/])(?!.*(?:^|\/)\.\.(?:\/|$))[A-Za-z0-9._/-]+$/;
|
|
14443
|
-
var SAFE_REPORT_NAME = /^(?!\.{1,2}$)[A-Za-z0-9][A-Za-z0-9._ -]{0,127}$/;
|
|
14444
|
-
function isSafeRepository(value) {
|
|
14445
|
-
if (!SAFE_SHELL_DATA.test(value) || value.trim() !== value || value.includes(" "))
|
|
14446
|
-
return false;
|
|
14447
|
-
const slug = value.replace(/^https:\/\/github\.com\//, "").replace(/^git@github\.com:/, "").replace(/\/$/, "");
|
|
14448
|
-
return GITHUB_REPOSITORY_SLUG.test(slug);
|
|
14449
|
-
}
|
|
14450
|
-
function isSafeGscProperty(value) {
|
|
14451
|
-
if (!SAFE_SHELL_DATA.test(value) || value.trim() !== value)
|
|
14452
|
-
return false;
|
|
14453
|
-
return value.split(" ").every((property) => {
|
|
14454
|
-
if (!property)
|
|
14455
|
-
return false;
|
|
14456
|
-
if (property.startsWith("sc-domain:"))
|
|
14457
|
-
return DNS_NAME.test(property.slice(10));
|
|
14458
|
-
if (DNS_NAME.test(property))
|
|
14459
|
-
return true;
|
|
14460
|
-
try {
|
|
14461
|
-
const url = new URL(property);
|
|
14462
|
-
return url.protocol === "https:" && !url.username && !url.password && !url.port && !url.search && !url.hash && DNS_NAME.test(url.hostname);
|
|
14463
|
-
} catch {
|
|
14464
|
-
return false;
|
|
14465
|
-
}
|
|
14466
|
-
});
|
|
14467
|
-
}
|
|
14468
|
-
var PARAM_VALIDATORS = {
|
|
14469
|
-
REPO_URL: (value) => typeof value === "string" && isSafeRepository(value),
|
|
14470
|
-
GSC_PROPERTY: (value) => typeof value === "string" && isSafeGscProperty(value),
|
|
14471
|
-
BRANCH: (value) => typeof value === "string" && SAFE_GIT_REF.test(value),
|
|
14472
|
-
SCOPE_PATH: (value) => typeof value === "string" && SAFE_RELATIVE_PATH.test(value),
|
|
14473
|
-
REPORT_NAME: (value) => typeof value === "string" && value.trim() === value && SAFE_REPORT_NAME.test(value),
|
|
14474
|
-
TAG_PATTERN: (value) => typeof value === "string" && SAFE_TAG_PATTERN.test(value),
|
|
14475
|
-
AGENT_FS_ORG_ID: (value) => typeof value === "string" && SAFE_EXTERNAL_ID.test(value),
|
|
14476
|
-
ORG_ID: (value) => typeof value === "string" && SAFE_EXTERNAL_ID.test(value)
|
|
14477
|
-
};
|
|
14478
|
-
function isUsableParam(key, value) {
|
|
14479
|
-
if (!hasValue(value))
|
|
14480
|
-
return false;
|
|
14481
|
-
return PARAM_VALIDATORS[key]?.(value) ?? true;
|
|
14482
|
-
}
|
|
14483
|
-
function uniqueSorted(values) {
|
|
14484
|
-
return [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
|
14485
|
-
}
|
|
14486
|
-
function automationUrl(input) {
|
|
14487
|
-
return input.kind === "schedule" ? `/schedules/${input.id}` : `/workflows/${input.id}`;
|
|
14488
|
-
}
|
|
14489
|
-
var INTEGRATION_FIX_URL = {
|
|
14490
|
-
slack: "/settings/integrations/slack",
|
|
14491
|
-
github: "/settings/integrations/github",
|
|
14492
|
-
linear: "/settings/integrations/linear",
|
|
14493
|
-
jira: "/settings/integrations/jira",
|
|
14494
|
-
agentmail: "/settings/integrations/agentmail",
|
|
14495
|
-
gsc: "/settings/integrations/gsc",
|
|
14496
|
-
agentfs: "/settings/integrations/agentfs"
|
|
14497
|
-
};
|
|
14498
|
-
var INTEGRATION_FIRST_REQUIREMENTS = new Set([
|
|
14499
|
-
"gsc",
|
|
14500
|
-
"linear",
|
|
14501
|
-
"agentmail"
|
|
14502
|
-
]);
|
|
14503
|
-
function automationIntegrationFixUrl(id) {
|
|
14504
|
-
return INTEGRATION_FIX_URL[id];
|
|
14505
|
-
}
|
|
14506
|
-
function isNeedsSetupFailure(reason) {
|
|
14507
|
-
return reason?.startsWith(NEEDS_SETUP_PREFIX) ?? false;
|
|
14508
|
-
}
|
|
14509
|
-
function preflightAutomation(input, setup) {
|
|
14510
|
-
const params = input.params ?? {};
|
|
14511
|
-
const guardedParams = Object.keys(params).filter((key) => (key in PARAM_VALIDATORS));
|
|
14512
|
-
const missingParams = uniqueSorted([...input.requiredParams ?? [], ...guardedParams].filter((key) => !isUsableParam(key, params[key])));
|
|
14513
|
-
const missingIntegrations = uniqueSorted((input.requires ?? []).filter((id) => setup[id] === "unverified"));
|
|
14514
|
-
const missing = { params: missingParams, integrations: missingIntegrations };
|
|
14515
|
-
const baseUrl = automationUrl(input);
|
|
14516
|
-
const fixes = [
|
|
14517
|
-
...missingParams.map((key) => ({
|
|
14518
|
-
type: "param",
|
|
14519
|
-
key,
|
|
14520
|
-
url: `${baseUrl}?param=${encodeURIComponent(key)}`
|
|
14521
|
-
})),
|
|
14522
|
-
...missingIntegrations.map((key) => ({
|
|
14523
|
-
type: "integration",
|
|
14524
|
-
key,
|
|
14525
|
-
url: automationIntegrationFixUrl(key)
|
|
14526
|
-
}))
|
|
14527
|
-
];
|
|
14528
|
-
if (missingParams.length === 0 && missingIntegrations.length === 0) {
|
|
14529
|
-
return {
|
|
14530
|
-
id: input.id,
|
|
14531
|
-
name: input.name,
|
|
14532
|
-
kind: input.kind,
|
|
14533
|
-
state: "running",
|
|
14534
|
-
missing,
|
|
14535
|
-
fixes,
|
|
14536
|
-
fixUrl: baseUrl
|
|
14537
|
-
};
|
|
14538
|
-
}
|
|
14539
|
-
const firstParam = missingParams[0];
|
|
14540
|
-
const firstIntegration = (input.requires ?? []).find((id) => missingIntegrations.includes(id));
|
|
14541
|
-
const integrationFirst = (input.requires ?? []).some((id) => INTEGRATION_FIRST_REQUIREMENTS.has(id));
|
|
14542
|
-
const fixUrl = integrationFirst && firstIntegration ? automationIntegrationFixUrl(firstIntegration) : firstParam ? `${baseUrl}?param=${encodeURIComponent(firstParam)}` : firstIntegration ? automationIntegrationFixUrl(firstIntegration) : baseUrl;
|
|
14543
|
-
const failureReason = `${NEEDS_SETUP_PREFIX} params=[${missingParams.join(",")}] integrations=[${missingIntegrations.join(",")}]`;
|
|
14544
|
-
return {
|
|
14545
|
-
id: input.id,
|
|
14546
|
-
name: input.name,
|
|
14547
|
-
kind: input.kind,
|
|
14548
|
-
state: "needs_setup",
|
|
14549
|
-
missing,
|
|
14550
|
-
fixes,
|
|
14551
|
-
fixUrl,
|
|
14552
|
-
failureReason
|
|
14553
|
-
};
|
|
14554
|
-
}
|
|
14555
|
-
var EXACT_TOKEN = /^\{\{\s*([^}]+?)\s*\}\}$/;
|
|
14556
|
-
var TOKEN = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
14557
|
-
function renderAutomationTokens(value, params) {
|
|
14558
|
-
function render(current) {
|
|
14559
|
-
if (typeof current === "string") {
|
|
14560
|
-
const exact = EXACT_TOKEN.exec(current);
|
|
14561
|
-
if (exact?.[1] && Object.hasOwn(params, exact[1]))
|
|
14562
|
-
return params[exact[1]];
|
|
14563
|
-
return current.replace(TOKEN, (token, key) => {
|
|
14564
|
-
if (!Object.hasOwn(params, key))
|
|
14565
|
-
return token;
|
|
14566
|
-
const replacement = params[key];
|
|
14567
|
-
return typeof replacement === "object" ? JSON.stringify(replacement) : String(replacement);
|
|
14568
|
-
});
|
|
14569
|
-
}
|
|
14570
|
-
if (Array.isArray(current))
|
|
14571
|
-
return current.map(render);
|
|
14572
|
-
if (current != null && typeof current === "object") {
|
|
14573
|
-
return Object.fromEntries(Object.entries(current).map(([key, nested]) => [key, render(nested)]));
|
|
14574
|
-
}
|
|
14575
|
-
return current;
|
|
14576
|
-
}
|
|
14577
|
-
return render(value);
|
|
14578
|
-
}
|
|
14579
|
-
function present(value) {
|
|
14580
|
-
return !!value?.trim();
|
|
14581
|
-
}
|
|
14582
|
-
async function getAutomationSetupStates() {
|
|
14583
|
-
const [linearTokens, jiraTokens, jiraApp] = await Promise.all([
|
|
14584
|
-
getOAuthTokens("linear"),
|
|
14585
|
-
getOAuthTokens("jira"),
|
|
14586
|
-
getOAuthApp("jira")
|
|
14587
|
-
]);
|
|
14588
|
-
let jiraCloudId = false;
|
|
14589
|
-
try {
|
|
14590
|
-
const metadata = jiraApp?.metadata ? JSON.parse(jiraApp.metadata) : null;
|
|
14591
|
-
jiraCloudId = !!(metadata && typeof metadata === "object" && metadata.cloudId);
|
|
14592
|
-
} catch {
|
|
14593
|
-
jiraCloudId = false;
|
|
14594
|
-
}
|
|
14595
|
-
const agentFsKey = process.env.API_AGENT_FS_API_KEY ?? process.env.AGENT_FS_API_KEY;
|
|
14596
|
-
const agentFsOrg = process.env.AGENT_FS_DEFAULT_ORG_ID ?? process.env.AGENT_FS_SHARED_ORG_ID;
|
|
14597
|
-
const gscConfigured = present(process.env.GSC_SERVICE_ACCOUNT_BASE64) || present(process.env.GSC_SERVICE_ACCOUNT_JSON);
|
|
14598
|
-
return {
|
|
14599
|
-
slack: getSlackConfiguration().mode === "socket" && isSlackConfigured() ? "configured" : "unverified",
|
|
14600
|
-
github: present(process.env.GITHUB_WEBHOOK_SECRET) && present(process.env.GITHUB_APP_ID) && present(process.env.GITHUB_APP_PRIVATE_KEY) ? "verified" : "unverified",
|
|
14601
|
-
linear: linearTokens ? "verified" : "unverified",
|
|
14602
|
-
jira: jiraTokens && jiraCloudId ? "verified" : "unverified",
|
|
14603
|
-
gsc: gscConfigured ? "verified" : "unverified",
|
|
14604
|
-
agentmail: !isEnvFlagEnabled("AGENTMAIL_DISABLE", false) && present(process.env.AGENTMAIL_API_KEY) ? "verified" : "unverified",
|
|
14605
|
-
agentfs: present(process.env.AGENT_FS_API_URL) && present(agentFsKey) && present(agentFsOrg) && present(process.env.AGENT_FS_DEFAULT_DRIVE_ID) ? "verified" : "unverified"
|
|
14606
|
-
};
|
|
14607
|
-
}
|
|
14608
|
-
function schedulePreflightInput(schedule) {
|
|
14609
|
-
return { ...schedule, kind: "schedule" };
|
|
14610
|
-
}
|
|
14611
|
-
function workflowPreflightInput(workflow) {
|
|
14612
|
-
return { ...workflow, kind: "workflow" };
|
|
14613
|
-
}
|
|
14614
|
-
async function listEnabledAutomationPreflightInputs() {
|
|
14615
|
-
const rows = await getDbClient().query(`SELECT id, name, 'schedule' AS kind, params, requiredParams, requires
|
|
14616
|
-
FROM scheduled_tasks
|
|
14617
|
-
WHERE enabled = 1
|
|
14618
|
-
UNION ALL
|
|
14619
|
-
SELECT id, name, 'workflow' AS kind, params, requiredParams, requires
|
|
14620
|
-
FROM workflows
|
|
14621
|
-
WHERE enabled = 1`);
|
|
14622
|
-
return rows.map((row) => ({
|
|
14623
|
-
id: row.id,
|
|
14624
|
-
name: row.name,
|
|
14625
|
-
kind: row.kind,
|
|
14626
|
-
params: JSON.parse(row.params),
|
|
14627
|
-
requiredParams: JSON.parse(row.requiredParams),
|
|
14628
|
-
requires: JSON.parse(row.requires)
|
|
14629
|
-
}));
|
|
14630
|
-
}
|
|
14631
|
-
function utcDayBounds(now) {
|
|
14632
|
-
const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
14633
|
-
return { start: start.toISOString(), end: new Date(start.getTime() + 86400000).toISOString() };
|
|
14634
|
-
}
|
|
14635
|
-
async function recordSchedulePreflightFailure(scheduleId, failureReason, now = new Date) {
|
|
14636
|
-
const message = scrubSecrets(failureReason);
|
|
14637
|
-
return await getDbClient().transaction(async (tx) => {
|
|
14638
|
-
const row = await tx.get("SELECT lastErrorAt, lastErrorMessage FROM scheduled_tasks WHERE id = ?", [scheduleId]);
|
|
14639
|
-
if (!row)
|
|
14640
|
-
return false;
|
|
14641
|
-
if (row.lastErrorMessage === message && row.lastErrorAt?.slice(0, 10) === now.toISOString().slice(0, 10)) {
|
|
14642
|
-
return false;
|
|
14643
|
-
}
|
|
14644
|
-
await tx.run(`UPDATE scheduled_tasks
|
|
14645
|
-
SET lastErrorAt = ?, lastErrorMessage = ?, lastUpdatedAt = ?
|
|
14646
|
-
WHERE id = ?`, [now.toISOString(), message, now.toISOString(), scheduleId]);
|
|
14647
|
-
return true;
|
|
14648
|
-
});
|
|
14649
|
-
}
|
|
14650
|
-
async function recordWorkflowPreflightFailure(input) {
|
|
14651
|
-
const now = input.now ?? new Date;
|
|
14652
|
-
const bounds = utcDayBounds(now);
|
|
14653
|
-
return await getDbClient().transaction(async (tx) => {
|
|
14654
|
-
const existing = await tx.get(`SELECT id FROM workflow_runs
|
|
14655
|
-
WHERE workflowId = ? AND status = 'failed'
|
|
14656
|
-
AND error LIKE 'needs_setup:%'
|
|
14657
|
-
AND startedAt >= ? AND startedAt < ?
|
|
14658
|
-
ORDER BY startedAt ASC LIMIT 1`, [input.workflowId, bounds.start, bounds.end]);
|
|
14659
|
-
if (existing)
|
|
14660
|
-
return { runId: existing.id, recorded: false };
|
|
14661
|
-
const runId = crypto.randomUUID();
|
|
14662
|
-
const timestamp = now.toISOString();
|
|
14663
|
-
await tx.run(`INSERT INTO workflow_runs
|
|
14664
|
-
(id, workflowId, status, triggerType, triggerData, error, created_by,
|
|
14665
|
-
startedAt, lastUpdatedAt, finishedAt)
|
|
14666
|
-
VALUES (?, ?, 'failed', ?, ?, ?, ?, ?, ?, ?)`, [
|
|
14667
|
-
runId,
|
|
14668
|
-
input.workflowId,
|
|
14669
|
-
input.triggerType,
|
|
14670
|
-
input.triggerData === undefined ? null : scrubJsonValue(input.triggerData),
|
|
14671
|
-
scrubSecrets(input.failureReason),
|
|
14672
|
-
input.createdBy ?? null,
|
|
14673
|
-
timestamp,
|
|
14674
|
-
timestamp,
|
|
14675
|
-
timestamp
|
|
14676
|
-
]);
|
|
14677
|
-
return { runId, recorded: true };
|
|
14678
|
-
});
|
|
14679
|
-
}
|
|
14680
|
-
|
|
14681
|
-
// src/workflows/engine.ts
|
|
14682
|
-
init_db2();
|
|
14683
|
-
init_workflow_replay();
|
|
14684
|
-
init_telemetry();
|
|
14685
|
-
|
|
14686
|
-
// src/workflows/checkpoint.ts
|
|
14687
|
-
init_db2();
|
|
14688
|
-
async function checkpointStep(runId, stepId, nodeId, result, ctx) {
|
|
14689
|
-
await getDbClient().transaction(async () => {
|
|
14690
|
-
await updateWorkflowRunStep(stepId, {
|
|
14691
|
-
status: "completed",
|
|
14692
|
-
output: result.output,
|
|
14693
|
-
nextPort: result.nextPort || undefined,
|
|
14694
|
-
finishedAt: new Date().toISOString()
|
|
14695
|
-
});
|
|
14696
|
-
ctx[nodeId] = result.output;
|
|
14697
|
-
await updateWorkflowRun(runId, {
|
|
14698
|
-
context: ctx
|
|
14699
|
-
});
|
|
14700
|
-
});
|
|
14701
|
-
}
|
|
14702
|
-
async function checkpointStepFailure(runId, stepId, error, retryCount, retryPolicy, options) {
|
|
14703
|
-
const now = new Date().toISOString();
|
|
14704
|
-
if (retryPolicy && retryCount < retryPolicy.maxRetries) {
|
|
14705
|
-
const delay = calculateBackoff(retryPolicy, retryCount);
|
|
14706
|
-
const nextRetryAt = new Date(Date.now() + delay).toISOString();
|
|
14707
|
-
await updateWorkflowRunStep(stepId, {
|
|
14708
|
-
status: "failed",
|
|
14709
|
-
error,
|
|
14710
|
-
retryCount: retryCount + 1,
|
|
14711
|
-
maxRetries: retryPolicy.maxRetries,
|
|
14712
|
-
nextRetryAt
|
|
14713
|
-
});
|
|
14714
|
-
return { shouldRetry: true };
|
|
14715
|
-
}
|
|
14716
|
-
await updateWorkflowRunStep(stepId, {
|
|
14717
|
-
status: "failed",
|
|
14718
|
-
error,
|
|
14719
|
-
finishedAt: now,
|
|
14720
|
-
nextRetryAt: null
|
|
14721
|
-
});
|
|
14722
|
-
const markRunFailed = options?.markRunFailed ?? true;
|
|
14723
|
-
if (markRunFailed) {
|
|
14724
|
-
await getDbClient().transaction(async () => {
|
|
14725
|
-
const run = await getWorkflowRun(runId);
|
|
14726
|
-
if (run?.status !== "running" && run?.status !== "waiting")
|
|
14727
|
-
return;
|
|
14728
|
-
await updateWorkflowRun(runId, {
|
|
14729
|
-
status: "failed",
|
|
14730
|
-
error: `Step failed: ${error}`,
|
|
14731
|
-
finishedAt: now
|
|
14732
|
-
});
|
|
14733
|
-
});
|
|
14734
|
-
}
|
|
14735
|
-
return { shouldRetry: false };
|
|
14736
|
-
}
|
|
14737
|
-
async function checkpointStepWaiting(runId, stepId, ctx) {
|
|
14738
|
-
return getDbClient().transaction(async () => {
|
|
14739
|
-
const step = await getDbClient().get(`UPDATE workflow_run_steps
|
|
14740
|
-
SET status = 'waiting'
|
|
14741
|
-
WHERE id = ? AND runId = ? AND status = 'running'
|
|
14742
|
-
AND EXISTS (
|
|
14743
|
-
SELECT 1 FROM workflow_runs
|
|
14744
|
-
WHERE id = ? AND status IN ('running', 'waiting')
|
|
14745
|
-
)
|
|
14746
|
-
RETURNING id`, [stepId, runId, runId]);
|
|
14747
|
-
if (!step)
|
|
14748
|
-
return false;
|
|
14749
|
-
await updateWorkflowRun(runId, { status: "waiting", context: ctx });
|
|
14750
|
-
return true;
|
|
14751
|
-
});
|
|
14752
|
-
}
|
|
14753
|
-
function calculateBackoff(policy, attempt) {
|
|
14754
|
-
let delay;
|
|
14755
|
-
switch (policy.strategy) {
|
|
14756
|
-
case "exponential": {
|
|
14757
|
-
const base = policy.baseDelayMs * 2 ** attempt;
|
|
14758
|
-
delay = Math.random() * Math.min(base, policy.maxDelayMs);
|
|
14759
|
-
break;
|
|
14760
|
-
}
|
|
14761
|
-
case "linear":
|
|
14762
|
-
delay = policy.baseDelayMs * (attempt + 1);
|
|
14763
|
-
break;
|
|
14764
|
-
case "static":
|
|
14765
|
-
delay = policy.baseDelayMs;
|
|
14766
|
-
break;
|
|
14767
|
-
default:
|
|
14768
|
-
delay = policy.baseDelayMs;
|
|
14769
|
-
}
|
|
14770
|
-
return Math.min(delay, policy.maxDelayMs);
|
|
14771
|
-
}
|
|
14772
|
-
|
|
14773
|
-
// src/workflows/completed-step-routing.ts
|
|
14774
|
-
init_db2();
|
|
14775
|
-
|
|
14776
|
-
// src/workflows/foreach-join.ts
|
|
14777
|
-
init_db2();
|
|
14778
|
-
var FOREACH_TERMINAL_STEP_STATUSES = new Set([
|
|
14779
|
-
"completed",
|
|
14780
|
-
"failed",
|
|
14781
|
-
"cancelled",
|
|
14782
|
-
"skipped"
|
|
14783
|
-
]);
|
|
14784
|
-
function parseSyntheticNodeId(nodeId) {
|
|
14785
|
-
const separatorIndex = nodeId.indexOf("#");
|
|
14786
|
-
if (separatorIndex <= 0 || separatorIndex === nodeId.length - 1)
|
|
14787
|
-
return null;
|
|
14788
|
-
return {
|
|
14789
|
-
parentNodeId: nodeId.slice(0, separatorIndex),
|
|
14790
|
-
itemKey: nodeId.slice(separatorIndex + 1)
|
|
14791
|
-
};
|
|
14792
|
-
}
|
|
14793
|
-
function resolveForeachParent(def, nodeId) {
|
|
14794
|
-
const parsed = parseSyntheticNodeId(nodeId);
|
|
14795
|
-
if (!parsed)
|
|
14796
|
-
return null;
|
|
14797
|
-
const parent = def.nodes.find((node) => node.id === parsed.parentNodeId);
|
|
14798
|
-
return parent?.type === "foreach" ? parent : null;
|
|
14799
|
-
}
|
|
14800
|
-
async function joinForeach(def, runId, childStep, ctx) {
|
|
14801
|
-
const parent = resolveForeachParent(def, childStep.nodeId);
|
|
14802
|
-
if (!parent) {
|
|
14803
|
-
throw new Error(`Step "${childStep.nodeId}" is not a foreach child`);
|
|
14804
|
-
}
|
|
14805
|
-
const children = (await getWorkflowRunStepsByRunId(runId)).filter((step) => resolveForeachParent(def, step.nodeId)?.id === parent.id).sort((a, b) => childIndex(a) - childIndex(b));
|
|
14806
|
-
if (children.some((step) => !FOREACH_TERMINAL_STEP_STATUSES.has(step.status))) {
|
|
14807
|
-
return { joined: false, parentNodeId: parent.id, successors: [] };
|
|
14808
|
-
}
|
|
14809
|
-
const parentStep = await getLatestStepForNode(runId, parent.id);
|
|
14810
|
-
if (!parentStep) {
|
|
14811
|
-
throw new Error(`Waiting foreach parent step "${parent.id}" was not found`);
|
|
14812
|
-
}
|
|
14813
|
-
if (parentStep.status === "completed") {
|
|
14814
|
-
return { joined: false, parentNodeId: parent.id, successors: [] };
|
|
14815
|
-
}
|
|
14816
|
-
const aggregate = buildForeachAggregate(children);
|
|
14817
|
-
await checkpointStep(runId, parentStep.id, parent.id, { output: aggregate }, ctx);
|
|
14818
|
-
return {
|
|
14819
|
-
joined: true,
|
|
14820
|
-
parentNodeId: parent.id,
|
|
14821
|
-
successors: getSuccessors(def, parent.id)
|
|
14822
|
-
};
|
|
14823
|
-
}
|
|
14824
|
-
function buildForeachAggregate(children) {
|
|
14825
|
-
const orderedChildren = [...children].sort((a, b) => childIndex(a) - childIndex(b));
|
|
14826
|
-
const results = orderedChildren.map((step) => {
|
|
14827
|
-
const parsed = parseSyntheticNodeId(step.nodeId);
|
|
14828
|
-
const status = resultStatus(step);
|
|
14829
|
-
return {
|
|
14830
|
-
itemKey: parsed.itemKey,
|
|
14831
|
-
status,
|
|
14832
|
-
output: step.output
|
|
14833
|
-
};
|
|
14834
|
-
});
|
|
14835
|
-
const failedCount = results.filter((result) => result.status !== "completed").length;
|
|
14836
|
-
const aggregate = {
|
|
14837
|
-
results,
|
|
14838
|
-
okCount: results.length - failedCount,
|
|
14839
|
-
failedCount
|
|
14840
|
-
};
|
|
14841
|
-
return aggregate;
|
|
14842
|
-
}
|
|
14843
|
-
function childIndex(step) {
|
|
14844
|
-
if (typeof step.input !== "object" || step.input === null)
|
|
14845
|
-
return Number.MAX_SAFE_INTEGER;
|
|
14846
|
-
const index = step.input.index;
|
|
14847
|
-
return typeof index === "number" ? index : Number.MAX_SAFE_INTEGER;
|
|
14848
|
-
}
|
|
14849
|
-
function resultStatus(step) {
|
|
14850
|
-
if (step.status !== "completed")
|
|
14851
|
-
return step.status;
|
|
14852
|
-
return step.error ? "failed" : "completed";
|
|
14853
|
-
}
|
|
14854
|
-
|
|
14855
|
-
// src/workflows/completed-step-routing.ts
|
|
14856
|
-
async function loadCompletedStepRouting(def, runId, completedNodeIds) {
|
|
14857
|
-
const activeEdges = new Set;
|
|
14858
|
-
const stepsByNodeId = new Map;
|
|
14859
|
-
for (const nodeId of completedNodeIds) {
|
|
14860
|
-
if (resolveForeachParent(def, nodeId))
|
|
14861
|
-
continue;
|
|
14862
|
-
const step = await getLatestStepForNode(runId, nodeId);
|
|
14863
|
-
if (step)
|
|
14864
|
-
stepsByNodeId.set(nodeId, step);
|
|
14865
|
-
const successors = step?.nextPort ? getSuccessors(def, nodeId, step.nextPort) : getSuccessors(def, nodeId);
|
|
14866
|
-
for (const successor of successors) {
|
|
14867
|
-
activeEdges.add(`${nodeId}→${successor.id}`);
|
|
14868
|
-
}
|
|
14869
|
-
}
|
|
14870
|
-
return { activeEdges, stepsByNodeId };
|
|
14871
|
-
}
|
|
14872
|
-
|
|
14873
|
-
// src/workflows/cooldown.ts
|
|
14874
|
-
init_db2();
|
|
14875
|
-
function cooldownToMs(cooldown) {
|
|
14876
|
-
let ms = 0;
|
|
14877
|
-
if (cooldown.hours)
|
|
14878
|
-
ms += cooldown.hours * 60 * 60 * 1000;
|
|
14879
|
-
if (cooldown.minutes)
|
|
14880
|
-
ms += cooldown.minutes * 60 * 1000;
|
|
14881
|
-
if (cooldown.seconds)
|
|
14882
|
-
ms += cooldown.seconds * 1000;
|
|
14883
|
-
return ms;
|
|
14884
|
-
}
|
|
14885
|
-
async function shouldSkipCooldown(workflowId, cooldown) {
|
|
14886
|
-
const cooldownMs = cooldownToMs(cooldown);
|
|
14887
|
-
const now = Date.now();
|
|
14888
|
-
const lastSuccess = await getLastSuccessfulRun(workflowId);
|
|
14889
|
-
if (lastSuccess?.finishedAt) {
|
|
14890
|
-
const lastFinished = new Date(lastSuccess.finishedAt).getTime();
|
|
14891
|
-
if (now - lastFinished < cooldownMs)
|
|
14892
|
-
return true;
|
|
14893
|
-
}
|
|
14894
|
-
const lastAttempt = await getLastRunStart(workflowId);
|
|
14895
|
-
if (lastAttempt?.startedAt) {
|
|
14896
|
-
const lastStarted = new Date(lastAttempt.startedAt).getTime();
|
|
14897
|
-
if (now - lastStarted < cooldownMs)
|
|
14898
|
-
return true;
|
|
14899
|
-
}
|
|
14900
|
-
return false;
|
|
14901
|
-
}
|
|
14902
|
-
|
|
14903
|
-
// src/workflows/input.ts
|
|
14904
|
-
init_db2();
|
|
14905
|
-
init_secret_scrubber();
|
|
14906
|
-
async function resolveInputs(input) {
|
|
14907
|
-
const resolved = {};
|
|
14908
|
-
for (const [key, value] of Object.entries(input)) {
|
|
14909
|
-
resolved[key] = await resolveInputValue(value);
|
|
14910
|
-
}
|
|
14911
|
-
return resolved;
|
|
14912
|
-
}
|
|
14913
|
-
async function resolveInputValue(value) {
|
|
14914
|
-
const envMatch = /^\$\{(.+)\}$/.exec(value);
|
|
14915
|
-
if (envMatch?.[1]) {
|
|
14916
|
-
const envName = envMatch[1];
|
|
14917
|
-
const envValue = process.env[envName];
|
|
14918
|
-
if (envValue === undefined) {
|
|
14919
|
-
throw new Error(`Environment variable "${envName}" is not set`);
|
|
14920
|
-
}
|
|
14921
|
-
return envValue;
|
|
14922
|
-
}
|
|
14923
|
-
if (value.startsWith("secret.")) {
|
|
14924
|
-
const secretName = value.slice("secret.".length);
|
|
14925
|
-
const configs = await getSwarmConfigs({ scope: "global", key: secretName });
|
|
14926
|
-
const secretConfig = configs.find((c) => c.isSecret);
|
|
14927
|
-
if (!secretConfig) {
|
|
14928
|
-
throw new Error(`Secret "${secretName}" not found in config store`);
|
|
14929
|
-
}
|
|
14930
|
-
return secretConfig.value;
|
|
14931
|
-
}
|
|
14932
|
-
return value;
|
|
14933
|
-
}
|
|
14934
|
-
var REDACTED_SECRET_VALUE = "***REDACTED***";
|
|
14935
|
-
function getSecretInputKeys(input) {
|
|
14936
|
-
const keys = new Set;
|
|
14937
|
-
if (!input)
|
|
14938
|
-
return keys;
|
|
14939
|
-
for (const [key, value] of Object.entries(input)) {
|
|
14940
|
-
if (typeof value !== "string")
|
|
14941
|
-
continue;
|
|
14942
|
-
if (value.startsWith("secret.")) {
|
|
14943
|
-
keys.add(key);
|
|
14944
|
-
continue;
|
|
14945
|
-
}
|
|
14946
|
-
const envMatch = /^\$\{(.+)\}$/.exec(value);
|
|
14947
|
-
if (envMatch?.[1] && isSensitiveKey(envMatch[1])) {
|
|
14948
|
-
keys.add(key);
|
|
14949
|
-
}
|
|
14950
|
-
}
|
|
14951
|
-
return keys;
|
|
14952
|
-
}
|
|
14953
|
-
function redactSecretsForStorage(ctx, secretKeys) {
|
|
14954
|
-
if (secretKeys.size === 0)
|
|
14955
|
-
return ctx;
|
|
14956
|
-
const inputBlock = ctx.input;
|
|
14957
|
-
if (!inputBlock || typeof inputBlock !== "object")
|
|
14958
|
-
return ctx;
|
|
14959
|
-
const redactedInput = { ...inputBlock };
|
|
14960
|
-
let touched = false;
|
|
14961
|
-
for (const key of secretKeys) {
|
|
14962
|
-
if (key in redactedInput) {
|
|
14963
|
-
redactedInput[key] = REDACTED_SECRET_VALUE;
|
|
14964
|
-
touched = true;
|
|
14965
|
-
}
|
|
14966
|
-
}
|
|
14967
|
-
if (!touched)
|
|
14968
|
-
return ctx;
|
|
14969
|
-
return { ...ctx, input: redactedInput };
|
|
14970
|
-
}
|
|
14971
|
-
|
|
14972
|
-
// src/workflows/json-schema-validator.ts
|
|
14973
|
-
function validateJsonSchema(schema, data) {
|
|
14974
|
-
const errors = [];
|
|
14975
|
-
validate(schema, data, "", errors);
|
|
14976
|
-
return errors;
|
|
14977
|
-
}
|
|
14978
|
-
function validate(schema, data, path, errors) {
|
|
14979
|
-
const prefix = path || "root";
|
|
14980
|
-
if (schema.type !== undefined) {
|
|
14981
|
-
const expected = schema.type;
|
|
14982
|
-
if (!matchesType(expected, data)) {
|
|
14983
|
-
errors.push(`${prefix}: expected type "${expected}", got ${typeOf(data)}`);
|
|
14984
|
-
return;
|
|
14985
|
-
}
|
|
14986
|
-
}
|
|
14987
|
-
if (Array.isArray(schema.required) && typeof data === "object" && data !== null) {
|
|
14988
|
-
const obj = data;
|
|
14989
|
-
for (const key of schema.required) {
|
|
14990
|
-
if (!(key in obj)) {
|
|
14991
|
-
errors.push(`${prefix}: missing required property "${key}"`);
|
|
14992
|
-
}
|
|
14993
|
-
}
|
|
14994
|
-
}
|
|
14995
|
-
if (Array.isArray(schema.enum)) {
|
|
14996
|
-
const allowed = schema.enum;
|
|
14997
|
-
if (!allowed.some((v) => JSON.stringify(v) === JSON.stringify(data))) {
|
|
14998
|
-
errors.push(`${prefix}: value ${JSON.stringify(data)} not in enum [${allowed.map((v) => JSON.stringify(v)).join(", ")}]`);
|
|
14999
|
-
return;
|
|
15000
|
-
}
|
|
15001
|
-
}
|
|
15002
|
-
if ("const" in schema) {
|
|
15003
|
-
if (JSON.stringify(data) !== JSON.stringify(schema.const)) {
|
|
15004
|
-
errors.push(`${prefix}: value ${JSON.stringify(data)} does not match const ${JSON.stringify(schema.const)}`);
|
|
15005
|
-
return;
|
|
15006
|
-
}
|
|
15007
|
-
}
|
|
15008
|
-
if (schema.properties !== undefined && typeof schema.properties === "object" && schema.properties !== null && typeof data === "object" && data !== null) {
|
|
15009
|
-
const props = schema.properties;
|
|
15010
|
-
const obj = data;
|
|
15011
|
-
for (const [key, subSchema] of Object.entries(props)) {
|
|
15012
|
-
if (key in obj) {
|
|
15013
|
-
validate(subSchema, obj[key], path ? `${path}.${key}` : key, errors);
|
|
15014
|
-
}
|
|
15015
|
-
}
|
|
15016
|
-
}
|
|
15017
|
-
if (schema.items !== undefined && typeof schema.items === "object" && schema.items !== null && Array.isArray(data)) {
|
|
15018
|
-
const itemSchema = schema.items;
|
|
15019
|
-
for (let i = 0;i < data.length; i++) {
|
|
15020
|
-
validate(itemSchema, data[i], `${prefix}[${i}]`, errors);
|
|
15021
|
-
}
|
|
15022
|
-
}
|
|
15023
|
-
}
|
|
15024
|
-
function matchesType(expected, data) {
|
|
15025
|
-
switch (expected) {
|
|
15026
|
-
case "string":
|
|
15027
|
-
return typeof data === "string";
|
|
15028
|
-
case "number":
|
|
15029
|
-
return typeof data === "number";
|
|
15030
|
-
case "boolean":
|
|
15031
|
-
return typeof data === "boolean";
|
|
15032
|
-
case "array":
|
|
15033
|
-
return Array.isArray(data);
|
|
15034
|
-
case "object":
|
|
15035
|
-
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
15036
|
-
default:
|
|
15037
|
-
return true;
|
|
15038
|
-
}
|
|
15039
|
-
}
|
|
15040
|
-
function typeOf(data) {
|
|
15041
|
-
if (data === null)
|
|
15042
|
-
return "null";
|
|
15043
|
-
if (data === undefined)
|
|
15044
|
-
return "undefined";
|
|
15045
|
-
if (Array.isArray(data))
|
|
15046
|
-
return "array";
|
|
15047
|
-
return typeof data;
|
|
15048
|
-
}
|
|
15049
|
-
function isPlainObject(value) {
|
|
15050
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15051
|
-
}
|
|
15052
|
-
function findJsonSchemaShapeErrors(schema, path = "outputSchema") {
|
|
15053
|
-
const errors = [];
|
|
15054
|
-
if (!isPlainObject(schema)) {
|
|
15055
|
-
errors.push(`${path}: must be a JSON Schema object, got ${typeOf(schema)}`);
|
|
15056
|
-
return errors;
|
|
15057
|
-
}
|
|
15058
|
-
if (schema.type !== undefined && typeof schema.type !== "string") {
|
|
15059
|
-
errors.push(`${path}.type: must be a string, got ${typeOf(schema.type)}`);
|
|
15060
|
-
}
|
|
15061
|
-
if (schema.required !== undefined && (!Array.isArray(schema.required) || !schema.required.every((v) => typeof v === "string"))) {
|
|
15062
|
-
errors.push(`${path}.required: must be an array of strings`);
|
|
15063
|
-
}
|
|
15064
|
-
if (schema.enum !== undefined && !Array.isArray(schema.enum)) {
|
|
15065
|
-
errors.push(`${path}.enum: must be an array`);
|
|
15066
|
-
}
|
|
15067
|
-
if (schema.properties !== undefined) {
|
|
15068
|
-
if (!isPlainObject(schema.properties)) {
|
|
15069
|
-
errors.push(`${path}.properties: must be an object mapping property names to schemas`);
|
|
15070
|
-
} else {
|
|
15071
|
-
for (const [key, value] of Object.entries(schema.properties)) {
|
|
15072
|
-
errors.push(...findJsonSchemaShapeErrors(value, `${path}.properties.${key}`));
|
|
15073
|
-
}
|
|
15074
|
-
}
|
|
15075
|
-
}
|
|
15076
|
-
if (schema.items !== undefined) {
|
|
15077
|
-
errors.push(...findJsonSchemaShapeErrors(schema.items, `${path}.items`));
|
|
15078
|
-
}
|
|
15079
|
-
return errors;
|
|
15080
|
-
}
|
|
15081
|
-
|
|
15082
|
-
// src/workflows/limits.ts
|
|
15083
|
-
var DEFAULT_MAX_STEPS_PER_RUN = 500;
|
|
15084
|
-
function getMaxWorkflowStepsPerRun() {
|
|
15085
|
-
return Number(process.env.WORKFLOW_MAX_STEPS_PER_RUN) || DEFAULT_MAX_STEPS_PER_RUN;
|
|
15086
|
-
}
|
|
15087
|
-
|
|
15088
|
-
// src/workflows/readiness.ts
|
|
15089
|
-
async function findWorkflowReadinessProblems(definition, registry) {
|
|
15090
|
-
const nodesByType = new Map;
|
|
15091
|
-
for (const node of definition.nodes) {
|
|
15092
|
-
if (!registry.has(node.type))
|
|
15093
|
-
continue;
|
|
15094
|
-
const group = nodesByType.get(node.type) ?? [];
|
|
15095
|
-
group.push({ id: node.id, config: node.config });
|
|
15096
|
-
nodesByType.set(node.type, group);
|
|
15097
|
-
}
|
|
15098
|
-
const problems = [];
|
|
15099
|
-
for (const [type, nodes] of nodesByType) {
|
|
15100
|
-
try {
|
|
15101
|
-
for (const problem of await registry.get(type).checkReadiness(nodes)) {
|
|
15102
|
-
problems.push({ type, ...problem });
|
|
15103
|
-
}
|
|
15104
|
-
} catch {
|
|
15105
|
-
problems.push({
|
|
15106
|
-
type,
|
|
15107
|
-
nodeIds: nodes.map((node) => node.id),
|
|
15108
|
-
message: `Could not check whether "${type}" nodes are ready to run`
|
|
15109
|
-
});
|
|
15110
|
-
}
|
|
15111
|
-
}
|
|
15112
|
-
return problems;
|
|
15113
|
-
}
|
|
15114
|
-
function describeNodes(problem) {
|
|
15115
|
-
const noun = problem.nodeIds.length === 1 ? "node" : "nodes";
|
|
15116
|
-
return `${problem.type} ${noun} ${problem.nodeIds.map((id) => `"${id}"`).join(", ")}`;
|
|
15117
|
-
}
|
|
15118
|
-
function formatReadinessWarning(problem) {
|
|
15119
|
-
return `${problem.message} Affects ${describeNodes(problem)}. Runs of this workflow fail before any node executes until this is fixed.`;
|
|
15120
|
-
}
|
|
15121
|
-
function formatReadinessProblems(problems) {
|
|
15122
|
-
return problems.map((problem) => `${problem.message} Needed by ${describeNodes(problem)}.`).join(" ");
|
|
15123
|
-
}
|
|
15124
|
-
function formatReadinessRunError(problems) {
|
|
15125
|
-
return `Run not started, no node executed: ${formatReadinessProblems(problems)}`;
|
|
15126
|
-
}
|
|
15127
|
-
async function workflowSaveWarnings(definition, registry) {
|
|
15128
|
-
return (await findWorkflowReadinessProblems(definition, registry)).map(formatReadinessWarning);
|
|
15129
|
-
}
|
|
15130
|
-
function withSaveWarnings(text, warnings) {
|
|
15131
|
-
return warnings.length === 0 ? text : `${text} ${warnings.map((warning) => `Warning: ${warning}`).join(" ")}`;
|
|
15132
|
-
}
|
|
15133
|
-
|
|
15134
|
-
// src/workflows/template.ts
|
|
15135
|
-
init_template();
|
|
15136
|
-
|
|
15137
|
-
// src/workflows/validation.ts
|
|
15138
|
-
function extractPassResult(executorType, output) {
|
|
15139
|
-
if (!output || typeof output !== "object")
|
|
15140
|
-
return false;
|
|
15141
|
-
const o = output;
|
|
15142
|
-
switch (executorType) {
|
|
15143
|
-
case "validate":
|
|
15144
|
-
return o.pass === true;
|
|
15145
|
-
case "script":
|
|
15146
|
-
return o.exitCode === 0;
|
|
15147
|
-
case "property-match":
|
|
15148
|
-
return o.passed === true;
|
|
15149
|
-
case "raw-llm":
|
|
15150
|
-
if (typeof o.result === "object" && o.result !== null) {
|
|
15151
|
-
return o.result.pass === true;
|
|
15152
|
-
}
|
|
15153
|
-
return false;
|
|
15154
|
-
default:
|
|
15155
|
-
return o.pass === true || o.passed === true || o.exitCode === 0;
|
|
15156
|
-
}
|
|
15157
|
-
}
|
|
15158
|
-
async function runStepValidation(registry, stepNode, stepOutput, context, meta) {
|
|
15159
|
-
if (!stepNode.validation) {
|
|
15160
|
-
return { outcome: "pass" };
|
|
15161
|
-
}
|
|
15162
|
-
const validation = stepNode.validation;
|
|
15163
|
-
const executorType = validation.executor || "validate";
|
|
15164
|
-
const executor = registry.get(executorType);
|
|
15165
|
-
const validationConfig = {
|
|
15166
|
-
targetNodeId: meta.nodeId,
|
|
15167
|
-
...validation.config
|
|
15168
|
-
};
|
|
15169
|
-
const validationContext = {
|
|
15170
|
-
...context,
|
|
15171
|
-
[meta.nodeId]: stepOutput
|
|
15172
|
-
};
|
|
15173
|
-
const result = await executor.run({
|
|
15174
|
-
config: validationConfig,
|
|
15175
|
-
context: validationContext,
|
|
15176
|
-
meta: {
|
|
15177
|
-
...meta,
|
|
15178
|
-
stepId: crypto.randomUUID()
|
|
15179
|
-
}
|
|
15180
|
-
});
|
|
15181
|
-
const passed = result.status === "success" && extractPassResult(executorType, result.output);
|
|
15182
|
-
if (passed) {
|
|
15183
|
-
return { outcome: "pass", passed: true };
|
|
15184
|
-
}
|
|
15185
|
-
if (validation.mustPass) {
|
|
15186
|
-
if (validation.retry) {
|
|
15187
|
-
return {
|
|
15188
|
-
outcome: "retry",
|
|
15189
|
-
passed: false,
|
|
15190
|
-
retryContext: {
|
|
15191
|
-
previousOutput: stepOutput,
|
|
15192
|
-
validationResult: result.output
|
|
15193
|
-
}
|
|
15194
|
-
};
|
|
15195
|
-
}
|
|
15196
|
-
return { outcome: "halt", passed: false };
|
|
15197
|
-
}
|
|
15198
|
-
return { outcome: "pass", passed: false };
|
|
15199
|
-
}
|
|
15200
|
-
|
|
15201
|
-
// src/workflows/engine.ts
|
|
15202
|
-
var DEFAULT_TIMEOUT_MS = 30000;
|
|
15203
|
-
function maxIterations() {
|
|
15204
|
-
return Number(process.env.WORKFLOW_MAX_ITERATIONS) || 100;
|
|
15205
|
-
}
|
|
15206
|
-
|
|
15207
|
-
class TriggerSchemaError extends Error {
|
|
15208
|
-
validationErrors;
|
|
15209
|
-
constructor(validationErrors) {
|
|
15210
|
-
super(`Trigger schema validation failed: ${validationErrors.join("; ")}`);
|
|
15211
|
-
this.validationErrors = validationErrors;
|
|
15212
|
-
this.name = "TriggerSchemaError";
|
|
15213
|
-
}
|
|
15214
|
-
}
|
|
15215
|
-
async function resolveRenderedWorkflowInputs(input) {
|
|
15216
|
-
const stringInputs = {};
|
|
15217
|
-
const resolved = {};
|
|
15218
|
-
for (const [key, value] of Object.entries(input)) {
|
|
15219
|
-
if (typeof value === "string")
|
|
15220
|
-
stringInputs[key] = value;
|
|
15221
|
-
else
|
|
15222
|
-
resolved[key] = value;
|
|
15223
|
-
}
|
|
15224
|
-
return { ...resolved, ...await resolveInputs(stringInputs) };
|
|
15225
|
-
}
|
|
15226
|
-
async function startWorkflowExecution(workflow, triggerData, registry, options = {}) {
|
|
15227
|
-
const preflight = preflightAutomation(workflowPreflightInput(workflow), await getAutomationSetupStates());
|
|
15228
|
-
if (preflight.state === "needs_setup") {
|
|
15229
|
-
const { runId: runId2, recorded } = await recordWorkflowPreflightFailure({
|
|
15230
|
-
workflowId: workflow.id,
|
|
15231
|
-
triggerType: options.triggerType ?? "manual",
|
|
15232
|
-
triggerData,
|
|
15233
|
-
failureReason: preflight.failureReason,
|
|
15234
|
-
createdBy: options.requestedByUserId
|
|
15235
|
-
});
|
|
15236
|
-
if (recorded)
|
|
15237
|
-
await notifyAutomationPreflightFailure(preflight);
|
|
15238
|
-
return runId2;
|
|
15239
|
-
}
|
|
15240
|
-
workflow = renderAutomationTokens(workflow, workflow.params ?? {});
|
|
15241
|
-
if (workflow.triggerSchema) {
|
|
15242
|
-
const validationErrors = validateJsonSchema(workflow.triggerSchema, triggerData);
|
|
15243
|
-
if (validationErrors.length > 0) {
|
|
15244
|
-
throw new TriggerSchemaError(validationErrors);
|
|
15245
|
-
}
|
|
15246
|
-
}
|
|
15247
|
-
if (workflow.cooldown && await shouldSkipCooldown(workflow.id, workflow.cooldown)) {
|
|
15248
|
-
const runId2 = crypto.randomUUID();
|
|
15249
|
-
await createWorkflowRun({
|
|
15250
|
-
id: runId2,
|
|
15251
|
-
workflowId: workflow.id,
|
|
15252
|
-
triggerType: options.triggerType ?? "manual",
|
|
15253
|
-
triggerData,
|
|
15254
|
-
createdBy: options.requestedByUserId
|
|
15255
|
-
});
|
|
15256
|
-
await updateWorkflowRun(runId2, {
|
|
15257
|
-
status: "skipped",
|
|
15258
|
-
error: "cooldown",
|
|
15259
|
-
finishedAt: new Date().toISOString()
|
|
15260
|
-
});
|
|
15261
|
-
return runId2;
|
|
15262
|
-
}
|
|
15263
|
-
const runId = crypto.randomUUID();
|
|
15264
|
-
await createWorkflowRun({
|
|
15265
|
-
id: runId,
|
|
15266
|
-
workflowId: workflow.id,
|
|
15267
|
-
triggerType: options.triggerType ?? "manual",
|
|
15268
|
-
triggerData,
|
|
15269
|
-
createdBy: options.requestedByUserId
|
|
15270
|
-
});
|
|
15271
|
-
telemetry.workflow("started", {
|
|
15272
|
-
workflowId: workflow.id,
|
|
15273
|
-
nodeCount: workflow.definition.nodes.length,
|
|
15274
|
-
triggerType: options.triggerType ?? "manual"
|
|
15275
|
-
});
|
|
15276
|
-
const notReady = await findWorkflowReadinessProblems(workflow.definition, registry);
|
|
15277
|
-
if (notReady.length > 0) {
|
|
15278
|
-
await updateWorkflowRun(runId, {
|
|
15279
|
-
status: "failed",
|
|
15280
|
-
error: formatReadinessRunError(notReady),
|
|
15281
|
-
finishedAt: new Date().toISOString()
|
|
15282
|
-
});
|
|
15283
|
-
return runId;
|
|
15284
|
-
}
|
|
15285
|
-
const ctx = { trigger: triggerData };
|
|
15286
|
-
if (options.requestedByUserId) {
|
|
15287
|
-
ctx.swarm = { requestedByUserId: options.requestedByUserId };
|
|
15288
|
-
}
|
|
15289
|
-
if (workflow.dir || workflow.vcsRepo) {
|
|
15290
|
-
ctx.workflow = { dir: workflow.dir, vcsRepo: workflow.vcsRepo };
|
|
15291
|
-
}
|
|
15292
|
-
if (workflow.input) {
|
|
15293
|
-
try {
|
|
15294
|
-
const resolved = await resolveRenderedWorkflowInputs(workflow.input);
|
|
15295
|
-
Object.assign(ctx, { input: resolved });
|
|
15296
|
-
} catch (err) {
|
|
15297
|
-
await updateWorkflowRun(runId, {
|
|
15298
|
-
status: "failed",
|
|
15299
|
-
error: `Input resolution failed: ${err}`,
|
|
15300
|
-
finishedAt: new Date().toISOString()
|
|
15301
|
-
});
|
|
15302
|
-
return runId;
|
|
15303
|
-
}
|
|
15304
|
-
}
|
|
15305
|
-
const entryNodes = findEntryNodes(workflow.definition);
|
|
15306
|
-
const secretKeys = getSecretInputKeys(workflow.input);
|
|
15307
|
-
await walkGraph(workflow.definition, runId, ctx, entryNodes, registry, workflow.id, secretKeys, options);
|
|
15308
|
-
return runId;
|
|
15309
|
-
}
|
|
15310
|
-
var activeWalks = new Map;
|
|
15311
|
-
var executingSteps = new Set;
|
|
15312
|
-
async function failRunOnUnreadableReplay(runId, err, stepId) {
|
|
15313
|
-
if (!(err instanceof WorkflowReplayStateError))
|
|
15314
|
-
return false;
|
|
15315
|
-
const finishedAt = new Date().toISOString();
|
|
15316
|
-
console.error(`[workflow] ${err.message}`);
|
|
15317
|
-
if (stepId) {
|
|
15318
|
-
await updateWorkflowRunStep(stepId, {
|
|
15319
|
-
status: "failed",
|
|
15320
|
-
error: err.message,
|
|
15321
|
-
finishedAt,
|
|
15322
|
-
nextRetryAt: null
|
|
15323
|
-
});
|
|
15324
|
-
}
|
|
15325
|
-
await updateWorkflowRun(runId, { status: "failed", error: err.message, finishedAt });
|
|
15326
|
-
return true;
|
|
15327
|
-
}
|
|
15328
|
-
function isWorkflowRunActive(runId) {
|
|
15329
|
-
return activeWalks.has(runId);
|
|
15330
|
-
}
|
|
15331
|
-
function holdWorkflowRun(runId) {
|
|
15332
|
-
activeWalks.set(runId, (activeWalks.get(runId) ?? 0) + 1);
|
|
15333
|
-
let released = false;
|
|
15334
|
-
return () => {
|
|
15335
|
-
if (released)
|
|
15336
|
-
return;
|
|
15337
|
-
released = true;
|
|
15338
|
-
const remaining = activeWalks.get(runId) - 1;
|
|
15339
|
-
if (remaining === 0)
|
|
15340
|
-
activeWalks.delete(runId);
|
|
15341
|
-
else
|
|
15342
|
-
activeWalks.set(runId, remaining);
|
|
15343
|
-
};
|
|
15344
|
-
}
|
|
15345
|
-
async function walkGraph(def, runId, ctx, startNodes, registry, workflowId, secretKeys = new Set, options = {}) {
|
|
15346
|
-
const release = holdWorkflowRun(runId);
|
|
15347
|
-
try {
|
|
15348
|
-
await walkGraphOwned(def, runId, ctx, startNodes, registry, workflowId, secretKeys, options);
|
|
15349
|
-
} catch (err) {
|
|
15350
|
-
if (!await failRunOnUnreadableReplay(runId, err))
|
|
15351
|
-
throw err;
|
|
15352
|
-
} finally {
|
|
15353
|
-
release();
|
|
15354
|
-
}
|
|
15355
|
-
}
|
|
15356
|
-
async function walkGraphOwned(def, runId, ctx, startNodes, registry, workflowId, secretKeys = new Set, options = {}) {
|
|
15357
|
-
if (!options.requestedByUserId) {
|
|
15358
|
-
options = {
|
|
15359
|
-
...options,
|
|
15360
|
-
requestedByUserId: (await getWorkflowRun(runId))?.createdBy
|
|
15361
|
-
};
|
|
15362
|
-
}
|
|
15363
|
-
let nodeExecutionCount = 0;
|
|
15364
|
-
if (!("run" in ctx)) {
|
|
15365
|
-
ctx.run = { id: runId };
|
|
15366
|
-
}
|
|
15367
|
-
const { completedNodeIds } = await rehydrateCompletedStepOutputs(def, runId, ctx, registry);
|
|
15368
|
-
const { activeEdges } = await loadCompletedStepRouting(def, runId, completedNodeIds);
|
|
15369
|
-
const allSteps = await getWorkflowRunStepsByRunId(runId);
|
|
15370
|
-
if (allSteps.length >= getMaxWorkflowStepsPerRun()) {
|
|
15371
|
-
await updateWorkflowRun(runId, {
|
|
15372
|
-
status: "failed",
|
|
15373
|
-
error: `Circuit breaker: run exceeded ${getMaxWorkflowStepsPerRun()} total steps (WORKFLOW_MAX_STEPS_PER_RUN)`,
|
|
15374
|
-
finishedAt: new Date().toISOString()
|
|
15375
|
-
});
|
|
15376
|
-
return;
|
|
15377
|
-
}
|
|
15378
|
-
for (const step of allSteps) {
|
|
15379
|
-
if (step.status !== "waiting")
|
|
15380
|
-
continue;
|
|
15381
|
-
const successors = getSuccessors(def, step.nodeId);
|
|
15382
|
-
for (const succ of successors) {
|
|
15383
|
-
activeEdges.add(`${step.nodeId}→${succ.id}`);
|
|
15384
|
-
}
|
|
15385
|
-
}
|
|
15386
|
-
const awaited = awaitedNodeIds(allSteps, activeEdges);
|
|
15387
|
-
let pendingNodes = startNodes.filter((n) => {
|
|
15388
|
-
const preds = getAllPredecessors(def, n.id);
|
|
15389
|
-
if (preds.length === 0) {
|
|
15390
|
-
return !completedNodeIds.has(n.id);
|
|
15391
|
-
}
|
|
15392
|
-
const activePreds = preds.filter((predId) => activeEdges.has(`${predId}→${n.id}`) || awaited.has(predId));
|
|
15393
|
-
const predsToCheck = activePreds.length > 0 ? activePreds : preds;
|
|
15394
|
-
return predsToCheck.every((p) => completedNodeIds.has(p));
|
|
15395
|
-
});
|
|
15396
|
-
const executedInThisWalk = new Set;
|
|
15397
|
-
while (pendingNodes.length > 0) {
|
|
15398
|
-
nodeExecutionCount += pendingNodes.length;
|
|
15399
|
-
if (nodeExecutionCount > maxIterations()) {
|
|
15400
|
-
await updateWorkflowRun(runId, {
|
|
15401
|
-
status: "failed",
|
|
15402
|
-
error: `Max node executions (${maxIterations()}) exceeded — possible infinite loop`,
|
|
15403
|
-
finishedAt: new Date().toISOString()
|
|
15404
|
-
});
|
|
15405
|
-
return;
|
|
15406
|
-
}
|
|
15407
|
-
const results = await Promise.all(pendingNodes.map((node) => executeStep(def, runId, ctx, node, registry, workflowId, secretKeys, options).catch(async (err) => {
|
|
15408
|
-
await failRunOnUnreadableReplay(runId, err);
|
|
15409
|
-
return { outcome: "failed", successors: [] };
|
|
15410
|
-
})));
|
|
15411
|
-
const nextBatch = new Map;
|
|
15412
|
-
let hasWaiting = false;
|
|
15413
|
-
for (let i = 0;i < results.length; i++) {
|
|
15414
|
-
const result = results[i];
|
|
15415
|
-
if (result.outcome === "failed") {
|
|
15416
|
-
const currentRun = await getWorkflowRun(runId);
|
|
15417
|
-
if (currentRun?.status === "failed")
|
|
15418
|
-
return;
|
|
15419
|
-
continue;
|
|
15420
|
-
}
|
|
15421
|
-
if (result.outcome === "waiting") {
|
|
15422
|
-
hasWaiting = true;
|
|
15423
|
-
continue;
|
|
15424
|
-
}
|
|
15425
|
-
const sourceNodeId = pendingNodes[i].id;
|
|
15426
|
-
completedNodeIds.add(sourceNodeId);
|
|
15427
|
-
executedInThisWalk.add(sourceNodeId);
|
|
15428
|
-
for (const succ of result.successors) {
|
|
15429
|
-
activeEdges.add(`${sourceNodeId}→${succ.id}`);
|
|
15430
|
-
nextBatch.set(succ.id, succ);
|
|
15431
|
-
}
|
|
15432
|
-
}
|
|
15433
|
-
if (hasWaiting)
|
|
15434
|
-
return;
|
|
15435
|
-
const readyNext = [];
|
|
15436
|
-
const batchSteps = nextBatch.size > 0 ? await getWorkflowRunStepsByRunId(runId) : [];
|
|
15437
|
-
const batchAwaited = awaitedNodeIds(batchSteps, activeEdges);
|
|
15438
|
-
const batchLatest = latestStepByNode(batchSteps);
|
|
15439
|
-
for (const [nodeId, node] of nextBatch) {
|
|
15440
|
-
if (executedInThisWalk.has(nodeId))
|
|
15441
|
-
continue;
|
|
15442
|
-
const allPreds = getAllPredecessors(def, nodeId);
|
|
15443
|
-
for (const predId of allPreds) {
|
|
15444
|
-
if (completedNodeIds.has(predId))
|
|
15445
|
-
continue;
|
|
15446
|
-
const latest = batchLatest.get(predId);
|
|
15447
|
-
if (latest?.status !== "completed")
|
|
15448
|
-
continue;
|
|
15449
|
-
completedNodeIds.add(predId);
|
|
15450
|
-
if (latest.output !== undefined)
|
|
15451
|
-
ctx[predId] = latest.output;
|
|
15452
|
-
}
|
|
15453
|
-
const activePreds = allPreds.filter((predId) => activeEdges.has(`${predId}→${nodeId}`) || batchAwaited.has(predId));
|
|
15454
|
-
const allActivePredsCompleted = activePreds.every((p) => completedNodeIds.has(p));
|
|
15455
|
-
if (allActivePredsCompleted) {
|
|
15456
|
-
readyNext.push(node);
|
|
15457
|
-
}
|
|
15458
|
-
}
|
|
15459
|
-
pendingNodes = readyNext;
|
|
15460
|
-
}
|
|
15461
|
-
await getDbClient().transaction(async () => {
|
|
15462
|
-
const run = await getWorkflowRun(runId);
|
|
15463
|
-
if (!run || run.status !== "running")
|
|
15464
|
-
return;
|
|
15465
|
-
const finalSteps = await getWorkflowRunStepsByRunId(runId);
|
|
15466
|
-
if (hasRunningStep(finalSteps))
|
|
15467
|
-
return;
|
|
15468
|
-
const hasWaitingSteps = finalSteps.some((s) => s.status === "waiting");
|
|
15469
|
-
const hasPendingRetries = finalSteps.some((s) => s.status === "failed" && s.nextRetryAt != null);
|
|
15470
|
-
const failedSteps = finalSteps.filter((s) => s.status === "failed" && s.nextRetryAt == null);
|
|
15471
|
-
const entryNodeIds = new Set(findEntryNodes(def).map((n) => n.id));
|
|
15472
|
-
const hasCompletedSteps = finalSteps.some((s) => s.status === "completed" && !entryNodeIds.has(s.nodeId));
|
|
15473
|
-
if (hasWaitingSteps) {
|
|
15474
|
-
await updateWorkflowRun(runId, { status: "waiting" });
|
|
15475
|
-
} else if (!hasPendingRetries) {
|
|
15476
|
-
if (failedSteps.length > 0 && !hasCompletedSteps) {
|
|
15477
|
-
const failedNodeIds = failedSteps.map((s) => s.nodeId).join(", ");
|
|
15478
|
-
await updateWorkflowRun(runId, {
|
|
15479
|
-
status: "failed",
|
|
15480
|
-
error: `All branches failed. Failed nodes: ${failedNodeIds}`,
|
|
15481
|
-
context: ctx,
|
|
15482
|
-
finishedAt: new Date().toISOString()
|
|
15483
|
-
});
|
|
15484
|
-
} else if (failedSteps.length > 0) {
|
|
15485
|
-
const failedNodeIds = failedSteps.map((s) => s.nodeId).join(", ");
|
|
15486
|
-
await updateWorkflowRun(runId, {
|
|
15487
|
-
status: "completed",
|
|
15488
|
-
error: `Partial failure: nodes [${failedNodeIds}] failed (mustPass validation), but other branches completed successfully`,
|
|
15489
|
-
context: ctx,
|
|
15490
|
-
finishedAt: new Date().toISOString()
|
|
15491
|
-
});
|
|
15492
|
-
} else {
|
|
15493
|
-
await updateWorkflowRun(runId, {
|
|
15494
|
-
status: "completed",
|
|
15495
|
-
context: ctx,
|
|
15496
|
-
finishedAt: new Date().toISOString()
|
|
15497
|
-
});
|
|
15498
|
-
}
|
|
15499
|
-
}
|
|
15500
|
-
});
|
|
15501
|
-
}
|
|
15502
|
-
function latestStepByNode(steps) {
|
|
15503
|
-
const latest = new Map;
|
|
15504
|
-
for (const step of steps)
|
|
15505
|
-
latest.set(step.nodeId, step);
|
|
15506
|
-
return latest;
|
|
15507
|
-
}
|
|
15508
|
-
function awaitedNodeIds(steps, activeEdges) {
|
|
15509
|
-
const latest = latestStepByNode(steps);
|
|
15510
|
-
const awaited = new Set;
|
|
15511
|
-
for (const [nodeId, step] of latest) {
|
|
15512
|
-
if (step.status === "running" || step.status === "waiting")
|
|
15513
|
-
awaited.add(nodeId);
|
|
15514
|
-
}
|
|
15515
|
-
for (const edge of activeEdges) {
|
|
15516
|
-
const target = edge.slice(edge.indexOf("→") + 1);
|
|
15517
|
-
if (!latest.has(target))
|
|
15518
|
-
awaited.add(target);
|
|
15519
|
-
}
|
|
15520
|
-
return awaited;
|
|
15521
|
-
}
|
|
15522
|
-
function hasRunningStep(steps) {
|
|
15523
|
-
for (const step of latestStepByNode(steps).values()) {
|
|
15524
|
-
if (step.status === "running")
|
|
15525
|
-
return true;
|
|
15526
|
-
}
|
|
15527
|
-
return false;
|
|
15528
|
-
}
|
|
15529
|
-
async function rehydrateCompletedStepOutputs(def, runId, ctx, registry) {
|
|
15530
|
-
const completedNodeIds = new Set(await getCompletedStepNodeIds(runId));
|
|
15531
|
-
const latestSteps = new Map;
|
|
15532
|
-
for (const nodeId of completedNodeIds) {
|
|
15533
|
-
if (resolveForeachParent(def, nodeId))
|
|
15534
|
-
continue;
|
|
15535
|
-
const step = await getLatestStepForNode(runId, nodeId);
|
|
15536
|
-
if (!step)
|
|
15537
|
-
continue;
|
|
15538
|
-
if (step.output !== undefined) {
|
|
15539
|
-
const node = def.nodes.find((candidate) => candidate.id === nodeId);
|
|
15540
|
-
if (node && registry.has(node.type)) {
|
|
15541
|
-
const executor = registry.get(node.type);
|
|
15542
|
-
const parseResult = executor.outputSchema.safeParse(step.output);
|
|
15543
|
-
if (!parseResult.success) {
|
|
15544
|
-
console.warn(`[workflow] Recovery: step ${nodeId} output failed validation: ${parseResult.error.message}`);
|
|
15545
|
-
continue;
|
|
15546
|
-
}
|
|
15547
|
-
}
|
|
15548
|
-
ctx[nodeId] = step.output;
|
|
15549
|
-
}
|
|
15550
|
-
latestSteps.set(nodeId, step);
|
|
15551
|
-
}
|
|
15552
|
-
return { completedNodeIds, latestSteps };
|
|
15553
|
-
}
|
|
15554
|
-
function getAllPredecessors(def, nodeId) {
|
|
15555
|
-
const preds = [];
|
|
15556
|
-
for (const node of def.nodes) {
|
|
15557
|
-
if (!node.next)
|
|
15558
|
-
continue;
|
|
15559
|
-
if (getNextTargets(node.next).includes(nodeId)) {
|
|
15560
|
-
preds.push(node.id);
|
|
15561
|
-
}
|
|
15562
|
-
}
|
|
15563
|
-
return preds;
|
|
15564
|
-
}
|
|
15565
|
-
async function executeStep(def, runId, ctx, node, registry, workflowId, secretKeys = new Set, options = {}) {
|
|
15566
|
-
let claimedStepId;
|
|
15567
|
-
const claimStep = async () => {
|
|
15568
|
-
const run = await getWorkflowRun(runId);
|
|
15569
|
-
if (!run || run.status !== "running" && run.status !== "waiting") {
|
|
15570
|
-
return { halted: true };
|
|
15571
|
-
}
|
|
15572
|
-
if (node.type !== "foreach") {
|
|
15573
|
-
const current = await getCurrentStepForNode(runId, node.id, getAllPredecessors(def, node.id));
|
|
15574
|
-
if (current && (current.status !== "running" || executingSteps.has(current.id))) {
|
|
15575
|
-
return { current };
|
|
15576
|
-
}
|
|
15577
|
-
}
|
|
15578
|
-
const iteration = await getStepCountForNode(runId, node.id);
|
|
15579
|
-
const idempotencyKey = `${runId}:${node.id}:${iteration}`;
|
|
15580
|
-
const existingStep = await getStepByIdempotencyKey(idempotencyKey);
|
|
15581
|
-
if (existingStep && (existingStep.status === "completed" || existingStep.status === "waiting")) {
|
|
15582
|
-
return { existingStep, stepId: existingStep.id, deduped: true };
|
|
15583
|
-
}
|
|
15584
|
-
const latestForeachStep = node.type === "foreach" ? await getLatestStepForNode(runId, node.id) : undefined;
|
|
15585
|
-
const reusableForeachStep = latestForeachStep && !FOREACH_TERMINAL_STEP_STATUSES.has(latestForeachStep.status) ? latestForeachStep : undefined;
|
|
15586
|
-
const stepId = reusableForeachStep?.id ?? crypto.randomUUID();
|
|
15587
|
-
if (!reusableForeachStep) {
|
|
15588
|
-
await createWorkflowRunStep({
|
|
15589
|
-
id: stepId,
|
|
15590
|
-
runId,
|
|
15591
|
-
nodeId: node.id,
|
|
15592
|
-
nodeType: node.type,
|
|
15593
|
-
input: redactSecretsForStorage(ctx, secretKeys),
|
|
15594
|
-
idempotencyKey
|
|
15595
|
-
});
|
|
15596
|
-
}
|
|
15597
|
-
claimedStepId = stepId;
|
|
15598
|
-
executingSteps.add(stepId);
|
|
15599
|
-
return { existingStep, stepId, deduped: false };
|
|
15600
|
-
};
|
|
15601
|
-
const dedup = await getDbClient().transaction(claimStep).catch((err) => {
|
|
15602
|
-
if (claimedStepId)
|
|
15603
|
-
executingSteps.delete(claimedStepId);
|
|
15604
|
-
throw err;
|
|
15605
|
-
});
|
|
15606
|
-
if ("halted" in dedup)
|
|
15607
|
-
return { outcome: "completed", successors: [] };
|
|
15608
|
-
if ("current" in dedup && dedup.current) {
|
|
15609
|
-
if (dedup.current.status !== "completed")
|
|
15610
|
-
return { outcome: "waiting", successors: [] };
|
|
15611
|
-
ctx[node.id] = dedup.current.output;
|
|
15612
|
-
return { outcome: "completed", successors: [] };
|
|
15613
|
-
}
|
|
15614
|
-
if (dedup.deduped && dedup.existingStep) {
|
|
15615
|
-
if (dedup.existingStep.status === "completed") {
|
|
15616
|
-
ctx[node.id] = dedup.existingStep.output;
|
|
15617
|
-
const successors = getSuccessors(def, node.id);
|
|
15618
|
-
return { outcome: "completed", successors };
|
|
15619
|
-
}
|
|
15620
|
-
return { outcome: "waiting", successors: [] };
|
|
15621
|
-
}
|
|
15622
|
-
try {
|
|
15623
|
-
return await runClaimedStep(def, runId, ctx, node, registry, dedup.existingStep, dedup.stepId, workflowId, options);
|
|
15624
|
-
} catch (err) {
|
|
15625
|
-
if (await failRunOnUnreadableReplay(runId, err, dedup.stepId)) {
|
|
15626
|
-
return { outcome: "failed", successors: [] };
|
|
15627
|
-
}
|
|
15628
|
-
throw err;
|
|
15629
|
-
} finally {
|
|
15630
|
-
executingSteps.delete(dedup.stepId);
|
|
15631
|
-
}
|
|
15632
|
-
}
|
|
15633
|
-
async function runClaimedStep(def, runId, ctx, node, registry, existingStep, stepId, workflowId, options = {}) {
|
|
15634
|
-
const executor = registry.get(node.type);
|
|
15635
|
-
const systemOneRetryProblems = systemOneRetryViolations(node);
|
|
15636
|
-
if (systemOneRetryProblems.length > 0) {
|
|
15637
|
-
await checkpointStepFailure(runId, stepId, systemOneRetryProblems.join("; "), 0);
|
|
15638
|
-
return { outcome: "failed", successors: [] };
|
|
15639
|
-
}
|
|
15640
|
-
const interpolationCtx = buildNodeInterpolationCtx(node, ctx);
|
|
15641
|
-
if (node.inputSchema) {
|
|
15642
|
-
const inputErrors = validateJsonSchema(node.inputSchema, interpolationCtx);
|
|
15643
|
-
if (inputErrors.length > 0) {
|
|
15644
|
-
const errorMsg = `Input schema validation failed: ${inputErrors.join("; ")}`;
|
|
15645
|
-
await checkpointStepFailure(runId, stepId, errorMsg, 0);
|
|
15646
|
-
throw new Error(errorMsg);
|
|
15647
|
-
}
|
|
15648
|
-
}
|
|
15649
|
-
const {
|
|
15650
|
-
value: interpolatedValue,
|
|
15651
|
-
unresolved,
|
|
15652
|
-
scriptBodyUnresolved,
|
|
15653
|
-
strictUnresolved
|
|
15654
|
-
} = interpolateNodeConfig(node, interpolationCtx);
|
|
15655
|
-
const interpolatedConfig = interpolatedValue;
|
|
15656
|
-
const executionCtx = { ...ctx, ...interpolationCtx };
|
|
15657
|
-
if (scriptBodyUnresolved && scriptBodyUnresolved.length > 0) {
|
|
15658
|
-
const errorMsg = scriptBodyInterpolationError(node.id, scriptBodyUnresolved);
|
|
15659
|
-
await checkpointStepFailure(runId, stepId, errorMsg, 0);
|
|
15660
|
-
return { outcome: "failed", successors: [] };
|
|
15661
|
-
}
|
|
15662
|
-
if (strictUnresolved && strictUnresolved.length > 0) {
|
|
15663
|
-
await checkpointStepFailure(runId, stepId, systemOneUnresolvedError(node.id, strictUnresolved), 0);
|
|
15664
|
-
return { outcome: "failed", successors: [] };
|
|
15665
|
-
}
|
|
15666
|
-
if (unresolved.length > 0) {
|
|
15667
|
-
console.warn(`[workflow] Step ${node.id}: unresolved interpolation tokens: ${unresolved.join(", ")}`);
|
|
15668
|
-
await updateWorkflowRunStep(stepId, {
|
|
15669
|
-
diagnostics: JSON.stringify({ unresolvedTokens: unresolved })
|
|
15670
|
-
});
|
|
15671
|
-
}
|
|
15672
|
-
const meta = {
|
|
15673
|
-
runId,
|
|
15674
|
-
stepId,
|
|
15675
|
-
nodeId: node.id,
|
|
15676
|
-
workflowId: workflowId || "",
|
|
15677
|
-
dryRun: false,
|
|
15678
|
-
requestedByUserId: options.requestedByUserId,
|
|
15679
|
-
inputCtx: interpolationCtx
|
|
15680
|
-
};
|
|
15681
|
-
const configuredTimeoutMs = typeof node.config?.timeoutMs === "number" ? node.config.timeoutMs : node.type === "script" && typeof node.config?.timeout === "number" ? node.config.timeout : undefined;
|
|
15682
|
-
const timeoutMs = configuredTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
15683
|
-
let result;
|
|
15684
|
-
try {
|
|
15685
|
-
result = await Promise.race([
|
|
15686
|
-
executor.run({
|
|
15687
|
-
config: interpolatedConfig,
|
|
15688
|
-
context: executionCtx,
|
|
15689
|
-
meta
|
|
15690
|
-
}),
|
|
15691
|
-
timeoutPromise(timeoutMs)
|
|
15692
|
-
]);
|
|
15693
|
-
} catch (err) {
|
|
15694
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
15695
|
-
const retryPolicy = node.retry || executor.retryPolicy;
|
|
15696
|
-
const currentRetryCount = existingStep?.retryCount || 0;
|
|
15697
|
-
const { shouldRetry } = await checkpointStepFailure(runId, stepId, errorMsg, currentRetryCount, retryPolicy);
|
|
15698
|
-
if (!shouldRetry) {
|
|
15699
|
-
throw err;
|
|
15700
|
-
}
|
|
15701
|
-
return { outcome: "completed", successors: [] };
|
|
15702
|
-
}
|
|
15703
|
-
if (result.status === "failed") {
|
|
15704
|
-
const retryPolicy = node.retry || executor.retryPolicy;
|
|
15705
|
-
const currentRetryCount = existingStep?.retryCount || 0;
|
|
15706
|
-
const { shouldRetry } = await checkpointStepFailure(runId, stepId, result.error || "Executor returned failed status", currentRetryCount, retryPolicy);
|
|
15707
|
-
if (result.output !== undefined) {
|
|
15708
|
-
await updateWorkflowRunStep(stepId, { output: result.output });
|
|
15709
|
-
}
|
|
15710
|
-
if (!shouldRetry) {
|
|
15711
|
-
throw new Error(result.error || "Step execution failed");
|
|
15712
|
-
}
|
|
15713
|
-
return { outcome: "completed", successors: [] };
|
|
15714
|
-
}
|
|
15715
|
-
if ("async" in result && result.async) {
|
|
15716
|
-
const waiting = await checkpointStepWaiting(runId, stepId, ctx);
|
|
15717
|
-
return { outcome: waiting ? "waiting" : "completed", successors: [] };
|
|
15718
|
-
}
|
|
15719
|
-
if (node.outputSchema && result.status === "success") {
|
|
15720
|
-
const outputErrors = validateJsonSchema(node.outputSchema, result.output);
|
|
15721
|
-
if (outputErrors.length > 0) {
|
|
15722
|
-
const errorMsg = `Output schema validation failed: ${outputErrors.join("; ")}`;
|
|
15723
|
-
await checkpointStepFailure(runId, stepId, errorMsg, 0);
|
|
15724
|
-
throw new Error(errorMsg);
|
|
15725
|
-
}
|
|
15726
|
-
}
|
|
15727
|
-
let validationResult;
|
|
15728
|
-
if (node.validation) {
|
|
15729
|
-
validationResult = await runStepValidation(registry, node, result.output, executionCtx, meta);
|
|
15730
|
-
if (validationResult.outcome === "halt") {
|
|
15731
|
-
const errorMsg = "Validation failed (mustPass)";
|
|
15732
|
-
await checkpointStepFailure(runId, stepId, errorMsg, 0, undefined, { markRunFailed: false });
|
|
15733
|
-
return { outcome: "failed", successors: [] };
|
|
15734
|
-
}
|
|
15735
|
-
if (validationResult.outcome === "retry") {
|
|
15736
|
-
if (validationResult.retryContext) {
|
|
15737
|
-
const historyKey = `${node.id}_validations`;
|
|
15738
|
-
const existing = ctx[historyKey] || [];
|
|
15739
|
-
ctx[historyKey] = [...existing, validationResult.retryContext];
|
|
15740
|
-
}
|
|
15741
|
-
const retryPolicy = node.validation.retry || node.retry;
|
|
15742
|
-
const currentRetryCount = existingStep?.retryCount || 0;
|
|
15743
|
-
await checkpointStepFailure(runId, stepId, "Validation failed, retrying", currentRetryCount, retryPolicy);
|
|
15744
|
-
return { outcome: "completed", successors: [] };
|
|
15745
|
-
}
|
|
15746
|
-
}
|
|
15747
|
-
result.nextPort = resolveValidationPort(node.next, result.nextPort, validationResult?.passed);
|
|
15748
|
-
await checkpointStep(runId, stepId, node.id, result, ctx);
|
|
15749
|
-
const successors = result.nextPort ? getSuccessors(def, node.id, result.nextPort) : getSuccessors(def, node.id);
|
|
15750
|
-
return { outcome: "completed", successors };
|
|
15751
|
-
}
|
|
15752
|
-
function findReadyNodes(def, completedNodeIds, activeEdges) {
|
|
15753
|
-
const predecessors = new Map;
|
|
15754
|
-
for (const node of def.nodes) {
|
|
15755
|
-
if (!predecessors.has(node.id)) {
|
|
15756
|
-
predecessors.set(node.id, new Set);
|
|
15757
|
-
}
|
|
15758
|
-
}
|
|
15759
|
-
for (const node of def.nodes) {
|
|
15760
|
-
if (!node.next)
|
|
15761
|
-
continue;
|
|
15762
|
-
for (const target of getNextTargets(node.next)) {
|
|
15763
|
-
if (!predecessors.has(target)) {
|
|
15764
|
-
predecessors.set(target, new Set);
|
|
15765
|
-
}
|
|
15766
|
-
predecessors.get(target).add(node.id);
|
|
15767
|
-
}
|
|
15768
|
-
}
|
|
15769
|
-
return def.nodes.filter((node) => {
|
|
15770
|
-
if (completedNodeIds.has(node.id))
|
|
15771
|
-
return false;
|
|
15772
|
-
const preds = predecessors.get(node.id);
|
|
15773
|
-
if (!preds || preds.size === 0)
|
|
15774
|
-
return true;
|
|
15775
|
-
if (activeEdges) {
|
|
15776
|
-
const activePreds = [...preds].filter((predId) => activeEdges.has(`${predId}→${node.id}`));
|
|
15777
|
-
if (activePreds.length === 0)
|
|
15778
|
-
return false;
|
|
15779
|
-
return activePreds.every((pred) => completedNodeIds.has(pred));
|
|
15780
|
-
}
|
|
15781
|
-
for (const pred of preds) {
|
|
15782
|
-
if (!completedNodeIds.has(pred))
|
|
15783
|
-
return false;
|
|
15784
|
-
}
|
|
15785
|
-
return true;
|
|
15786
|
-
});
|
|
15787
|
-
}
|
|
15788
|
-
function buildNodeInterpolationCtx(node, ctx) {
|
|
15789
|
-
const interpolationCtx = {};
|
|
15790
|
-
if (ctx.trigger !== undefined)
|
|
15791
|
-
interpolationCtx.trigger = ctx.trigger;
|
|
15792
|
-
if (ctx.input !== undefined)
|
|
15793
|
-
interpolationCtx.input = ctx.input;
|
|
15794
|
-
if (ctx.workflow !== undefined)
|
|
15795
|
-
interpolationCtx.workflow = ctx.workflow;
|
|
15796
|
-
if (ctx.swarm !== undefined)
|
|
15797
|
-
interpolationCtx.swarm = ctx.swarm;
|
|
15798
|
-
if (ctx.run !== undefined)
|
|
15799
|
-
interpolationCtx.run = ctx.run;
|
|
15800
|
-
if (!node.inputs)
|
|
15801
|
-
return interpolationCtx;
|
|
15802
|
-
for (const [localName, sourcePath] of Object.entries(node.inputs)) {
|
|
15803
|
-
const keys = sourcePath.split(".");
|
|
15804
|
-
let value = ctx;
|
|
15805
|
-
for (const key of keys) {
|
|
15806
|
-
if (value == null || typeof value !== "object") {
|
|
15807
|
-
value = undefined;
|
|
15808
|
-
break;
|
|
15809
|
-
}
|
|
15810
|
-
value = value[key];
|
|
15811
|
-
}
|
|
15812
|
-
interpolationCtx[localName] = value;
|
|
15813
|
-
}
|
|
15814
|
-
return interpolationCtx;
|
|
15815
|
-
}
|
|
15816
|
-
var SCRIPT_BODY_ALLOWED_SOURCES = new Set(["input", "workflow", "swarm", "run"]);
|
|
15817
|
-
function buildScriptBodyCtx(node, interpolationCtx) {
|
|
15818
|
-
const aliases = node.inputs ?? {};
|
|
15819
|
-
const safe = {};
|
|
15820
|
-
for (const [key, value] of Object.entries(interpolationCtx)) {
|
|
15821
|
-
const sourcePath = aliases[key];
|
|
15822
|
-
const root = sourcePath === undefined ? key : sourcePath.split(".")[0] ?? "";
|
|
15823
|
-
if (SCRIPT_BODY_ALLOWED_SOURCES.has(root))
|
|
15824
|
-
safe[key] = value;
|
|
15825
|
-
}
|
|
15826
|
-
return safe;
|
|
15827
|
-
}
|
|
15828
|
-
function interpolateNodeConfig(node, interpolationCtx) {
|
|
15829
|
-
if (node.type === SYSTEM_ONE_DECISION_NODE_TYPE) {
|
|
15830
|
-
const { state, questions, ...rest } = node.config;
|
|
15831
|
-
const restResult = deepInterpolate(rest, interpolationCtx);
|
|
15832
|
-
const dynamic = {};
|
|
15833
|
-
if (Object.hasOwn(node.config, "state"))
|
|
15834
|
-
dynamic.state = state;
|
|
15835
|
-
if (Object.hasOwn(node.config, "questions"))
|
|
15836
|
-
dynamic.questions = questions;
|
|
15837
|
-
const dynamicResult = deepInterpolate(dynamic, interpolationCtx, { preserveRawTokens: true });
|
|
15838
|
-
const unresolved = [...restResult.unresolved, ...dynamicResult.unresolved];
|
|
15839
|
-
return {
|
|
15840
|
-
value: {
|
|
15841
|
-
...restResult.value,
|
|
15842
|
-
...dynamicResult.value
|
|
15843
|
-
},
|
|
15844
|
-
unresolved,
|
|
15845
|
-
strictUnresolved: unresolved
|
|
15846
|
-
};
|
|
15847
|
-
}
|
|
15848
|
-
if (node.type === "foreach" && Object.hasOwn(node.config, "over")) {
|
|
15849
|
-
const { over, body, ...configWithoutOverAndBody } = node.config;
|
|
15850
|
-
const configResult2 = deepInterpolate(configWithoutOverAndBody, interpolationCtx);
|
|
15851
|
-
const overResult = deepInterpolate(over, interpolationCtx, { preserveRawTokens: true });
|
|
15852
|
-
return {
|
|
15853
|
-
value: {
|
|
15854
|
-
...configResult2.value,
|
|
15855
|
-
over: overResult.value,
|
|
15856
|
-
body
|
|
15857
|
-
},
|
|
15858
|
-
unresolved: [...configResult2.unresolved, ...overResult.unresolved]
|
|
15859
|
-
};
|
|
15860
|
-
}
|
|
15861
|
-
if (node.type === "human-in-the-loop" && typeof node.config.questions === "string") {
|
|
15862
|
-
const { questions, ...configWithoutQuestions } = node.config;
|
|
15863
|
-
const configResult2 = deepInterpolate(configWithoutQuestions, interpolationCtx);
|
|
15864
|
-
const questionsResult = deepInterpolate(questions, interpolationCtx, {
|
|
15865
|
-
preserveRawTokens: true
|
|
15866
|
-
});
|
|
15867
|
-
return {
|
|
15868
|
-
value: {
|
|
15869
|
-
...configResult2.value,
|
|
15870
|
-
questions: questionsResult.value
|
|
15871
|
-
},
|
|
15872
|
-
unresolved: [...configResult2.unresolved, ...questionsResult.unresolved]
|
|
15873
|
-
};
|
|
15874
|
-
}
|
|
15875
|
-
if (node.type === "script" && typeof node.config.script === "string") {
|
|
15876
|
-
const { script, ...configWithoutScript } = node.config;
|
|
15877
|
-
const restResult = deepInterpolate(configWithoutScript, interpolationCtx);
|
|
15878
|
-
const scriptResult = deepInterpolate(script, buildScriptBodyCtx(node, interpolationCtx));
|
|
15879
|
-
return {
|
|
15880
|
-
value: {
|
|
15881
|
-
...restResult.value,
|
|
15882
|
-
script: scriptResult.unresolved.length > 0 ? script : scriptResult.value
|
|
15883
|
-
},
|
|
15884
|
-
unresolved: [...restResult.unresolved, ...scriptResult.unresolved],
|
|
15885
|
-
scriptBodyUnresolved: scriptResult.unresolved
|
|
15886
|
-
};
|
|
15887
|
-
}
|
|
15888
|
-
if (node.type !== "swarm-script" || !Object.hasOwn(node.config, "args")) {
|
|
15889
|
-
return deepInterpolate(node.config, interpolationCtx);
|
|
15890
|
-
}
|
|
15891
|
-
const { args, ...configWithoutArgs } = node.config;
|
|
15892
|
-
const configResult = deepInterpolate(configWithoutArgs, interpolationCtx);
|
|
15893
|
-
const argsResult = deepInterpolate(args, interpolationCtx, { preserveRawTokens: true });
|
|
15894
|
-
return {
|
|
15895
|
-
value: {
|
|
15896
|
-
...configResult.value,
|
|
15897
|
-
args: argsResult.value
|
|
15898
|
-
},
|
|
15899
|
-
unresolved: [...configResult.unresolved, ...argsResult.unresolved]
|
|
15900
|
-
};
|
|
15901
|
-
}
|
|
15902
|
-
function scriptBodyInterpolationError(nodeId, tokens) {
|
|
15903
|
-
const renderedTokens = [...new Set(tokens)].map((token) => `{{${token}}}`).join(", ");
|
|
15904
|
-
return `Script body interpolation failed for node "${nodeId}": unresolved or disallowed token(s): ${renderedTokens}. ` + "Executable source may interpolate only input, workflow, swarm, or run values. " + "Pass dynamic trigger or upstream values through config.args and read them from argv.";
|
|
15905
|
-
}
|
|
15906
|
-
function timeoutPromise(ms) {
|
|
15907
|
-
return new Promise((_, reject) => {
|
|
15908
|
-
setTimeout(() => reject(new Error(`Step timed out after ${ms}ms`)), ms);
|
|
15909
|
-
});
|
|
15910
|
-
}
|
|
15911
14416
|
|
|
15912
14417
|
// src/workflows/index.ts
|
|
15913
14418
|
init_event_bus();
|
|
15914
14419
|
|
|
15915
14420
|
// src/workflows/recovery.ts
|
|
15916
14421
|
init_db2();
|
|
15917
|
-
|
|
15918
|
-
// src/workflows/approval-resolution.ts
|
|
15919
|
-
function shapeApprovalResolution(registry, definition, nodeId, parkedOutput, approval) {
|
|
15920
|
-
const node = definition.nodes.find((candidate) => candidate.id === nodeId);
|
|
15921
|
-
if (node && registry.has(node.type)) {
|
|
15922
|
-
const shaped = registry.get(node.type).resolveApproval(parkedOutput, approval);
|
|
15923
|
-
if (shaped)
|
|
15924
|
-
return shaped;
|
|
15925
|
-
}
|
|
15926
|
-
return {
|
|
15927
|
-
output: {
|
|
15928
|
-
requestId: approval.requestId,
|
|
15929
|
-
status: approval.status,
|
|
15930
|
-
responses: approval.responses
|
|
15931
|
-
},
|
|
15932
|
-
nextPort: approval.status === "timeout" ? "timeout" : approval.status === "rejected" ? "rejected" : "approved"
|
|
15933
|
-
};
|
|
15934
|
-
}
|
|
15935
|
-
|
|
15936
|
-
// src/workflows/constants.ts
|
|
15937
|
-
var FAILED_TASK_OUTPUT_PREFIX = "[FAILED:";
|
|
15938
|
-
|
|
15939
|
-
// src/workflows/resume.ts
|
|
15940
|
-
init_db2();
|
|
15941
|
-
init_secret_scrubber();
|
|
15942
|
-
init_event_bus();
|
|
15943
|
-
|
|
15944
|
-
// src/workflows/executors/wait.ts
|
|
15945
|
-
init_zod();
|
|
15946
|
-
|
|
15947
|
-
// src/workflows/wait-filter.ts
|
|
15948
|
-
var PARAM_SHADOW_KEYS = [
|
|
15949
|
-
"require",
|
|
15950
|
-
"process",
|
|
15951
|
-
"Bun",
|
|
15952
|
-
"globalThis",
|
|
15953
|
-
"global",
|
|
15954
|
-
"fetch",
|
|
15955
|
-
"setTimeout",
|
|
15956
|
-
"setInterval"
|
|
15957
|
-
];
|
|
15958
|
-
var PARAM_SHADOW_VALUES = PARAM_SHADOW_KEYS.map(() => {
|
|
15959
|
-
return;
|
|
15960
|
-
});
|
|
15961
|
-
var BODY_SHADOW_KEYS = ["eval", "Function", "AsyncFunction"];
|
|
15962
|
-
var BODY_SHADOW_PREAMBLE = BODY_SHADOW_KEYS.map((k) => `var ${k} = undefined;`).join(" ");
|
|
15963
|
-
var FILTER_TIMEOUT_MS = 50;
|
|
15964
|
-
function getByDotPath(obj, path) {
|
|
15965
|
-
if (obj === null || typeof obj !== "object") {
|
|
15966
|
-
return { found: false, value: undefined };
|
|
15967
|
-
}
|
|
15968
|
-
const segments = path.split(".");
|
|
15969
|
-
let cursor = obj;
|
|
15970
|
-
for (const seg of segments) {
|
|
15971
|
-
if (cursor === null || typeof cursor !== "object") {
|
|
15972
|
-
return { found: false, value: undefined };
|
|
15973
|
-
}
|
|
15974
|
-
const rec = cursor;
|
|
15975
|
-
if (!(seg in rec)) {
|
|
15976
|
-
return { found: false, value: undefined };
|
|
15977
|
-
}
|
|
15978
|
-
cursor = rec[seg];
|
|
15979
|
-
}
|
|
15980
|
-
return { found: true, value: cursor };
|
|
15981
|
-
}
|
|
15982
|
-
function deepEqual(a, b) {
|
|
15983
|
-
if (a === b)
|
|
15984
|
-
return true;
|
|
15985
|
-
if (typeof a !== typeof b)
|
|
15986
|
-
return false;
|
|
15987
|
-
if (a === null || b === null)
|
|
15988
|
-
return false;
|
|
15989
|
-
if (Array.isArray(a)) {
|
|
15990
|
-
if (!Array.isArray(b))
|
|
15991
|
-
return false;
|
|
15992
|
-
if (a.length !== b.length)
|
|
15993
|
-
return false;
|
|
15994
|
-
for (let i = 0;i < a.length; i++) {
|
|
15995
|
-
if (!deepEqual(a[i], b[i]))
|
|
15996
|
-
return false;
|
|
15997
|
-
}
|
|
15998
|
-
return true;
|
|
15999
|
-
}
|
|
16000
|
-
if (typeof a === "object" && typeof b === "object") {
|
|
16001
|
-
const aKeys = Object.keys(a);
|
|
16002
|
-
const bKeys = Object.keys(b);
|
|
16003
|
-
if (aKeys.length !== bKeys.length)
|
|
16004
|
-
return false;
|
|
16005
|
-
for (const k of aKeys) {
|
|
16006
|
-
if (!deepEqual(a[k], b[k])) {
|
|
16007
|
-
return false;
|
|
16008
|
-
}
|
|
16009
|
-
}
|
|
16010
|
-
return true;
|
|
16011
|
-
}
|
|
16012
|
-
return false;
|
|
16013
|
-
}
|
|
16014
|
-
function matchObjectFilter(payload, filter) {
|
|
16015
|
-
for (const [key, expected] of Object.entries(filter)) {
|
|
16016
|
-
const { found, value } = getByDotPath(payload, key);
|
|
16017
|
-
if (!found)
|
|
16018
|
-
return false;
|
|
16019
|
-
if (!deepEqual(value, expected))
|
|
16020
|
-
return false;
|
|
16021
|
-
}
|
|
16022
|
-
return true;
|
|
16023
|
-
}
|
|
16024
|
-
async function runWithTimeout(fn) {
|
|
16025
|
-
return new Promise((resolve) => {
|
|
16026
|
-
let settled = false;
|
|
16027
|
-
const timer = setTimeout(() => {
|
|
16028
|
-
if (settled)
|
|
16029
|
-
return;
|
|
16030
|
-
settled = true;
|
|
16031
|
-
resolve(null);
|
|
16032
|
-
}, FILTER_TIMEOUT_MS);
|
|
16033
|
-
queueMicrotask(() => {
|
|
16034
|
-
if (settled)
|
|
16035
|
-
return;
|
|
16036
|
-
try {
|
|
16037
|
-
const v = fn();
|
|
16038
|
-
if (v !== null && typeof v === "object" && typeof v.then === "function") {
|
|
16039
|
-
v.catch(() => {});
|
|
16040
|
-
settled = true;
|
|
16041
|
-
clearTimeout(timer);
|
|
16042
|
-
resolve({ __asyncRejected: true });
|
|
16043
|
-
return;
|
|
16044
|
-
}
|
|
16045
|
-
if (settled)
|
|
16046
|
-
return;
|
|
16047
|
-
settled = true;
|
|
16048
|
-
clearTimeout(timer);
|
|
16049
|
-
resolve(v);
|
|
16050
|
-
} catch {
|
|
16051
|
-
if (settled)
|
|
16052
|
-
return;
|
|
16053
|
-
settled = true;
|
|
16054
|
-
clearTimeout(timer);
|
|
16055
|
-
resolve(null);
|
|
16056
|
-
}
|
|
16057
|
-
});
|
|
16058
|
-
});
|
|
16059
|
-
}
|
|
16060
|
-
var ASYNC_SENTINEL = "__asyncRejected";
|
|
16061
|
-
function isAsyncSentinel(value) {
|
|
16062
|
-
return value !== null && typeof value === "object" && value[ASYNC_SENTINEL] === true;
|
|
16063
|
-
}
|
|
16064
|
-
function compileStringFilter(filter) {
|
|
16065
|
-
if (filter.length > 2048) {
|
|
16066
|
-
throw new Error(`wait filter source exceeds 2KB cap (${filter.length} bytes)`);
|
|
16067
|
-
}
|
|
16068
|
-
let fn;
|
|
16069
|
-
try {
|
|
16070
|
-
fn = new Function(...PARAM_SHADOW_KEYS, "payload", `${BODY_SHADOW_PREAMBLE} return (${filter})(payload);`);
|
|
16071
|
-
} catch (err) {
|
|
16072
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
16073
|
-
throw new Error(`wait filter compile error: ${msg}`);
|
|
16074
|
-
}
|
|
16075
|
-
return (payload) => fn(...PARAM_SHADOW_VALUES, payload);
|
|
16076
|
-
}
|
|
16077
|
-
function nullifyPrototypes(value) {
|
|
16078
|
-
if (value === null || typeof value !== "object")
|
|
16079
|
-
return value;
|
|
16080
|
-
if (Array.isArray(value)) {
|
|
16081
|
-
return value.map(nullifyPrototypes);
|
|
16082
|
-
}
|
|
16083
|
-
const out = Object.create(null);
|
|
16084
|
-
for (const [k, v] of Object.entries(value)) {
|
|
16085
|
-
out[k] = nullifyPrototypes(v);
|
|
16086
|
-
}
|
|
16087
|
-
return out;
|
|
16088
|
-
}
|
|
16089
|
-
async function matchesFilter(payload, filter) {
|
|
16090
|
-
if (filter === undefined || filter === null)
|
|
16091
|
-
return true;
|
|
16092
|
-
if (typeof filter === "object" && !Array.isArray(filter)) {
|
|
16093
|
-
return matchObjectFilter(payload, filter);
|
|
16094
|
-
}
|
|
16095
|
-
if (typeof filter === "string") {
|
|
16096
|
-
let userFn;
|
|
16097
|
-
try {
|
|
16098
|
-
userFn = compileStringFilter(filter);
|
|
16099
|
-
} catch {
|
|
16100
|
-
return false;
|
|
16101
|
-
}
|
|
16102
|
-
let cloned;
|
|
16103
|
-
try {
|
|
16104
|
-
const cloneOnce = structuredClone(payload);
|
|
16105
|
-
cloned = nullifyPrototypes(cloneOnce);
|
|
16106
|
-
} catch {
|
|
16107
|
-
try {
|
|
16108
|
-
cloned = nullifyPrototypes(payload);
|
|
16109
|
-
} catch {
|
|
16110
|
-
cloned = payload;
|
|
16111
|
-
}
|
|
16112
|
-
}
|
|
16113
|
-
const result = await runWithTimeout(() => userFn(cloned));
|
|
16114
|
-
if (result === null)
|
|
16115
|
-
return false;
|
|
16116
|
-
if (isAsyncSentinel(result))
|
|
16117
|
-
return false;
|
|
16118
|
-
return !!result;
|
|
16119
|
-
}
|
|
16120
|
-
return false;
|
|
16121
|
-
}
|
|
16122
|
-
|
|
16123
|
-
// src/workflows/executors/wait.ts
|
|
16124
|
-
var WaitConfigSchema = exports_external.discriminatedUnion("mode", [
|
|
16125
|
-
exports_external.object({
|
|
16126
|
-
mode: exports_external.literal("time"),
|
|
16127
|
-
durationMs: exports_external.number().int().min(1).max(31536000000).describe("Wait duration in milliseconds (effective resolution ~5s)")
|
|
16128
|
-
}),
|
|
16129
|
-
exports_external.object({
|
|
16130
|
-
mode: exports_external.literal("event"),
|
|
16131
|
-
eventName: exports_external.string().min(1),
|
|
16132
|
-
filter: exports_external.union([exports_external.record(exports_external.string(), exports_external.unknown()), exports_external.string().max(2048)]).optional(),
|
|
16133
|
-
scope: exports_external.enum(["run", "global"]).default("run"),
|
|
16134
|
-
timeoutMs: exports_external.number().int().min(1).max(31536000000).optional().describe("Timeout in milliseconds (effective resolution ~5s) — when reached, routes via 'timeout' port")
|
|
16135
|
-
})
|
|
16136
|
-
]);
|
|
16137
|
-
var WaitOutputSchema = exports_external.object({
|
|
16138
|
-
waitId: exports_external.string().uuid(),
|
|
16139
|
-
mode: exports_external.enum(["time", "event"]),
|
|
16140
|
-
firedAt: exports_external.string().nullable(),
|
|
16141
|
-
payload: exports_external.unknown().optional()
|
|
16142
|
-
});
|
|
16143
|
-
|
|
16144
|
-
class WaitExecutor extends BaseExecutor {
|
|
16145
|
-
type = "wait";
|
|
16146
|
-
mode = "async";
|
|
16147
|
-
configSchema = WaitConfigSchema;
|
|
16148
|
-
outputSchema = WaitOutputSchema;
|
|
16149
|
-
async execute(config, _context, meta) {
|
|
16150
|
-
const { db } = this.deps;
|
|
16151
|
-
const existing = await db.getWaitStateByStepId(meta.stepId);
|
|
16152
|
-
if (existing) {
|
|
16153
|
-
if (existing.status !== "pending") {
|
|
16154
|
-
const nextPort = computeNextPort(existing.mode, existing.status);
|
|
16155
|
-
return {
|
|
16156
|
-
status: "success",
|
|
16157
|
-
output: {
|
|
16158
|
-
waitId: existing.id,
|
|
16159
|
-
mode: existing.mode,
|
|
16160
|
-
firedAt: existing.resolvedAt,
|
|
16161
|
-
payload: existing.firedPayload ?? undefined
|
|
16162
|
-
},
|
|
16163
|
-
nextPort
|
|
16164
|
-
};
|
|
16165
|
-
}
|
|
16166
|
-
return {
|
|
16167
|
-
status: "success",
|
|
16168
|
-
async: true,
|
|
16169
|
-
waitFor: "wait.fired",
|
|
16170
|
-
correlationId: existing.id
|
|
16171
|
-
};
|
|
16172
|
-
}
|
|
16173
|
-
if (config.mode === "time") {
|
|
16174
|
-
const waitId2 = crypto.randomUUID();
|
|
16175
|
-
const wakeUpAt = new Date(Date.now() + config.durationMs).toISOString();
|
|
16176
|
-
await db.createWaitState({
|
|
16177
|
-
id: waitId2,
|
|
16178
|
-
workflowRunId: meta.runId,
|
|
16179
|
-
workflowRunStepId: meta.stepId,
|
|
16180
|
-
mode: "time",
|
|
16181
|
-
wakeUpAt
|
|
16182
|
-
});
|
|
16183
|
-
return {
|
|
16184
|
-
status: "success",
|
|
16185
|
-
async: true,
|
|
16186
|
-
waitFor: "wait.fired",
|
|
16187
|
-
correlationId: waitId2
|
|
16188
|
-
};
|
|
16189
|
-
}
|
|
16190
|
-
if (typeof config.filter === "string") {
|
|
16191
|
-
compileStringFilter(config.filter);
|
|
16192
|
-
}
|
|
16193
|
-
const waitId = crypto.randomUUID();
|
|
16194
|
-
const expiresAt = config.timeoutMs ? new Date(Date.now() + config.timeoutMs).toISOString() : null;
|
|
16195
|
-
await db.createWaitState({
|
|
16196
|
-
id: waitId,
|
|
16197
|
-
workflowRunId: meta.runId,
|
|
16198
|
-
workflowRunStepId: meta.stepId,
|
|
16199
|
-
mode: "event",
|
|
16200
|
-
eventName: config.eventName,
|
|
16201
|
-
eventFilter: config.filter ?? null,
|
|
16202
|
-
expiresAt,
|
|
16203
|
-
scope: config.scope
|
|
16204
|
-
});
|
|
16205
|
-
subscribeWaitToBus(waitId, config.eventName);
|
|
16206
|
-
return {
|
|
16207
|
-
status: "success",
|
|
16208
|
-
async: true,
|
|
16209
|
-
waitFor: "wait.fired",
|
|
16210
|
-
correlationId: waitId
|
|
16211
|
-
};
|
|
16212
|
-
}
|
|
16213
|
-
}
|
|
16214
|
-
function computeNextPort(mode, status) {
|
|
16215
|
-
if (mode === "time")
|
|
16216
|
-
return "default";
|
|
16217
|
-
return status === "timeout" ? "timeout" : "event";
|
|
16218
|
-
}
|
|
16219
|
-
|
|
16220
|
-
// src/workflows/task-step-routing.ts
|
|
16221
|
-
init_db2();
|
|
16222
|
-
init_types();
|
|
16223
|
-
async function isTaskBoundToStep(taskId, stepId) {
|
|
16224
|
-
const task = await getTaskById(taskId);
|
|
16225
|
-
return task?.workflowRunStepId === stepId;
|
|
16226
|
-
}
|
|
16227
|
-
async function failStepAndRunIfWaiting(stepId, runId, reason, ownerTaskId) {
|
|
16228
|
-
const now = new Date().toISOString();
|
|
16229
|
-
return await getDbClient().transaction(async () => {
|
|
16230
|
-
const current = await getWorkflowRunStep(stepId);
|
|
16231
|
-
if (!current || current.status !== "waiting")
|
|
16232
|
-
return false;
|
|
16233
|
-
if (ownerTaskId && !await isTaskBoundToStep(ownerTaskId, stepId))
|
|
16234
|
-
return false;
|
|
16235
|
-
await updateWorkflowRunStep(stepId, {
|
|
16236
|
-
status: "failed",
|
|
16237
|
-
error: reason,
|
|
16238
|
-
finishedAt: now
|
|
16239
|
-
});
|
|
16240
|
-
await updateWorkflowRun(runId, {
|
|
16241
|
-
status: "failed",
|
|
16242
|
-
error: reason,
|
|
16243
|
-
finishedAt: now
|
|
16244
|
-
});
|
|
16245
|
-
return true;
|
|
16246
|
-
});
|
|
16247
|
-
}
|
|
16248
|
-
async function checkpointPortStepAndResolveSuccessors(def, runId, stepId, nodeId, output, nextPort, ctx) {
|
|
16249
|
-
return await getDbClient().transaction(async () => {
|
|
16250
|
-
const current = await getWorkflowRunStep(stepId);
|
|
16251
|
-
if (!current || current.status !== "waiting")
|
|
16252
|
-
return { claimed: false, successors: [] };
|
|
16253
|
-
await checkpointStep(runId, stepId, nodeId, { output, nextPort }, ctx);
|
|
16254
|
-
await updateWorkflowRun(runId, { status: "running" });
|
|
16255
|
-
return { claimed: true, successors: getSuccessors(def, nodeId, nextPort) };
|
|
16256
|
-
});
|
|
16257
|
-
}
|
|
16258
|
-
async function scheduleTaskStepRetry(def, runId, step, taskId, reason) {
|
|
16259
|
-
const node = def.nodes.find((n) => n.id === step.nodeId);
|
|
16260
|
-
if (!node || node.type !== "agent-task" || !node.retry)
|
|
16261
|
-
return "not-eligible";
|
|
16262
|
-
const parsed = RetryPolicySchema.safeParse(node.retry);
|
|
16263
|
-
if (!parsed.success)
|
|
16264
|
-
return "not-eligible";
|
|
16265
|
-
const policy = parsed.data;
|
|
16266
|
-
return await getDbClient().transaction(async () => {
|
|
16267
|
-
const current = await getWorkflowRunStep(step.id);
|
|
16268
|
-
if (!current || current.status !== "waiting")
|
|
16269
|
-
return "not-claimed";
|
|
16270
|
-
if (!await isTaskBoundToStep(taskId, step.id))
|
|
16271
|
-
return "not-claimed";
|
|
16272
|
-
if (current.retryCount >= policy.maxRetries)
|
|
16273
|
-
return "not-eligible";
|
|
16274
|
-
await detachTaskFromWorkflowRunStep(taskId);
|
|
16275
|
-
await checkpointStepFailure(runId, step.id, reason, current.retryCount, policy);
|
|
16276
|
-
return "scheduled";
|
|
16277
|
-
});
|
|
16278
|
-
}
|
|
16279
|
-
var UNCLAIMED = {
|
|
16280
|
-
claimed: false,
|
|
16281
|
-
foreachChild: false,
|
|
16282
|
-
joined: false,
|
|
16283
|
-
successors: []
|
|
16284
|
-
};
|
|
16285
|
-
async function completeTaskStepAndResolveSuccessors(def, runId, step, output, ctx, failureReason, ownerTaskId) {
|
|
16286
|
-
return await getDbClient().transaction(async () => {
|
|
16287
|
-
const current = await getWorkflowRunStep(step.id);
|
|
16288
|
-
if (!current || current.status !== "waiting")
|
|
16289
|
-
return UNCLAIMED;
|
|
16290
|
-
if (ownerTaskId && !await isTaskBoundToStep(ownerTaskId, step.id))
|
|
16291
|
-
return UNCLAIMED;
|
|
16292
|
-
const foreachParent = resolveForeachParent(def, step.nodeId);
|
|
16293
|
-
if (foreachParent) {
|
|
16294
|
-
await updateWorkflowRunStep(step.id, {
|
|
16295
|
-
status: "completed",
|
|
16296
|
-
output,
|
|
16297
|
-
...failureReason !== undefined ? { error: failureReason } : {},
|
|
16298
|
-
finishedAt: new Date().toISOString()
|
|
16299
|
-
});
|
|
16300
|
-
const join = await joinForeach(def, runId, step, ctx);
|
|
16301
|
-
await updateWorkflowRun(runId, { status: "running" });
|
|
16302
|
-
return {
|
|
16303
|
-
claimed: true,
|
|
16304
|
-
foreachChild: true,
|
|
16305
|
-
joined: join.joined,
|
|
16306
|
-
successors: join.successors
|
|
16307
|
-
};
|
|
16308
|
-
}
|
|
16309
|
-
await checkpointStep(runId, step.id, step.nodeId, { output }, ctx);
|
|
16310
|
-
await updateWorkflowRun(runId, { status: "running" });
|
|
16311
|
-
return {
|
|
16312
|
-
claimed: true,
|
|
16313
|
-
foreachChild: false,
|
|
16314
|
-
joined: true,
|
|
16315
|
-
successors: getSuccessors(def, step.nodeId)
|
|
16316
|
-
};
|
|
16317
|
-
});
|
|
16318
|
-
}
|
|
16319
|
-
|
|
16320
|
-
// src/workflows/resume.ts
|
|
16321
|
-
function setupWorkflowResumeListener(eventBus, registry) {
|
|
16322
|
-
const onTaskCompleted = async (data) => {
|
|
16323
|
-
try {
|
|
16324
|
-
const event = data;
|
|
16325
|
-
if (!event.workflowRunId || !event.workflowRunStepId)
|
|
16326
|
-
return;
|
|
16327
|
-
await resumeFromTaskCompletion(event, registry);
|
|
16328
|
-
} catch (err) {
|
|
16329
|
-
console.error("[workflows] Resume from task completion failed:", err);
|
|
16330
|
-
}
|
|
16331
|
-
};
|
|
16332
|
-
eventBus.on("task.completed", onTaskCompleted);
|
|
16333
|
-
const onTaskFailed = async (data) => {
|
|
16334
|
-
try {
|
|
16335
|
-
const event = data;
|
|
16336
|
-
if (!event.workflowRunId || !event.workflowRunStepId)
|
|
16337
|
-
return;
|
|
16338
|
-
await handleTaskFailure(event, event.failureReason ?? "Task failed", registry, {
|
|
16339
|
-
retryable: true
|
|
16340
|
-
});
|
|
16341
|
-
} catch (err) {
|
|
16342
|
-
console.error("[workflows] Handle task failure error:", err);
|
|
16343
|
-
}
|
|
16344
|
-
};
|
|
16345
|
-
eventBus.on("task.failed", onTaskFailed);
|
|
16346
|
-
const onTaskCancelled = async (data) => {
|
|
16347
|
-
try {
|
|
16348
|
-
const event = data;
|
|
16349
|
-
if (!event.workflowRunId || !event.workflowRunStepId)
|
|
16350
|
-
return;
|
|
16351
|
-
await handleTaskFailure(event, "Task was cancelled", registry, { retryable: false });
|
|
16352
|
-
} catch (err) {
|
|
16353
|
-
console.error("[workflows] Handle task cancellation error:", err);
|
|
16354
|
-
}
|
|
16355
|
-
};
|
|
16356
|
-
eventBus.on("task.cancelled", onTaskCancelled);
|
|
16357
|
-
const onApprovalResolved = async (data) => {
|
|
16358
|
-
try {
|
|
16359
|
-
const event = data;
|
|
16360
|
-
if (!event.workflowRunId || !event.workflowRunStepId)
|
|
16361
|
-
return;
|
|
16362
|
-
await resumeFromApprovalResolution(event, registry);
|
|
16363
|
-
} catch (err) {
|
|
16364
|
-
console.error("[workflows] Resume from approval resolution failed:", err);
|
|
16365
|
-
}
|
|
16366
|
-
};
|
|
16367
|
-
eventBus.on("approval.resolved", onApprovalResolved);
|
|
16368
|
-
return () => {
|
|
16369
|
-
eventBus.off("task.completed", onTaskCompleted);
|
|
16370
|
-
eventBus.off("task.failed", onTaskFailed);
|
|
16371
|
-
eventBus.off("task.cancelled", onTaskCancelled);
|
|
16372
|
-
eventBus.off("approval.resolved", onApprovalResolved);
|
|
16373
|
-
};
|
|
16374
|
-
}
|
|
16375
|
-
async function failClosedOnUnreadableReplay(runId, stepId, resume) {
|
|
16376
|
-
try {
|
|
16377
|
-
await resume();
|
|
16378
|
-
} catch (err) {
|
|
16379
|
-
if (!await failRunOnUnreadableReplay(runId, err, stepId))
|
|
16380
|
-
throw err;
|
|
16381
|
-
}
|
|
16382
|
-
}
|
|
16383
|
-
async function resumeFromTaskCompletion(event, registry) {
|
|
16384
|
-
await failClosedOnUnreadableReplay(event.workflowRunId, event.workflowRunStepId, () => resumeFromTaskCompletionUnguarded(event, registry));
|
|
16385
|
-
}
|
|
16386
|
-
async function resumeFromTaskCompletionUnguarded(event, registry) {
|
|
16387
|
-
const run = await getWorkflowRun(event.workflowRunId);
|
|
16388
|
-
if (!run || run.status !== "waiting" && run.status !== "running")
|
|
16389
|
-
return;
|
|
16390
|
-
const step = await getWorkflowRunStep(event.workflowRunStepId);
|
|
16391
|
-
if (!step || step.status !== "waiting")
|
|
16392
|
-
return;
|
|
16393
|
-
if (await isStaleTaskEvent(step.id, event))
|
|
16394
|
-
return;
|
|
16395
|
-
const workflow = await getWorkflow(run.workflowId);
|
|
16396
|
-
if (!workflow)
|
|
16397
|
-
return;
|
|
16398
|
-
const ctx = run.context ?? {};
|
|
16399
|
-
let taskOutput = event.output;
|
|
16400
|
-
if (event.output) {
|
|
16401
|
-
try {
|
|
16402
|
-
const parsed = JSON.parse(event.output);
|
|
16403
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
16404
|
-
taskOutput = parsed;
|
|
16405
|
-
}
|
|
16406
|
-
} catch {}
|
|
16407
|
-
}
|
|
16408
|
-
const stepOutput = { taskId: event.taskId, taskOutput };
|
|
16409
|
-
const routing = await completeTaskStepAndResolveSuccessors(workflow.definition, run.id, step, stepOutput, ctx);
|
|
16410
|
-
if (!routing.claimed)
|
|
16411
|
-
return;
|
|
16412
|
-
const successors = routing.successors;
|
|
16413
|
-
if (successors.length > 0) {
|
|
16414
|
-
const secretKeys = getSecretInputKeys(workflow.input);
|
|
16415
|
-
await walkGraph(workflow.definition, run.id, ctx, successors, registry, workflow.id, secretKeys);
|
|
16416
|
-
} else {
|
|
16417
|
-
await finalizeOrWait(run.id);
|
|
16418
|
-
}
|
|
16419
|
-
}
|
|
16420
|
-
async function finalizeOrWait(runId) {
|
|
16421
|
-
await getDbClient().transaction(async () => {
|
|
16422
|
-
const steps = await getWorkflowRunStepsByRunId(runId);
|
|
16423
|
-
if (hasRunningStep(steps)) {
|
|
16424
|
-
const run = await getWorkflowRun(runId);
|
|
16425
|
-
if (run?.status === "waiting")
|
|
16426
|
-
await updateWorkflowRun(runId, { status: "running" });
|
|
16427
|
-
return;
|
|
16428
|
-
}
|
|
16429
|
-
const hasWaiting = steps.some((s) => s.status === "waiting" || s.status === "failed" && s.nextRetryAt != null);
|
|
16430
|
-
if (hasWaiting) {
|
|
16431
|
-
await updateWorkflowRun(runId, { status: "waiting" });
|
|
16432
|
-
} else {
|
|
16433
|
-
await updateWorkflowRun(runId, {
|
|
16434
|
-
status: "completed",
|
|
16435
|
-
finishedAt: new Date().toISOString()
|
|
16436
|
-
});
|
|
16437
|
-
}
|
|
16438
|
-
});
|
|
16439
|
-
}
|
|
16440
|
-
async function handleTaskFailure(event, reason, registry, options) {
|
|
16441
|
-
await failClosedOnUnreadableReplay(event.workflowRunId, event.workflowRunStepId, () => handleTaskFailureUnguarded(event, reason, registry, options));
|
|
16442
|
-
}
|
|
16443
|
-
async function handleTaskFailureUnguarded(event, reason, registry, options) {
|
|
16444
|
-
const run = await getWorkflowRun(event.workflowRunId);
|
|
16445
|
-
if (!run || run.status !== "waiting" && run.status !== "running")
|
|
16446
|
-
return;
|
|
16447
|
-
const step = await getWorkflowRunStep(event.workflowRunStepId);
|
|
16448
|
-
if (!step || step.status !== "waiting")
|
|
16449
|
-
return;
|
|
16450
|
-
if (await isStaleTaskEvent(step.id, event))
|
|
16451
|
-
return;
|
|
16452
|
-
const workflow = await getWorkflow(run.workflowId);
|
|
16453
|
-
if (!workflow)
|
|
16454
|
-
return;
|
|
16455
|
-
if (options.retryable) {
|
|
16456
|
-
const retry = await scheduleTaskStepRetry(workflow.definition, run.id, step, event.taskId, reason);
|
|
16457
|
-
if (retry === "scheduled") {
|
|
16458
|
-
console.log(`[workflows] Task ${event.taskId} failed; step ${step.nodeId} of run ${run.id} queued for retry ${step.retryCount + 1}`);
|
|
16459
|
-
return;
|
|
16460
|
-
}
|
|
16461
|
-
if (retry === "not-claimed")
|
|
16462
|
-
return;
|
|
16463
|
-
}
|
|
16464
|
-
const onFailure = workflow.definition.onNodeFailure ?? "fail";
|
|
16465
|
-
if (onFailure === "fail") {
|
|
16466
|
-
await markRunFailed(event, reason);
|
|
16467
|
-
return;
|
|
16468
|
-
}
|
|
16469
|
-
const ctx = run.context ?? {};
|
|
16470
|
-
const stepOutput = {
|
|
16471
|
-
taskId: event.taskId,
|
|
16472
|
-
taskOutput: `${FAILED_TASK_OUTPUT_PREFIX} ${reason}] This node failed or was cancelled.`
|
|
16473
|
-
};
|
|
16474
|
-
const routing = await completeTaskStepAndResolveSuccessors(workflow.definition, run.id, step, stepOutput, ctx, reason);
|
|
16475
|
-
if (!routing.claimed)
|
|
16476
|
-
return;
|
|
16477
|
-
const successors = routing.successors;
|
|
16478
|
-
if (successors.length > 0) {
|
|
16479
|
-
const secretKeys = getSecretInputKeys(workflow.input);
|
|
16480
|
-
await walkGraph(workflow.definition, run.id, ctx, successors, registry, workflow.id, secretKeys);
|
|
16481
|
-
} else {
|
|
16482
|
-
await finalizeOrWait(run.id);
|
|
16483
|
-
}
|
|
16484
|
-
}
|
|
16485
|
-
async function isStaleTaskEvent(stepId, event) {
|
|
16486
|
-
if (!event.taskId)
|
|
16487
|
-
return false;
|
|
16488
|
-
const boundTask = await getTaskByWorkflowRunStepId(stepId);
|
|
16489
|
-
return boundTask != null && boundTask.id !== event.taskId;
|
|
16490
|
-
}
|
|
16491
|
-
async function markRunFailed(event, reason) {
|
|
16492
|
-
await failStepAndRunIfWaiting(event.workflowRunStepId, event.workflowRunId, reason);
|
|
16493
|
-
}
|
|
16494
|
-
async function retryFailedRun(runId, registry) {
|
|
16495
|
-
const run = await getWorkflowRun(runId);
|
|
16496
|
-
if (!run || run.status !== "failed")
|
|
16497
|
-
throw new Error("Run is not in failed state");
|
|
16498
|
-
const workflow = await getWorkflow(run.workflowId);
|
|
16499
|
-
if (!workflow)
|
|
16500
|
-
throw new Error("Workflow not found");
|
|
16501
|
-
const steps = await getWorkflowRunStepsByRunId(runId);
|
|
16502
|
-
const failedStep = steps.find((s) => s.status === "failed");
|
|
16503
|
-
if (!failedStep)
|
|
16504
|
-
throw new Error("No failed step found");
|
|
16505
|
-
const completedNodeIds = new Set(await getCompletedStepNodeIds(runId));
|
|
16506
|
-
const notReady = await findWorkflowReadinessProblems({ nodes: workflow.definition.nodes.filter((node) => !completedNodeIds.has(node.id)) }, registry);
|
|
16507
|
-
if (notReady.length > 0) {
|
|
16508
|
-
throw new Error(`Retry not started, run left failed: ${formatReadinessProblems(notReady)}`);
|
|
16509
|
-
}
|
|
16510
|
-
const { activeEdges } = await loadCompletedStepRouting(workflow.definition, runId, completedNodeIds);
|
|
16511
|
-
const foreachParent = resolveForeachParent(workflow.definition, failedStep.nodeId);
|
|
16512
|
-
const failedNode = foreachParent ?? workflow.definition.nodes.find((n) => n.id === failedStep.nodeId);
|
|
16513
|
-
if (!failedNode)
|
|
16514
|
-
throw new Error(`Node ${failedStep.nodeId} not found in workflow definition`);
|
|
16515
|
-
const hasStructuralPredecessor = workflow.definition.nodes.some((node) => node.next && getNextTargets(node.next).includes(failedNode.id));
|
|
16516
|
-
const hasActivePredecessor = [...activeEdges].some((edge) => edge.endsWith(`→${failedNode.id}`));
|
|
16517
|
-
const shouldRetryFailedNode = foreachParent != null || !hasStructuralPredecessor || hasActivePredecessor;
|
|
16518
|
-
const ctx = run.context ?? {};
|
|
16519
|
-
const claimed = await getDbClient().transaction(async () => {
|
|
16520
|
-
const current = await getWorkflowRun(runId);
|
|
16521
|
-
if (!current || current.status !== "failed")
|
|
16522
|
-
return false;
|
|
16523
|
-
await updateWorkflowRunStep(failedStep.id, {
|
|
16524
|
-
status: shouldRetryFailedNode ? "pending" : "cancelled",
|
|
16525
|
-
error: shouldRetryFailedNode ? null : "Skipped on retry because its branch was not active"
|
|
16526
|
-
});
|
|
16527
|
-
await updateWorkflowRun(runId, { status: "running", error: null, context: ctx });
|
|
16528
|
-
return true;
|
|
16529
|
-
});
|
|
16530
|
-
if (!claimed)
|
|
16531
|
-
throw new Error("Run is not in failed state");
|
|
16532
|
-
const liveNodeIds = new Set(steps.filter((s) => s.status === "running" || s.status === "waiting").map((s) => s.nodeId));
|
|
16533
|
-
const readyNodes = findReadyNodes(workflow.definition, completedNodeIds, activeEdges).filter((n) => n.id === failedNode.id || !liveNodeIds.has(n.id));
|
|
16534
|
-
const nodesToRun = !shouldRetryFailedNode || readyNodes.some((n) => n.id === failedNode.id) ? readyNodes : [failedNode, ...readyNodes];
|
|
16535
|
-
const secretKeys = getSecretInputKeys(workflow.input);
|
|
16536
|
-
await walkGraph(workflow.definition, runId, ctx, nodesToRun, registry, workflow.id, secretKeys);
|
|
16537
|
-
}
|
|
16538
|
-
async function cancelWorkflowRunRows(runId, cancelReason) {
|
|
16539
|
-
const terminalStatuses = ["completed", "failed", "cancelled", "skipped"];
|
|
16540
|
-
const now = new Date().toISOString();
|
|
16541
|
-
return await getDbClient().transaction(async () => {
|
|
16542
|
-
const current = await getWorkflowRun(runId);
|
|
16543
|
-
if (!current || terminalStatuses.includes(current.status))
|
|
16544
|
-
return false;
|
|
16545
|
-
const steps = await getWorkflowRunStepsByRunId(runId);
|
|
16546
|
-
for (const step of steps) {
|
|
16547
|
-
if (terminalStatuses.includes(step.status))
|
|
16548
|
-
continue;
|
|
16549
|
-
const task = await getTaskByWorkflowRunStepId(step.id);
|
|
16550
|
-
if (task) {
|
|
16551
|
-
await cancelTask(task.id, cancelReason);
|
|
16552
|
-
}
|
|
16553
|
-
await updateWorkflowRunStep(step.id, {
|
|
16554
|
-
status: "cancelled",
|
|
16555
|
-
error: cancelReason,
|
|
16556
|
-
finishedAt: now
|
|
16557
|
-
});
|
|
16558
|
-
}
|
|
16559
|
-
await cancelPendingApprovalRequestsForRun(runId, cancelReason);
|
|
16560
|
-
await updateWorkflowRun(runId, {
|
|
16561
|
-
status: "cancelled",
|
|
16562
|
-
error: cancelReason,
|
|
16563
|
-
finishedAt: now
|
|
16564
|
-
});
|
|
16565
|
-
return true;
|
|
16566
|
-
});
|
|
16567
|
-
}
|
|
16568
|
-
async function cancelWorkflowRun(runId, reason) {
|
|
16569
|
-
const run = await getWorkflowRun(runId);
|
|
16570
|
-
if (!run)
|
|
16571
|
-
throw new Error("Workflow run not found");
|
|
16572
|
-
const terminalStatuses = ["completed", "failed", "cancelled", "skipped"];
|
|
16573
|
-
if (terminalStatuses.includes(run.status) && run.status !== "cancelled") {
|
|
16574
|
-
throw new Error(`Cannot cancel run in '${run.status}' state`);
|
|
16575
|
-
}
|
|
16576
|
-
const cancelReason = reason ?? "Cancelled by user";
|
|
16577
|
-
const applied = await cancelWorkflowRunRows(runId, cancelReason);
|
|
16578
|
-
const cancelledApprovals = await listCancelledApprovalRequestsForRun(runId);
|
|
16579
|
-
const latestRun = await getWorkflowRun(runId);
|
|
16580
|
-
if (!applied && latestRun?.status !== "cancelled") {
|
|
16581
|
-
throw new Error(`Cannot cancel run in '${latestRun?.status ?? "missing"}' state`);
|
|
16582
|
-
}
|
|
16583
|
-
await postApprovalCancellationUpdates(cancelledApprovals, applied ? cancelReason : latestRun?.error ?? cancelReason);
|
|
16584
|
-
}
|
|
16585
|
-
async function resumeFromApprovalResolution(event, registry) {
|
|
16586
|
-
await failClosedOnUnreadableReplay(event.workflowRunId, event.workflowRunStepId, () => resumeFromApprovalResolutionUnguarded(event, registry));
|
|
16587
|
-
}
|
|
16588
|
-
async function resumeFromApprovalResolutionUnguarded(event, registry) {
|
|
16589
|
-
const run = await getWorkflowRun(event.workflowRunId);
|
|
16590
|
-
if (!run || run.status !== "waiting" && run.status !== "running")
|
|
16591
|
-
return;
|
|
16592
|
-
const step = await getWorkflowRunStep(event.workflowRunStepId);
|
|
16593
|
-
if (!step || step.status !== "waiting")
|
|
16594
|
-
return;
|
|
16595
|
-
const workflow = await getWorkflow(run.workflowId);
|
|
16596
|
-
if (!workflow)
|
|
16597
|
-
return;
|
|
16598
|
-
if (event.status === "cancelled") {
|
|
16599
|
-
console.warn(`[workflows] approval ${event.requestId} is cancelled; step ${event.workflowRunStepId} stays waiting for the run cancel path`);
|
|
16600
|
-
return;
|
|
16601
|
-
}
|
|
16602
|
-
const ctx = run.context ?? {};
|
|
16603
|
-
const { output: stepOutput, nextPort } = shapeApprovalResolution(registry, workflow.definition, step.nodeId, step.output, { requestId: event.requestId, status: event.status, responses: event.responses });
|
|
16604
|
-
const routing = await checkpointPortStepAndResolveSuccessors(workflow.definition, run.id, step.id, step.nodeId, stepOutput, nextPort, ctx);
|
|
16605
|
-
if (!routing.claimed)
|
|
16606
|
-
return;
|
|
16607
|
-
const successors = routing.successors;
|
|
16608
|
-
if (successors.length > 0) {
|
|
16609
|
-
const secretKeys = getSecretInputKeys(workflow.input);
|
|
16610
|
-
await walkGraph(workflow.definition, run.id, ctx, successors, registry, workflow.id, secretKeys);
|
|
16611
|
-
} else {
|
|
16612
|
-
await finalizeOrWait(run.id);
|
|
16613
|
-
}
|
|
16614
|
-
}
|
|
16615
|
-
async function resumeWaitState(waitId, status, payload, registry) {
|
|
16616
|
-
const cappedPayload = capPayload(payload);
|
|
16617
|
-
const result = await resolveWaitState(waitId, { status, firedPayload: cappedPayload });
|
|
16618
|
-
if (!result.updated || !result.row)
|
|
16619
|
-
return;
|
|
16620
|
-
const waitRow = result.row;
|
|
16621
|
-
await failClosedOnUnreadableReplay(waitRow.workflowRunId, waitRow.workflowRunStepId, () => resumeClaimedWait(waitRow, status, cappedPayload, registry));
|
|
16622
|
-
}
|
|
16623
|
-
async function resumeClaimedWait(waitRow, status, cappedPayload, registry) {
|
|
16624
|
-
const run = await getWorkflowRun(waitRow.workflowRunId);
|
|
16625
|
-
if (!run || run.status !== "waiting" && run.status !== "running")
|
|
16626
|
-
return;
|
|
16627
|
-
const step = await getWorkflowRunStep(waitRow.workflowRunStepId);
|
|
16628
|
-
if (!step || step.status !== "waiting")
|
|
16629
|
-
return;
|
|
16630
|
-
const workflow = await getWorkflow(run.workflowId);
|
|
16631
|
-
if (!workflow)
|
|
16632
|
-
return;
|
|
16633
|
-
const nextPort = computeNextPort(waitRow.mode, status);
|
|
16634
|
-
const ctx = run.context ?? {};
|
|
16635
|
-
const stepOutput = {
|
|
16636
|
-
waitId: waitRow.id,
|
|
16637
|
-
mode: waitRow.mode,
|
|
16638
|
-
firedAt: waitRow.resolvedAt,
|
|
16639
|
-
payload: cappedPayload === undefined ? undefined : cappedPayload
|
|
16640
|
-
};
|
|
16641
|
-
const routing = await checkpointPortStepAndResolveSuccessors(workflow.definition, run.id, step.id, step.nodeId, stepOutput, nextPort, ctx);
|
|
16642
|
-
if (!routing.claimed)
|
|
16643
|
-
return;
|
|
16644
|
-
if (waitRow.mode === "event" && waitRow.eventName) {
|
|
16645
|
-
pruneWaitFromBus(waitRow.id, waitRow.eventName);
|
|
16646
|
-
}
|
|
16647
|
-
const successors = routing.successors;
|
|
16648
|
-
if (successors.length > 0) {
|
|
16649
|
-
const secretKeys = getSecretInputKeys(workflow.input);
|
|
16650
|
-
await walkGraph(workflow.definition, run.id, ctx, successors, registry, workflow.id, secretKeys);
|
|
16651
|
-
} else {
|
|
16652
|
-
await finalizeOrWait(run.id);
|
|
16653
|
-
}
|
|
16654
|
-
}
|
|
16655
|
-
var FIRED_PAYLOAD_BYTE_CAP = 64 * 1024;
|
|
16656
|
-
function capPayload(payload) {
|
|
16657
|
-
if (payload === undefined || payload === null)
|
|
16658
|
-
return payload;
|
|
16659
|
-
let encoded;
|
|
16660
|
-
try {
|
|
16661
|
-
encoded = JSON.stringify(payload);
|
|
16662
|
-
} catch {
|
|
16663
|
-
return { truncated: true, reason: "non-serializable" };
|
|
16664
|
-
}
|
|
16665
|
-
if (encoded.length <= FIRED_PAYLOAD_BYTE_CAP) {
|
|
16666
|
-
return payload;
|
|
16667
|
-
}
|
|
16668
|
-
const summary = encoded.slice(0, 1024);
|
|
16669
|
-
return {
|
|
16670
|
-
truncated: true,
|
|
16671
|
-
originalSize: encoded.length,
|
|
16672
|
-
summary
|
|
16673
|
-
};
|
|
16674
|
-
}
|
|
16675
|
-
var waitsByEvent = new Map;
|
|
16676
|
-
var listenersByEvent = new Map;
|
|
16677
|
-
var busRegistry = null;
|
|
16678
|
-
async function initWaitBusSubscriptions(registry) {
|
|
16679
|
-
busRegistry = registry;
|
|
16680
|
-
const pendingNames = await collectPendingEventNames();
|
|
16681
|
-
for (const name of pendingNames) {
|
|
16682
|
-
const pending = await getPendingWaitsByEvent(name);
|
|
16683
|
-
for (const w of pending) {
|
|
16684
|
-
registerWait(w.id, name);
|
|
16685
|
-
}
|
|
16686
|
-
}
|
|
16687
|
-
}
|
|
16688
|
-
async function collectPendingEventNames() {
|
|
16689
|
-
return new Set(await getPendingEventWaitNames());
|
|
16690
|
-
}
|
|
16691
|
-
function subscribeWaitToBus(waitId, eventName) {
|
|
16692
|
-
registerWait(waitId, eventName);
|
|
16693
|
-
}
|
|
16694
|
-
function registerWait(waitId, eventName) {
|
|
16695
|
-
let set = waitsByEvent.get(eventName);
|
|
16696
|
-
if (!set) {
|
|
16697
|
-
set = new Set;
|
|
16698
|
-
waitsByEvent.set(eventName, set);
|
|
16699
|
-
}
|
|
16700
|
-
set.add(waitId);
|
|
16701
|
-
if (!listenersByEvent.has(eventName)) {
|
|
16702
|
-
const listener = (data) => {
|
|
16703
|
-
processBusEvent(eventName, data).catch((err) => {
|
|
16704
|
-
console.error(`[workflows] Wait bus listener failed for event=${eventName}:`, scrubSecrets(err instanceof Error ? err.message : String(err)));
|
|
16705
|
-
});
|
|
16706
|
-
};
|
|
16707
|
-
listenersByEvent.set(eventName, listener);
|
|
16708
|
-
workflowEventBus.on(eventName, listener);
|
|
16709
|
-
}
|
|
16710
|
-
}
|
|
16711
|
-
function pruneWaitFromBus(waitId, eventName) {
|
|
16712
|
-
const set = waitsByEvent.get(eventName);
|
|
16713
|
-
if (!set)
|
|
16714
|
-
return;
|
|
16715
|
-
set.delete(waitId);
|
|
16716
|
-
if (set.size === 0) {
|
|
16717
|
-
waitsByEvent.delete(eventName);
|
|
16718
|
-
const listener = listenersByEvent.get(eventName);
|
|
16719
|
-
if (listener) {
|
|
16720
|
-
workflowEventBus.off(eventName, listener);
|
|
16721
|
-
listenersByEvent.delete(eventName);
|
|
16722
|
-
}
|
|
16723
|
-
}
|
|
16724
|
-
}
|
|
16725
|
-
async function processBusEvent(eventName, payload) {
|
|
16726
|
-
const set = waitsByEvent.get(eventName);
|
|
16727
|
-
if (!set || set.size === 0)
|
|
16728
|
-
return;
|
|
16729
|
-
if (!busRegistry)
|
|
16730
|
-
return;
|
|
16731
|
-
const waitIds = [...set];
|
|
16732
|
-
for (const waitId of waitIds) {
|
|
16733
|
-
try {
|
|
16734
|
-
const row = await getWaitStateById(waitId);
|
|
16735
|
-
if (!row || row.status !== "pending") {
|
|
16736
|
-
set.delete(waitId);
|
|
16737
|
-
continue;
|
|
16738
|
-
}
|
|
16739
|
-
if (row.eventScope === "run") {
|
|
16740
|
-
if (!isPayloadInRun(payload, row.workflowRunId))
|
|
16741
|
-
continue;
|
|
16742
|
-
}
|
|
16743
|
-
const ok = await matchesFilter(payload, row.eventFilter ?? undefined);
|
|
16744
|
-
if (!ok)
|
|
16745
|
-
continue;
|
|
16746
|
-
await resumeWaitState(waitId, "fired", payload, busRegistry);
|
|
16747
|
-
} catch (err) {
|
|
16748
|
-
console.error(`[workflows] Wait bus listener failed for wait=${waitId} event=${eventName}:`, err);
|
|
16749
|
-
}
|
|
16750
|
-
}
|
|
16751
|
-
if (set.size === 0) {
|
|
16752
|
-
waitsByEvent.delete(eventName);
|
|
16753
|
-
const listener = listenersByEvent.get(eventName);
|
|
16754
|
-
if (listener) {
|
|
16755
|
-
workflowEventBus.off(eventName, listener);
|
|
16756
|
-
listenersByEvent.delete(eventName);
|
|
16757
|
-
}
|
|
16758
|
-
}
|
|
16759
|
-
}
|
|
16760
|
-
function isPayloadInRun(payload, runId) {
|
|
16761
|
-
if (typeof payload !== "object" || payload === null)
|
|
16762
|
-
return false;
|
|
16763
|
-
const rec = payload;
|
|
16764
|
-
return rec._runId === runId || rec.workflowRunId === runId;
|
|
16765
|
-
}
|
|
16766
|
-
|
|
16767
|
-
// src/workflows/recovery.ts
|
|
14422
|
+
init_workflow_runs();
|
|
16768
14423
|
async function recoverIncompleteRuns(registry) {
|
|
16769
14424
|
let recovered = 0;
|
|
16770
14425
|
recovered += await recoverRunningRuns(registry);
|
|
16771
14426
|
recovered += await recoverWaitingRuns(registry);
|
|
16772
14427
|
recovered += await recoverApprovalWaitingRuns(registry);
|
|
16773
14428
|
recovered += await recoverWaitStates(registry);
|
|
14429
|
+
for (const parentStepId of await getSettledChildRunParentSteps()) {
|
|
14430
|
+
try {
|
|
14431
|
+
if (await resumeFromChildRun(parentStepId, registry))
|
|
14432
|
+
recovered++;
|
|
14433
|
+
} catch (err) {
|
|
14434
|
+
console.error(`[workflows] Failed to resume sub-workflow step ${parentStepId}:`, err);
|
|
14435
|
+
}
|
|
14436
|
+
}
|
|
16774
14437
|
if (recovered > 0) {
|
|
16775
14438
|
console.log(`[workflows] Recovered ${recovered} incomplete run(s)`);
|
|
16776
14439
|
}
|
|
@@ -23053,7 +20716,7 @@ class Protocol {
|
|
|
23053
20716
|
};
|
|
23054
20717
|
}
|
|
23055
20718
|
}
|
|
23056
|
-
function
|
|
20719
|
+
function isPlainObject(value) {
|
|
23057
20720
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
23058
20721
|
}
|
|
23059
20722
|
function mergeCapabilities(base, additional) {
|
|
@@ -23064,7 +20727,7 @@ function mergeCapabilities(base, additional) {
|
|
|
23064
20727
|
if (addValue === undefined)
|
|
23065
20728
|
continue;
|
|
23066
20729
|
const baseValue = result[k];
|
|
23067
|
-
if (
|
|
20730
|
+
if (isPlainObject(baseValue) && isPlainObject(addValue)) {
|
|
23068
20731
|
result[k] = { ...baseValue, ...addValue };
|
|
23069
20732
|
} else {
|
|
23070
20733
|
result[k] = addValue;
|
|
@@ -29229,7 +26892,7 @@ async function notifySlack(text) {
|
|
|
29229
26892
|
return;
|
|
29230
26893
|
}
|
|
29231
26894
|
try {
|
|
29232
|
-
const { getSlackApp: getSlackApp2 } = await import("./app-
|
|
26895
|
+
const { getSlackApp: getSlackApp2 } = await import("./app-80w6qspx.js");
|
|
29233
26896
|
const app = getSlackApp2();
|
|
29234
26897
|
if (!app) {
|
|
29235
26898
|
console.warn("[Jira webhook keepalive] Slack not available, cannot send notification");
|
|
@@ -29300,7 +26963,7 @@ async function deleteJiraWebhook(webhookId) {
|
|
|
29300
26963
|
console.log(`[Jira webhook keepalive] Deleted webhook id=${webhookId}`);
|
|
29301
26964
|
}
|
|
29302
26965
|
async function overwriteWebhookIds(next) {
|
|
29303
|
-
const { getDbClient: getDbClient2 } = await import("./db-
|
|
26966
|
+
const { getDbClient: getDbClient2 } = await import("./db-wa6s1mwv.js");
|
|
29304
26967
|
await getDbClient2().transaction(async (tx) => {
|
|
29305
26968
|
const row = await tx.get("SELECT metadata FROM oauth_apps WHERE provider = 'jira'");
|
|
29306
26969
|
if (!row) {
|
|
@@ -103105,7 +100768,7 @@ async function reloadGlobalConfigsAndIntegrations() {
|
|
|
103105
100768
|
return runWithoutRequestAuth(() => reloadGlobalConfigsAndIntegrationsInner());
|
|
103106
100769
|
}
|
|
103107
100770
|
async function reloadGlobalConfigsAndIntegrationsInner() {
|
|
103108
|
-
const { getEmbeddingProvider: getEmbeddingProvider2, resetEmbeddingProvider } = await import("./index-
|
|
100771
|
+
const { getEmbeddingProvider: getEmbeddingProvider2, resetEmbeddingProvider } = await import("./index-ac0v8v9d.js");
|
|
103109
100772
|
const wasEmbeddingsConfigured = getEmbeddingProvider2().isConfigured();
|
|
103110
100773
|
const updated = await loadGlobalConfigsIntoEnv(true);
|
|
103111
100774
|
await stopSlackApp();
|
|
@@ -103118,10 +100781,10 @@ async function reloadGlobalConfigsAndIntegrationsInner() {
|
|
|
103118
100781
|
integrations.push("embeddings");
|
|
103119
100782
|
}
|
|
103120
100783
|
if (!wasEmbeddingsConfigured && isEmbeddingsConfigured) {
|
|
103121
|
-
import("./boot-reembed-
|
|
100784
|
+
import("./boot-reembed-feda8wn8.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
103122
100785
|
console.error("[config-reload] memory backfill failed (non-fatal):", err);
|
|
103123
100786
|
});
|
|
103124
|
-
import("./boot-reembed-
|
|
100787
|
+
import("./boot-reembed-3nc0aaed.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
103125
100788
|
console.error("[config-reload] script backfill failed (non-fatal):", err);
|
|
103126
100789
|
});
|
|
103127
100790
|
}
|
|
@@ -104239,7 +101902,7 @@ function setPreloadedTools(server, names) {
|
|
|
104239
101902
|
async function applyPreToolCall(name, args, requestInfo, inputSchema) {
|
|
104240
101903
|
if (requestInfo.callOrigin !== "mcp")
|
|
104241
101904
|
return { args };
|
|
104242
|
-
const dispatcher = await import("./dispatcher-
|
|
101905
|
+
const dispatcher = await import("./dispatcher-nhs2pkr8.js");
|
|
104243
101906
|
const result = await dispatcher.dispatchPre("pre.tool.call", { tool: name, args, requestInfo }, inputSchema ? {
|
|
104244
101907
|
validateModify: async (data) => {
|
|
104245
101908
|
const schema = normalizeObjectSchema(inputSchema) ?? inputSchema;
|
|
@@ -110728,7 +108391,7 @@ async function applyRating(events, ctx = {}) {
|
|
|
110728
108391
|
const accepted = [];
|
|
110729
108392
|
const rejected = [];
|
|
110730
108393
|
for (const event of events) {
|
|
110731
|
-
const reason =
|
|
108394
|
+
const reason = validate(event);
|
|
110732
108395
|
if (reason) {
|
|
110733
108396
|
rejected.push({ event, reason });
|
|
110734
108397
|
continue;
|
|
@@ -110835,7 +108498,7 @@ async function applyRating(events, ctx = {}) {
|
|
|
110835
108498
|
}
|
|
110836
108499
|
return { applied, rejected: [...rejected, ...lateRejects] };
|
|
110837
108500
|
}
|
|
110838
|
-
function
|
|
108501
|
+
function validate(event) {
|
|
110839
108502
|
if (!event.source || event.source.trim() === "") {
|
|
110840
108503
|
return "source is required";
|
|
110841
108504
|
}
|
|
@@ -127726,6 +125389,7 @@ async function downloadSlackFile(file, token, path3) {
|
|
|
127726
125389
|
}
|
|
127727
125390
|
}
|
|
127728
125391
|
async function createSlackTaskWithFiles(description, options, inbound) {
|
|
125392
|
+
noteSlackInboundSideEffect("task_created");
|
|
127729
125393
|
if (inbound.fetched.length === 0) {
|
|
127730
125394
|
const task2 = await createTaskWithSiblingAwareness(description, options, { origin: "slack" });
|
|
127731
125395
|
return { task: task2, unattached: inbound.failed };
|
|
@@ -134069,7 +131733,7 @@ async function runHandler(loaded, handler, event, payload) {
|
|
|
134069
131733
|
const startedAt = Date.now();
|
|
134070
131734
|
let timeout;
|
|
134071
131735
|
try {
|
|
134072
|
-
const
|
|
131736
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
134073
131737
|
timeout = setTimeout(() => {
|
|
134074
131738
|
controller.abort();
|
|
134075
131739
|
reject(new ExtensionTimeoutError);
|
|
@@ -134078,7 +131742,7 @@ async function runHandler(loaded, handler, event, payload) {
|
|
|
134078
131742
|
});
|
|
134079
131743
|
const result = await Promise.race([
|
|
134080
131744
|
Promise.resolve().then(() => handler.handler(payload, buildCtx(loaded, event, controller.signal))),
|
|
134081
|
-
|
|
131745
|
+
timeoutPromise
|
|
134082
131746
|
]);
|
|
134083
131747
|
const durationMs = Date.now() - startedAt;
|
|
134084
131748
|
return { ok: true, result, durationMs };
|
|
@@ -134141,8 +131805,8 @@ async function dispatchPre(event, payload, opts) {
|
|
|
134141
131805
|
console.error("[extensions] pre dispatch inside transaction:", scrubSecrets(String(event)));
|
|
134142
131806
|
return { action: "continue" };
|
|
134143
131807
|
}
|
|
134144
|
-
let currentPayload = payload;
|
|
134145
|
-
|
|
131808
|
+
let currentPayload = { ...payload };
|
|
131809
|
+
const mergedData = {};
|
|
134146
131810
|
let modified = false;
|
|
134147
131811
|
for (const { loaded, handler } of handlersFor(event, opts)) {
|
|
134148
131812
|
if (!isRegistered(loaded.record.id))
|
|
@@ -134191,7 +131855,7 @@ async function dispatchPre(event, payload, opts) {
|
|
|
134191
131855
|
throw validation.error;
|
|
134192
131856
|
data = definedObject(validation.data);
|
|
134193
131857
|
}
|
|
134194
|
-
mergedData
|
|
131858
|
+
Object.assign(mergedData, data);
|
|
134195
131859
|
currentPayload = mergePayload(event, currentPayload, data);
|
|
134196
131860
|
modified = true;
|
|
134197
131861
|
await markSuccess(loaded);
|
|
@@ -134687,7 +132351,7 @@ class NotifyExecutor extends BaseExecutor {
|
|
|
134687
132351
|
}
|
|
134688
132352
|
}
|
|
134689
132353
|
case "slack": {
|
|
134690
|
-
const { getSlackApp: getSlackApp2 } = await import("./app-
|
|
132354
|
+
const { getSlackApp: getSlackApp2 } = await import("./app-80w6qspx.js");
|
|
134691
132355
|
const app = getSlackApp2();
|
|
134692
132356
|
if (!app) {
|
|
134693
132357
|
return {
|
|
@@ -135455,6 +133119,7 @@ function createExecutorRegistry(deps) {
|
|
|
135455
133119
|
registry3.register(new ForeachExecutor(deps));
|
|
135456
133120
|
registry3.register(new HumanInTheLoopExecutor(deps));
|
|
135457
133121
|
registry3.register(new WaitExecutor(deps));
|
|
133122
|
+
registry3.register(new SubWorkflowExecutor(deps, registry3));
|
|
135458
133123
|
return registry3;
|
|
135459
133124
|
}
|
|
135460
133125
|
|
|
@@ -135495,4 +133160,4 @@ async function initWorkflows() {
|
|
|
135495
133160
|
logOpenWebhookTriggers();
|
|
135496
133161
|
}
|
|
135497
133162
|
|
|
135498
|
-
export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, findUserById, findUserByExternalId, getUserIdentities, listUserEvents, listUserTokens, recordIdentityEvent, findOrCreateUserByEmail, linkIdentity, unlinkIdentity, mintToken, revokeToken, resolveUserByToken, mintSessionToken, revokeSessionToken, fingerprintApiKey, resolveIdentity, renderIdentity, createAdditiveBuffer, listHarnessModelSupport, recordHarnessModelSupport, agentRanModel, getAgentHarnessCliVersion, setAgentHarnessCliVersion, sanitizeModelTierOverrides, parseModelTierOverridesHeader, setAgentModelTierOverrides, previewModelTiers, recordClaimModelResolution, harnessModelErrorFor, poolTaskRunsOnHarness, explicitModelError, explicitModelErrorForAgent, referencedBundlePaths, skillDirs, TaskCreationBlockedError, recordUnmappedIdentity, MAX_TASK_ATTACHMENT_BYTES, recordTaskAttachmentUpload, buildEffectiveText, bufferedFileFailures, fetchSlackFiles, createSlackTaskWithFiles, notifySlackFileFailures, hasOtherUserMention, broadcastMatches, routeMessage, extractTaskFromMessage, extractSlackMessageText,
|
|
133163
|
+
export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, findUserById, findUserByExternalId, getUserIdentities, listUserEvents, listUserTokens, recordIdentityEvent, findOrCreateUserByEmail, linkIdentity, unlinkIdentity, mintToken, revokeToken, resolveUserByToken, mintSessionToken, revokeSessionToken, fingerprintApiKey, resolveIdentity, renderIdentity, createAdditiveBuffer, listHarnessModelSupport, recordHarnessModelSupport, agentRanModel, getAgentHarnessCliVersion, setAgentHarnessCliVersion, sanitizeModelTierOverrides, parseModelTierOverridesHeader, setAgentModelTierOverrides, previewModelTiers, recordClaimModelResolution, harnessModelErrorFor, poolTaskRunsOnHarness, explicitModelError, explicitModelErrorForAgent, referencedBundlePaths, skillDirs, TaskCreationBlockedError, recordUnmappedIdentity, MAX_TASK_ATTACHMENT_BYTES, recordTaskAttachmentUpload, buildEffectiveText, bufferedFileFailures, fetchSlackFiles, createSlackTaskWithFiles, notifySlackFileFailures, hasOtherUserMention, broadcastMatches, routeMessage, extractTaskFromMessage, extractSlackMessageText, recoverIncompleteRuns, startRetryPoller, stopRetryPoller, validateTemplateVariables, instantiateTemplate, handleWebhookTrigger, logOpenWebhookTriggers, handleEventTrigger, handleScheduleTrigger, verifyHmacSignature, WebhookError, snapshotWorkflow, snapshotAndUpdateWorkflow, startWaitPoller, stopWaitPoller, RawLlmConfigSchema, RawLlmOutputSchema, executeRawLlm, getExecutorRegistry, initWorkflows, dispatchScheduleTarget, calculateNextRun, startScheduler, stopScheduler, runScheduleNow, parseSkillContent, ExtensionAssetConflictError, ExtensionOwnershipError, getExtensionFiles, getExtensionById, getExtensionByName, listExtensions, listExtensionVersions, updateExtensionMeta, listExtensionRuns, SDK_ALLOWLIST, mcpToolNameForSdkMethod, mcpOverflowAuthError, hasKvViewArgs, resolveKvView, toolErr, createToolRegistrar, warnIfCorsAllowsAnyOrigin, isOriginAllowedForCredentials, allowedCredentialRedirect, setCorsHeaders, parseQueryParams, getPathSegments, wireHttpSpanLifecycle, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, writeUnhandledError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, runtimeInstanceHeader, describeRequestRoute, route, handleMcpBridge, Redacted, SCRIPT_SDK_RESPONSE_LIMIT_BYTES, readScriptSdkJsonResponse, ExtensionConfigError, ExtensionLifecycleError, setExtensionLoopbackBaseUrl, enableExtension, disableExtension, activateVersion, ExtensionAssetsInvalidError, installExtensionWithAssets, uninstallExtension, reloadExtension, loadEnabledExtensions, stopExtensionRuntime, ExtensionAbortedError, isRegistered, isExtensionAgentId, getExtensionBridgeToken, resolveBridgeCallOrigin, extensionIdForAgent, runContext, registerLoaded, unregister, listRegistered, dispatchPre, dispatchPost, stripSiblingBlock, createTaskWithSiblingAwareness, isInboxAllowed, isSenderAllowed, handleMessageReceived, isAzureDevOpsEnabled, initAzureDevOps, verifyAzureDevOpsWebhook, isAzureDevOpsUrl, canonicalAzureDevOpsRepoUrl, parseAzureDevOpsRepoUrl, createdPullRequestOf, commentedPayloadOf, handlePullRequestCreated, handlePullRequestCommented, AGENT_MAX_TASKS_CONFIG_KEY, reconcileAgentMaxTasksPolicy, resetAgentMaxTasksMirror, upsertSwarmConfigWithPolicyMirror, runtimeStaleThresholdMinutes, upsertRuntimeInstance, getRuntimeInstanceById, touchRuntimeInstance, countActiveRuntimeInstancesForAgent, listRuntimeInstancesForAgent, setRuntimeCredentialReady, reconcileAgentStatusFromRuntimes, agentsWithLiveRuntime, expireStaleRuntimeInstances, enqueueAuditRow, enqueueAdmissionRow, flushAuditBuffer, startAuditWriter, stopAuditWriter, startAuditGc, stopAuditGc, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, isGitLabEnabled, initGitLab, verifyGitLabWebhook, handleMergeRequest, handleIssue2 as handleIssue1, handleNote, handlePipeline, repointTrackerSyncBySwarmId, getJiraMetadata, updateJiraMetadata, clearJiraMetadata, registerJiraWebhook, deleteJiraWebhook, isJiraEnabled, initJira, handleJiraWebhook, handleAgentSessionEvent, handleIssueUpdate, handleIssueDelete, handleAgentSessionPrompted, isLinearEnabled, initLinear, parseCookieHeader, extractAndVerifyCookie, issuePageSessionCookie, resolveHttpRequestAuth, loadGlobalConfigsIntoEnv, reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload, handleCore, userCtx, resolveTaskAuditUserId, resolveHttpAuditUserId, resolveHttpFavoriteOwner, handleApps, cancelApprovalRequest, cancelApprovalRequestAndRun, getUserCommsPrefs, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, executeReadOnlyQuery, assertSelectOnlyQuery, handleDbQuery, snapshotMetric, AssetKeyAuthorizationError, authorizeAssetKeyWrite, snapshotPage, assertOAuthAppUrlsSafe, assertOAuthEgressUrlSafe, listOAuthPresets, listOAuthPresetIds, getOAuthPreset, hydrateOAuthAppFromPreset, scopedResourceScopeIdSchema, resolveScopedResourceId, dedupeMemoryDocumentIds, recordMemoryAccesses, recordRetrievals, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, REBOOT_RETRY_PIN_TAG, getPinCandidateAgent, resolveLeadOnlyRecoveryAssignment, getResumeGeneration, getNextResumeGeneration, createWorkerTaskFollowUp, createResumeFollowUp, createRerouteDecisionTask, createPoolStarvationDecisionTask, guardTerminalTaskResultWrite, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, reservedNamespaceError, reservedRoomKeyError, updateMcpServerChecked, updateTouchesStdioExecution, MemoryKeyError, refreshLinks, canReadMemory, getLinksForMemory, expandCandidatesWithGraph, indexMemoryContent, isApiDraining, drainApi, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, changesAllowMerge, updateSwarmRepoChecked, getCombConfig, resolveRoomNamespace, roomRequestInfo, authorizeRoomNamespace, RoomOperationSchema, removeAwarenessStates, roomTopic, decodeRoomSnapshot, roomView, getRoom, changeRoom, applyRoomUpdate, resetRoom, sweepRooms, closeRooms, removeNamespaceRooms, MEMORY_KEYS_DEFAULT_PREFIX, MEMORY_KEYS_MAX_LIMIT, estimateTokens, posteriorMean, listMemoryKeys, checkChunkIntegrity, getMemoryChunks, getPosteriorsByIds, createEvent, createEventsBatch, getEventCountsFiltered, getEventsFiltered, mergeScheduleTiming, validateRecurringTiming, checkOutputSchemaShape, sendTaskOutputSchema, sendTaskHandler, steerTaskUserInputSchema, steerTaskOutputSchema, steerTaskHandler, canClaim, emitBudgetRefusalSideEffects, workflowModelErrors, patchWorkflowDefinition, resolveScriptsOnlyMode, ALL_CAPABILITIES, hasCapability, getEnabledCapabilities, createServer };
|