@desplega.ai/agent-swarm 2.5.2 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/{acp-adapter-csh0zz3b.js → acp-adapter-gr2fcyyj.js} +3 -3
  2. package/dist/{actions-6xx079d1.js → actions-wv766ghf.js} +18 -18
  3. package/dist/{amp-adapter-8n6f0hz8.js → amp-adapter-speb906t.js} +3 -3
  4. package/dist/{app-ft6r6v23.js → app-ag29n14f.js} +3 -3
  5. package/dist/{assistant-vt2cyfzd.js → assistant-3x8bz3eg.js} +19 -19
  6. package/dist/{boot-reembed-r3e808ky.js → boot-reembed-5b3d2eea.js} +1 -1
  7. package/dist/{boot-reembed-tgrfy27t.js → boot-reembed-jsmfrpzf.js} +1 -1
  8. package/dist/{boot-scrub-sweep-masdwxkm.js → boot-scrub-sweep-s73gerxy.js} +2 -2
  9. package/dist/{boot-seeds-0n82qc7q.js → boot-seeds-5zmax2wr.js} +4 -4
  10. package/dist/{claude-adapter-s7j47k06.js → claude-adapter-hswdnm3t.js} +3 -3
  11. package/dist/{claude-managed-adapter-gt6pa35d.js → claude-managed-adapter-cvbzj1h6.js} +2 -2
  12. package/dist/{claude-managed-setup-3hzrgrnk.js → claude-managed-setup-3k8m3hhc.js} +2 -2
  13. package/dist/{claude-sdk-session-0zrdzyx2.js → claude-sdk-session-fkbyk66x.js} +2 -2
  14. package/dist/{cli-d172svcw.js → cli-1fc85mvt.js} +4 -4
  15. package/dist/{cli-3e9vs93g.js → cli-210xd1f4.js} +4 -4
  16. package/dist/{cli-vaxw3bg0.js → cli-25j01dpj.js} +3 -3
  17. package/dist/{cli-n6kmqngw.js → cli-35gtk1p6.js} +1 -1
  18. package/dist/{cli-fgn7yg5r.js → cli-3ngdk5te.js} +1 -1
  19. package/dist/{cli-7ccf7akh.js → cli-40jsnrjm.js} +2 -2
  20. package/dist/{cli-b1tv22wr.js → cli-502eqv93.js} +1 -1
  21. package/dist/{cli-ydg6ep51.js → cli-6n8axjv1.js} +1 -1
  22. package/dist/{cli-f55b6ky0.js → cli-8s5wx3nz.js} +2 -2
  23. package/dist/{cli-86zpfje5.js → cli-99xgw26r.js} +1 -1
  24. package/dist/{cli-xw9q173h.js → cli-9s05422w.js} +1 -1
  25. package/dist/{cli-h5d22pfs.js → cli-aecdcr77.js} +119 -282
  26. package/dist/{cli-w9etnnxk.js → cli-bf8ma3f1.js} +120 -50
  27. package/dist/{cli-15xd0j7w.js → cli-ce0ak9ms.js} +11 -11
  28. package/dist/{cli-y3dzwyy3.js → cli-e2ar4bp1.js} +71 -20
  29. package/dist/{cli-b6tnjdvx.js → cli-e8jyq7d8.js} +1 -1
  30. package/dist/{cli-6y43v79h.js → cli-egjggehm.js} +1 -1
  31. package/dist/{cli-cf63dnxj.js → cli-ewm3pwbq.js} +2 -2
  32. package/dist/{cli-bpbcpd8b.js → cli-fsdprwrg.js} +1 -1
  33. package/dist/{cli-qrjv7t18.js → cli-fzcn3d87.js} +1 -1
  34. package/dist/{cli-14v1sxxy.js → cli-gpsz7g88.js} +6 -6
  35. package/dist/{cli-wadzapwb.js → cli-hcpj2xqz.js} +1 -1
  36. package/dist/{cli-jmcjbjkt.js → cli-hhxjnjzs.js} +1 -1
  37. package/dist/{cli-j3dwd4kz.js → cli-jjznk3f7.js} +718 -108
  38. package/dist/{cli-csh8fnbg.js → cli-kxawjrxg.js} +147 -28
  39. package/dist/{cli-mntxtkhn.js → cli-mz9afxxf.js} +2 -1
  40. package/dist/{cli-0vgy5wfw.js → cli-n45mr7zh.js} +2 -2
  41. package/dist/{cli-aaqnqpyw.js → cli-p0rzjkya.js} +2 -2
  42. package/dist/{cli-3qv8naq8.js → cli-p6zs9h4y.js} +827 -107
  43. package/dist/{cli-e91vshb1.js → cli-p9rafdrb.js} +4 -4
  44. package/dist/{cli-edtm741g.js → cli-qgf8emrn.js} +5 -5
  45. package/dist/{cli-ha22h0eg.js → cli-qzme5949.js} +26 -26
  46. package/dist/{cli-xzge46sj.js → cli-s49hpn37.js} +2 -2
  47. package/dist/{cli-b63d08rs.js → cli-s53vp709.js} +1 -1
  48. package/dist/{cli-cyfgxnvb.js → cli-svrj71pg.js} +18 -16
  49. package/dist/{cli-4qkg9968.js → cli-vwn81jbs.js} +3 -3
  50. package/dist/{cli-f3pfn4x3.js → cli-w1zwctj2.js} +1 -1
  51. package/dist/{cli-kcnwn85g.js → cli-y561h5a9.js} +4 -4
  52. package/dist/{cli-n2918sed.js → cli-z21whrrg.js} +15 -15
  53. package/dist/cli.js +16 -16
  54. package/dist/{codex-adapter-brh2tcxr.js → codex-adapter-n8ka66vf.js} +2 -2
  55. package/dist/{codex-session-runner-0dnj21qs.js → codex-session-runner-fsebts37.js} +1 -1
  56. package/dist/{commands-1cnvseqx.js → commands-t04rt2pc.js} +2 -2
  57. package/dist/{controller-kc7s9xhn.js → controller-rgna04r2.js} +2 -2
  58. package/dist/{controllers-connect-jzx7t4bn.js → controllers-connect-bqm1rtft.js} +20 -20
  59. package/dist/{cursor-adapter-qpwp5sfg.js → cursor-adapter-swr2k95y.js} +1 -1
  60. package/dist/{db-v5ytg5pw.js → db-24ck09xy.js} +1 -1
  61. package/dist/{db-4vq88jkc.js → db-a4m7haw0.js} +3 -3
  62. package/dist/{dispatcher-trtcrqae.js → dispatcher-nwf9ss0q.js} +17 -17
  63. package/dist/{dsh-adapter-f20xwn1z.js → dsh-adapter-kpdnhjst.js} +2 -2
  64. package/dist/{external-channel-n9bymc2n.js → external-channel-j1e2p34p.js} +2 -2
  65. package/dist/{grok-adapter-4kpmmnr6.js → grok-adapter-qtjz0whk.js} +4 -4
  66. package/dist/{handlers-ptsraqa8.js → handlers-19xr6dnz.js} +20 -20
  67. package/dist/{heartbeat-60qtxtjf.js → heartbeat-vtesda5d.js} +20 -20
  68. package/dist/{hook-rbktcstg.js → hook-8wqaszda.js} +1 -1
  69. package/dist/{hook-ngtvh729.js → hook-b2hhj6rd.js} +1 -1
  70. package/dist/{http-z8xy72gp.js → http-mxcqhhcs.js} +2757 -2448
  71. package/dist/{index-a9nsavhx.js → index-1r8wfpzs.js} +1 -1
  72. package/dist/{index-cbxkmfc5.js → index-9pn1pfkf.js} +1 -1
  73. package/dist/{index-s193j5fh.js → index-kw2mmat7.js} +20 -20
  74. package/dist/{index-06qkmcx6.js → index-n1vbvta9.js} +17 -9
  75. package/dist/{index-6srwvd5b.js → index-p4xfz37z.js} +1 -1
  76. package/dist/{index-1cscynkb.js → index-spwxjgay.js} +17 -17
  77. package/dist/{index-r70q0xjx.js → index-srg522m6.js} +17 -17
  78. package/dist/{keepalive-a9srcphj.js → keepalive-dsd8rtgx.js} +3 -3
  79. package/dist/{lead-3devneqy.js → lead-zyerk1x2.js} +30 -30
  80. package/dist/{maintenance-0er16h2y.js → maintenance-jqmqwnet.js} +1 -1
  81. package/dist/{oauth-refresh-sweep-b65jcyw2.js → oauth-refresh-sweep-q2c3bnvm.js} +2 -2
  82. package/dist/{onboard-wqywz5py.js → onboard-w4yd7s8t.js} +2 -2
  83. package/dist/{opencode-adapter-e56086m1.js → opencode-adapter-9tppvxar.js} +2 -2
  84. package/dist/{otel-impl-vk32s884.js → otel-impl-f36byzbd.js} +1 -1
  85. package/dist/{pricing-refresh-t27anv4z.js → pricing-refresh-xvak5p4v.js} +3 -3
  86. package/dist/{rbac-roles-bmcgsxjn.js → rbac-roles-682k2mt5.js} +1 -1
  87. package/dist/{rbac-roles-452665y0.js → rbac-roles-gfn30fb8.js} +2 -2
  88. package/dist/{render-v2-0ttsk2j4.js → render-v2-hgytz8nn.js} +3 -3
  89. package/dist/{resume-a26kvr41.js → resume-2dvq348g.js} +7 -5
  90. package/dist/{session-access-dgh9nkwk.js → session-access-7f21ekq9.js} +4 -4
  91. package/dist/{setup-9vc9qahm.js → setup-epffxtbk.js} +1 -1
  92. package/dist/{work-objects-vn8nkxwn.js → work-objects-xz1a5rqk.js} +20 -20
  93. package/dist/{worker-1x1h8vnm.js → worker-42t8emjt.js} +30 -30
  94. package/openapi.json +3045 -2456
  95. package/package.json +4 -4
  96. package/src/be/approval-config-question.ts +389 -0
  97. package/src/be/automation-preflight.ts +37 -3
  98. package/src/be/db/session-tree.ts +73 -15
  99. package/src/be/db/skill-search.ts +87 -0
  100. package/src/be/db/tasks/read.ts +29 -2
  101. package/src/be/db/workflow-runs.ts +54 -7
  102. package/src/be/db.ts +25 -5
  103. package/src/be/migrations/239_skills_fts.sql +40 -0
  104. package/src/be/migrations/240_workflow_run_parent_step_attempt.sql +37 -0
  105. package/src/be/migrations/241_skills_meta_fts.sql +39 -0
  106. package/src/be/swarm-config-guard.ts +23 -0
  107. package/src/extensions/catalog.generated.json +4 -4
  108. package/src/http/approval-requests.ts +209 -38
  109. package/src/http/config.ts +22 -37
  110. package/src/http/sessions.ts +27 -2
  111. package/src/integrations/chat-sdk/inbound.ts +5 -4
  112. package/src/providers/amp-adapter.ts +1 -1
  113. package/src/providers/dsh-adapter.ts +1 -1
  114. package/src/tests/approval-config-question.test.ts +702 -0
  115. package/src/tests/chat-sdk-inbound.test.ts +20 -0
  116. package/src/tests/dsh-adapter.test.ts +1 -1
  117. package/src/tests/entrypoint-gitlab-credential-helper.test.ts +273 -0
  118. package/src/tests/extensions-example-comms-adherence-gate.test.ts +321 -12
  119. package/src/tests/list-endpoint-slimming.test.ts +33 -0
  120. package/src/tests/migration-240-workflow-run-parent-step-attempt.test.ts +55 -0
  121. package/src/tests/script-connections.test.ts +6 -0
  122. package/src/tests/sessions-tree.test.ts +168 -1
  123. package/src/tests/skill-search-long-content.test.ts +193 -0
  124. package/src/tests/skill-search.test.ts +173 -22
  125. package/src/tests/workflow-sub-workflow.test.ts +538 -1
  126. package/src/tools/request-human-input.ts +27 -5
  127. package/src/types.ts +7 -0
  128. package/src/workflows/approval-notifications.ts +10 -3
  129. package/src/workflows/checkpoint.ts +69 -35
  130. package/src/workflows/engine.ts +234 -87
  131. package/src/workflows/executors/base.ts +6 -0
  132. package/src/workflows/executors/human-in-the-loop.ts +25 -1
  133. package/src/workflows/executors/sub-workflow.ts +45 -16
  134. package/src/workflows/recovery.ts +63 -1
  135. package/src/workflows/resume.ts +60 -21
  136. package/src/workflows/retry-poller.ts +25 -4
  137. package/templates/extensions/comms-adherence-gate/README.md +38 -13
  138. package/templates/extensions/comms-adherence-gate/hooks.ts +272 -69
  139. package/templates/extensions/comms-adherence-gate/manifest.json +2 -2
  140. package/templates/skills/turso-interaction/content.md +1 -1
  141. package/templates/skills/workflow-iterate/content.md +8 -1
  142. /package/dist/{cli-stxdrqs2.js → cli-avdvgcay.js} +0 -0
  143. /package/dist/{pi-mono-adapter-tzps8f38.js → pi-mono-adapter-tqsge1ay.js} +0 -0
@@ -4,9 +4,22 @@ import {
4
4
  scrubJsonValue,
5
5
  init_scrub_json,
6
6
  getDbClient2,
7
+ getAgentById2,
8
+ updateAgentStatus2,
9
+ updateAgentMaxTasks2,
10
+ getActiveTaskCount2,
7
11
  getTaskById2,
12
+ getChildWorkflowRunId,
13
+ isParentStepAttemptCurrent,
8
14
  createForeachChildSteps,
9
15
  init_workflow_runs,
16
+ isApiOnlyConfigKey,
17
+ isInternalConfigKey,
18
+ internalConfigKeyError,
19
+ isReservedConfigKey,
20
+ reservedKeyError,
21
+ validateConfigEntry,
22
+ init_swarm_config_guard,
10
23
  getOAuthApp,
11
24
  getOAuthTokens,
12
25
  init_oauth,
@@ -14,6 +27,7 @@ import {
14
27
  init_workflow_replay,
15
28
  createTaskExtended2,
16
29
  getSwarmConfigs2,
30
+ upsertSwarmConfig2,
17
31
  getWorkflow2,
18
32
  createWorkflowRun2,
19
33
  getWorkflowRun2,
@@ -37,23 +51,30 @@ import {
37
51
  releaseApprovalCancellationNotificationClaim2,
38
52
  updateApprovalRequestNotifications2,
39
53
  init_db3
40
- } from "./cli-csh8fnbg.js";
54
+ } from "./cli-kxawjrxg.js";
55
+ import {
56
+ isMultiRuntimeEnabled
57
+ } from "./cli-e8xyjvfc.js";
41
58
  import {
42
59
  getSlackApp2
43
- } from "./cli-15xd0j7w.js";
60
+ } from "./cli-ce0ak9ms.js";
44
61
  import {
45
62
  externalChannelToolError2,
46
63
  isBlockExternalEnabled2,
47
64
  getChannelBlockReason2
48
- } from "./cli-b63d08rs.js";
65
+ } from "./cli-s53vp709.js";
49
66
  import {
50
67
  DEFAULT_MODEL,
51
68
  resolveCredential
52
69
  } from "./cli-qqzmjxkf.js";
70
+ import {
71
+ revokeRuntimeTokensForRuntime,
72
+ init_users
73
+ } from "./cli-e8jyq7d8.js";
53
74
  import {
54
75
  TERMINAL_TASK_STATUSES,
55
76
  init_types
56
- } from "./cli-mntxtkhn.js";
77
+ } from "./cli-mz9afxxf.js";
57
78
  import {
58
79
  getSlackConfiguration,
59
80
  isSlackConfigured,
@@ -124,7 +145,7 @@ async function notifyAutomationPreflightFailure(preflight) {
124
145
  return;
125
146
  }
126
147
  try {
127
- const { getSlackApp } = await import("./app-ft6r6v23.js");
148
+ const { getSlackApp } = await import("./app-ag29n14f.js");
128
149
  const app = getSlackApp();
129
150
  if (!app) {
130
151
  console.warn("[Automation Preflight] Slack not available, cannot send notification");
@@ -362,6 +383,15 @@ function utcDayBounds(now) {
362
383
  const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
363
384
  return { start: start.toISOString(), end: new Date(start.getTime() + 86400000).toISOString() };
364
385
  }
386
+ async function isFirstWorkflowPreflightFailureToday(workflowId, runId, now = new Date) {
387
+ const bounds = utcDayBounds(now);
388
+ const row = await getDbClient2().get(`SELECT 1 AS found FROM workflow_runs
389
+ WHERE workflowId = ? AND id != ? AND status = 'failed'
390
+ AND error LIKE 'needs_setup:%'
391
+ AND startedAt >= ? AND startedAt < ?
392
+ LIMIT 1`, [workflowId, runId, bounds.start, bounds.end]);
393
+ return row == null;
394
+ }
365
395
  async function recordSchedulePreflightFailure(scheduleId, failureReason, now = new Date) {
366
396
  const message = scrubSecrets(failureReason);
367
397
  return await getDbClient2().transaction(async (tx) => {
@@ -382,7 +412,7 @@ async function recordWorkflowPreflightFailure(input) {
382
412
  const bounds = utcDayBounds(now);
383
413
  return await getDbClient2().transaction(async (tx) => {
384
414
  const existing = await tx.get(`SELECT id FROM workflow_runs
385
- WHERE workflowId = ? AND status = 'failed'
415
+ WHERE workflowId = ? AND status = 'failed' AND parentStepId IS NULL
386
416
  AND error LIKE 'needs_setup:%'
387
417
  AND startedAt >= ? AND startedAt < ?
388
418
  ORDER BY startedAt ASC LIMIT 1`, [input.workflowId, bounds.start, bounds.end]);
@@ -404,7 +434,10 @@ async function recordWorkflowPreflightFailure(input) {
404
434
  timestamp,
405
435
  timestamp
406
436
  ]);
407
- return { runId, recorded: true };
437
+ return {
438
+ runId,
439
+ recorded: await isFirstWorkflowPreflightFailureToday(input.workflowId, runId, now)
440
+ };
408
441
  });
409
442
  }
410
443
 
@@ -457,6 +490,470 @@ class BaseExecutor {
457
490
 
458
491
  // src/workflows/executors/human-in-the-loop.ts
459
492
  init_zod();
493
+
494
+ // src/be/approval-config-question.ts
495
+ init_zod();
496
+ init_db3();
497
+
498
+ // src/be/multi-runtime.ts
499
+ init_secret_scrubber();
500
+ init_db3();
501
+ init_users();
502
+ var AGENT_MAX_TASKS_CONFIG_KEY = "AGENT_MAX_TASKS";
503
+ function parseAgentMaxTasksValue(raw) {
504
+ const str = raw.trim();
505
+ if (!/^\d+$/.test(str))
506
+ return null;
507
+ const value = Number(str);
508
+ return value >= 1 && value <= 100 ? value : null;
509
+ }
510
+ function defaultMaxTasksForAgent(agent) {
511
+ return agent.isLead ? 2 : 1;
512
+ }
513
+ async function getAgentMaxTasksConfig(agentId) {
514
+ return (await getSwarmConfigs2({ scope: "agent", scopeId: agentId, key: AGENT_MAX_TASKS_CONFIG_KEY }))[0] ?? null;
515
+ }
516
+ async function reconcileAgentMaxTasksPolicy(agentId) {
517
+ const agent = await getAgentById2(agentId);
518
+ if (!agent)
519
+ return;
520
+ const existing = await getAgentMaxTasksConfig(agentId);
521
+ if (existing) {
522
+ const authoritative = parseAgentMaxTasksValue(existing.value);
523
+ if (authoritative !== null && authoritative !== (agent.maxTasks ?? 1)) {
524
+ await updateAgentMaxTasks2(agentId, authoritative);
525
+ }
526
+ return;
527
+ }
528
+ await upsertSwarmConfig2({
529
+ scope: "agent",
530
+ scopeId: agentId,
531
+ key: AGENT_MAX_TASKS_CONFIG_KEY,
532
+ value: String(agent.maxTasks ?? defaultMaxTasksForAgent(agent)),
533
+ description: "Logical agent max-tasks policy (seeded from agents.maxTasks)"
534
+ });
535
+ }
536
+ async function resetAgentMaxTasksMirror(agentId) {
537
+ const agent = await getAgentById2(agentId);
538
+ if (!agent)
539
+ return;
540
+ await updateAgentMaxTasks2(agentId, defaultMaxTasksForAgent(agent));
541
+ }
542
+ async function upsertSwarmConfigWithPolicyMirror(data) {
543
+ const scopeId = data.scope === "agent" ? data.scopeId : null;
544
+ if (!scopeId || data.key !== AGENT_MAX_TASKS_CONFIG_KEY) {
545
+ return upsertSwarmConfig2(data);
546
+ }
547
+ return await getDbClient2().transaction(async () => {
548
+ const row = await upsertSwarmConfig2(data);
549
+ const value = parseAgentMaxTasksValue(data.value);
550
+ if (value !== null)
551
+ await updateAgentMaxTasks2(scopeId, value);
552
+ return row;
553
+ });
554
+ }
555
+ function rowToRuntimeInstance(row) {
556
+ let metadata = null;
557
+ if (row.metadata) {
558
+ try {
559
+ metadata = JSON.parse(row.metadata);
560
+ } catch {
561
+ metadata = null;
562
+ }
563
+ }
564
+ return {
565
+ id: row.id,
566
+ agentId: row.agent_id,
567
+ poolId: row.pool_id ?? null,
568
+ controllerId: row.controller_id ?? null,
569
+ externalHandle: row.external_handle ? JSON.parse(row.external_handle) : null,
570
+ provisionAttempts: row.provision_attempts ?? 0,
571
+ provisionError: row.provision_error ?? null,
572
+ provisionRequestedAt: row.provision_requested_at ?? null,
573
+ draining: row.draining === 1,
574
+ idleSince: row.idle_since ?? null,
575
+ leaseExpiresAt: row.lease_expires_at ?? null,
576
+ taskId: row.task_id ?? null,
577
+ status: row.status,
578
+ reportedSlots: row.reported_slots,
579
+ credentialReady: row.credential_ready === null ? null : row.credential_ready === 1,
580
+ metadata,
581
+ lastSeenAt: row.last_seen_at,
582
+ createdAt: row.created_at,
583
+ updatedAt: row.updated_at
584
+ };
585
+ }
586
+ function runtimeStaleThresholdMinutes() {
587
+ return Number(process.env.RUNTIME_STALE_THRESHOLD_MIN) || 5;
588
+ }
589
+ function runtimeLivenessCutoff() {
590
+ return new Date(Date.now() - runtimeStaleThresholdMinutes() * 60 * 1000).toISOString();
591
+ }
592
+ async function upsertRuntimeInstance(instance) {
593
+ const metadataJson = instance.metadata ? JSON.stringify(instance.metadata) : null;
594
+ const row = await getDbClient2().transaction(async (tx) => {
595
+ const provisioning = await tx.get("SELECT id FROM runtime_instances WHERE id = ? AND status = 'provisioning' AND agent_id = ?", [instance.id, instance.agentId]);
596
+ const row = await tx.get(`INSERT INTO runtime_instances (id, agent_id, status, reported_slots, metadata)
597
+ VALUES (?, ?, 'active', ?, ?)
598
+ ON CONFLICT(id) DO UPDATE SET
599
+ status = 'active',
600
+ reported_slots = excluded.reported_slots,
601
+ metadata = COALESCE(excluded.metadata, runtime_instances.metadata),
602
+ last_seen_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
603
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
604
+ WHERE runtime_instances.agent_id = excluded.agent_id
605
+ RETURNING *`, [instance.id, instance.agentId, instance.reportedSlots, metadataJson]);
606
+ if (row?.pool_id && provisioning) {
607
+ await tx.run("UPDATE agents SET runtime_provision_attempts = 0, runtime_provision_retry_at = NULL WHERE id = ?", [row.agent_id]);
608
+ await tx.run("UPDATE runtime_pools SET consecutive_failures = 0, circuit_open_until = NULL WHERE id = ?", [row.pool_id]);
609
+ }
610
+ return row;
611
+ });
612
+ return row ? rowToRuntimeInstance(row) : null;
613
+ }
614
+ async function getRuntimeInstanceById(id) {
615
+ const row = await getDbClient2().get("SELECT * FROM runtime_instances WHERE id = ?", [id]);
616
+ return row ? rowToRuntimeInstance(row) : null;
617
+ }
618
+ async function insertProvisioningRuntime(instance) {
619
+ const row = await getDbClient2().get(`INSERT INTO runtime_instances (id, agent_id, pool_id, controller_id, status,
620
+ reported_slots, provision_requested_at)
621
+ VALUES (?, ?, ?, ?, 'provisioning', ?, ?) RETURNING *`, [
622
+ instance.id,
623
+ instance.agentId,
624
+ instance.poolId,
625
+ instance.controllerId,
626
+ instance.reportedSlots ?? 1,
627
+ new Date().toISOString()
628
+ ]);
629
+ return rowToRuntimeInstance(row);
630
+ }
631
+ async function markRuntimeTerminated(id, reason) {
632
+ await getDbClient2().transaction(async () => {
633
+ const runtime = await getRuntimeInstanceById(id);
634
+ if (!runtime)
635
+ return;
636
+ await markRuntimeInstanceOffline(id, runtime.agentId);
637
+ if (runtime.status === "provisioning") {
638
+ await getDbClient2().run("UPDATE runtime_instances SET provision_error = ? WHERE id = ?", [
639
+ scrubSecrets(reason),
640
+ id
641
+ ]);
642
+ }
643
+ await reconcileAgentStatusFromRuntimes(runtime.agentId);
644
+ await revokeRuntimeTokensForRuntime(id);
645
+ });
646
+ }
647
+ async function touchRuntimeInstance(id, agentId) {
648
+ const result = await getDbClient2().run(`UPDATE runtime_instances
649
+ SET last_seen_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
650
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
651
+ WHERE id = ? AND agent_id = ? AND status = 'active' AND last_seen_at >= ?`, [id, agentId, runtimeLivenessCutoff()]);
652
+ return result.changes > 0;
653
+ }
654
+ async function markRuntimeInstanceOffline(id, agentId) {
655
+ const row = await getDbClient2().get(`UPDATE runtime_instances
656
+ SET status = 'offline', updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
657
+ WHERE id = ? AND agent_id = ? RETURNING *`, [id, agentId]);
658
+ return row ? rowToRuntimeInstance(row) : null;
659
+ }
660
+ async function countActiveRuntimeInstancesForAgent(agentId) {
661
+ const result = await getDbClient2().get(`SELECT COUNT(*) as count FROM runtime_instances
662
+ WHERE agent_id = ? AND status = 'active' AND last_seen_at >= ?`, [agentId, runtimeLivenessCutoff()]);
663
+ return result?.count ?? 0;
664
+ }
665
+ async function listRuntimeInstancesForAgent(agentId) {
666
+ const rows = await getDbClient2().query(`SELECT *, (status = 'active' AND last_seen_at >= ?) AS is_live
667
+ FROM runtime_instances
668
+ WHERE agent_id = ?
669
+ ORDER BY created_at ASC, id ASC`, [runtimeLivenessCutoff(), agentId]);
670
+ return rows.map((row) => ({ ...rowToRuntimeInstance(row), isLive: row.is_live === 1 }));
671
+ }
672
+ async function setRuntimeCredentialReady(id, agentId, ready) {
673
+ const result = await getDbClient2().run(`UPDATE runtime_instances
674
+ SET credential_ready = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
675
+ WHERE id = ? AND agent_id = ? AND status = 'active' AND last_seen_at >= ?`, [ready ? 1 : 0, id, agentId, runtimeLivenessCutoff()]);
676
+ return result.changes > 0;
677
+ }
678
+ async function hasReadyLiveRuntime(agentId) {
679
+ const row = await getDbClient2().get(`SELECT COUNT(*) c FROM runtime_instances
680
+ WHERE agent_id = ? AND status = 'active' AND last_seen_at >= ?
681
+ AND (credential_ready IS NULL OR credential_ready = 1)`, [agentId, runtimeLivenessCutoff()]);
682
+ return (row?.c ?? 0) > 0;
683
+ }
684
+ async function reconcileAgentStatusFromRuntimes(agentId) {
685
+ await getDbClient2().transaction(async () => {
686
+ const agent = await getAgentById2(agentId);
687
+ if (!agent)
688
+ return;
689
+ if (await countActiveRuntimeInstancesForAgent(agentId) === 0) {
690
+ if (agent.status !== "offline")
691
+ await updateAgentStatus2(agentId, "offline");
692
+ return;
693
+ }
694
+ if (!await hasReadyLiveRuntime(agentId)) {
695
+ if (agent.status !== "waiting_for_credentials") {
696
+ await updateAgentStatus2(agentId, "waiting_for_credentials");
697
+ }
698
+ return;
699
+ }
700
+ const next = await getActiveTaskCount2(agentId) > 0 ? "busy" : "idle";
701
+ if (agent.status !== next)
702
+ await updateAgentStatus2(agentId, next);
703
+ });
704
+ }
705
+ async function agentsWithLiveRuntime() {
706
+ const rows = await getDbClient2().query(`SELECT DISTINCT agent_id FROM runtime_instances
707
+ WHERE status = 'active' AND last_seen_at >= ?`, [runtimeLivenessCutoff()]);
708
+ return new Set(rows.map((r) => r.agent_id));
709
+ }
710
+ async function expireStaleRuntimeInstances() {
711
+ if (!isMultiRuntimeEnabled()) {
712
+ return { expired: 0, agentsOffline: 0, pruned: 0 };
713
+ }
714
+ return await getDbClient2().transaction(async (tx) => {
715
+ const cutoff = runtimeLivenessCutoff();
716
+ const stale = await tx.query(`SELECT id, agent_id FROM runtime_instances
717
+ WHERE status = 'active' AND last_seen_at < ? AND pool_id IS NULL`, [cutoff]);
718
+ const pruned = (await tx.run("DELETE FROM runtime_instances WHERE last_seen_at < ? AND status != 'provisioning' AND (pool_id IS NULL OR status = 'offline')", [cutoff])).changes;
719
+ let agentsOffline = 0;
720
+ for (const agentId of new Set(stale.map((r) => r.agent_id))) {
721
+ await reconcileAgentStatusFromRuntimes(agentId);
722
+ if ((await getAgentById2(agentId))?.status === "offline")
723
+ agentsOffline++;
724
+ }
725
+ return { expired: stale.length, agentsOffline, pruned };
726
+ });
727
+ }
728
+ async function listRuntimeInstancesForPool(poolId) {
729
+ return (await getDbClient2().query("SELECT * FROM runtime_instances WHERE pool_id = ? ORDER BY created_at", [poolId])).map(rowToRuntimeInstance);
730
+ }
731
+
732
+ // src/be/approval-config-question.ts
733
+ init_swarm_config_guard();
734
+ var ConfigQuestionSettingsSchema = object2({
735
+ key: string2().min(1).max(255).regex(/^\S+$/, "must not contain whitespace").describe("The swarm config key to set, e.g. BITBUCKET_TOKEN"),
736
+ scope: _enum2(["global", "agent", "repo"]).describe("Config scope the value is written to"),
737
+ scopeId: string2().min(1).optional().describe("Agent or repo id for agent/repo scope. From request-human-input, agent scope defaults to the calling agent; pass it explicitly when asking on behalf of another agent."),
738
+ isSecret: boolean().optional().describe("Store encrypted and never show the value (default true). An existing secret row always stays secret."),
739
+ helpUrl: string2().url().refine((url) => /^https?:\/\//i.test(url), "must be an http(s) URL").optional().describe("Link shown next to the field, e.g. where to create the token"),
740
+ options: array2(object2({
741
+ value: string2(),
742
+ label: string2(),
743
+ description: string2().optional()
744
+ })).optional().describe("Allowed values for a non-secret key, rendered as a choice"),
745
+ defaultValue: string2().optional().describe("Prefilled value for a non-secret key")
746
+ });
747
+ var ConfigAnswerSchema = discriminatedUnion("action", [
748
+ object2({ action: literal2("set"), value: string2() }),
749
+ object2({ action: literal2("keep") }),
750
+ object2({ action: literal2("skip") })
751
+ ]);
752
+ var ConfigResultSchema = object2({
753
+ key: string2(),
754
+ scope: _enum2(["global", "agent", "repo"]),
755
+ scopeId: string2().nullable(),
756
+ isSecret: boolean(),
757
+ status: _enum2(["set", "kept", "skipped"]),
758
+ updatedAt: string2().nullable(),
759
+ value: string2().optional()
760
+ });
761
+ var ConfigStateSchema = object2({
762
+ exists: boolean(),
763
+ isSecret: boolean(),
764
+ updatedAt: string2().nullable(),
765
+ value: string2().optional().describe("Current value; present only for non-secret rows")
766
+ });
767
+ async function readConfigRow(settings) {
768
+ const scopeId = settings.scope === "global" ? null : settings.scopeId ?? null;
769
+ const row = await getDbClient2().get(`SELECT isSecret, lastUpdatedAt, envPath, description,
770
+ CASE WHEN isSecret = 1 THEN NULL ELSE value END AS plainValue
771
+ FROM swarm_config
772
+ WHERE scope = ? AND ${scopeId === null ? "scopeId IS NULL" : "scopeId = ?"} AND key = ?`, scopeId === null ? [settings.scope, settings.key] : [settings.scope, scopeId, settings.key]);
773
+ if (!row)
774
+ return null;
775
+ const isSecret = row.isSecret === 1 || isApiOnlyConfigKey(settings.key);
776
+ return {
777
+ isSecret,
778
+ updatedAt: row.lastUpdatedAt,
779
+ envPath: row.envPath,
780
+ description: row.description,
781
+ ...isSecret || row.plainValue === null ? {} : { value: row.plainValue }
782
+ };
783
+ }
784
+ async function readConfigState(settings) {
785
+ const row = await readConfigRow(settings);
786
+ if (!row)
787
+ return { exists: false, isSecret: settings.isSecret !== false, updatedAt: null };
788
+ const isSecret = row.isSecret || settings.isSecret !== false;
789
+ return {
790
+ exists: true,
791
+ isSecret,
792
+ updatedAt: row.updatedAt,
793
+ ...!isSecret && row.value !== undefined ? { value: row.value } : {}
794
+ };
795
+ }
796
+ async function normalizeConfigQuestion(question, ctx) {
797
+ const fail = (reason) => ({
798
+ ok: false,
799
+ error: `config question "${question.id}": ${reason}`
800
+ });
801
+ const parsed = ConfigQuestionSettingsSchema.safeParse(question.config);
802
+ if (!parsed.success) {
803
+ const issue = parsed.error.issues[0];
804
+ const path = issue?.path.length ? `config.${issue.path.map(String).join(".")}` : "config";
805
+ return fail(`${path}: ${issue?.message ?? "invalid"}`);
806
+ }
807
+ if (ctx.approvers.policy !== "any") {
808
+ return fail('a config question needs approvers.policy "any" (a config value has one author)');
809
+ }
810
+ const settings = { ...parsed.data };
811
+ if (settings.scope === "agent" && !settings.scopeId && ctx.callerAgentId) {
812
+ settings.scopeId = ctx.callerAgentId;
813
+ }
814
+ if (settings.scope === "global" && settings.scopeId) {
815
+ return fail("global scope must not have scopeId");
816
+ }
817
+ if (settings.scope !== "global" && !settings.scopeId) {
818
+ return fail(`${settings.scope} scope requires scopeId`);
819
+ }
820
+ if (isReservedConfigKey(settings.key))
821
+ return fail(reservedKeyError(settings.key).message);
822
+ if (isInternalConfigKey(settings.key))
823
+ return fail(internalConfigKeyError(settings.key).message);
824
+ const row = await readConfigRow(settings);
825
+ const isSecret = settings.isSecret !== false || row?.isSecret === true || isApiOnlyConfigKey(settings.key);
826
+ if (isSecret && (settings.options || settings.defaultValue !== undefined)) {
827
+ return fail("options and defaultValue are only allowed when isSecret is false (and the key is not already stored as a secret)");
828
+ }
829
+ if (settings.options?.length === 0)
830
+ return fail("options must not be empty");
831
+ if (settings.options && settings.defaultValue !== undefined && !settings.options.some((option) => option.value === settings.defaultValue)) {
832
+ return fail("defaultValue must be one of options");
833
+ }
834
+ const label = question.label?.trim() ? question.label : settings.key;
835
+ return {
836
+ ok: true,
837
+ question: {
838
+ ...question,
839
+ label,
840
+ required: question.required ?? true,
841
+ config: { ...settings, isSecret }
842
+ }
843
+ };
844
+ }
845
+ async function normalizeApprovalQuestions(questions, ctx) {
846
+ const out = [];
847
+ for (const question of questions) {
848
+ if (question.type !== "config") {
849
+ if (question.config !== undefined) {
850
+ return {
851
+ ok: false,
852
+ error: `question "${question.id}": the config block is only valid on type "config"`
853
+ };
854
+ }
855
+ if (typeof question.label !== "string") {
856
+ return { ok: false, error: `question "${question.id}": label is required` };
857
+ }
858
+ out.push({ ...question, label: question.label });
859
+ continue;
860
+ }
861
+ const normalized = await normalizeConfigQuestion(question, ctx);
862
+ if (!normalized.ok)
863
+ return normalized;
864
+ out.push(normalized.question);
865
+ }
866
+ return { ok: true, questions: out };
867
+ }
868
+ function isConfigQuestion(question) {
869
+ return question.type === "config" && typeof question.config === "object" && !!question.config;
870
+ }
871
+ function parseConfigAnswer(question, raw, state) {
872
+ const fail = (reason) => ({
873
+ ok: false,
874
+ error: `config question "${question.id}": ${reason}`
875
+ });
876
+ const required = question.required !== false;
877
+ if (raw === undefined || raw === null) {
878
+ return required ? fail("an answer is required") : { ok: true, answer: { action: "skip" } };
879
+ }
880
+ const parsed = ConfigAnswerSchema.safeParse(raw);
881
+ if (!parsed.success) {
882
+ return fail('answer must be { action: "set", value } , { action: "keep" } or { action: "skip" }');
883
+ }
884
+ const answer = parsed.data;
885
+ if (answer.action === "keep") {
886
+ return state.exists ? { ok: true, answer } : fail("cannot keep a key that is not set");
887
+ }
888
+ if (answer.action === "skip") {
889
+ return required ? fail("a required question cannot be skipped") : { ok: true, answer };
890
+ }
891
+ if (answer.value.trim().length === 0)
892
+ return fail("value must not be empty");
893
+ const { config } = question;
894
+ const isSecret = config.isSecret || state.isSecret;
895
+ if (!isSecret && config.options && !config.options.some((o) => o.value === answer.value)) {
896
+ return fail("value must be one of options");
897
+ }
898
+ const error = validateConfigEntry({
899
+ scope: config.scope,
900
+ scopeId: config.scopeId ?? null,
901
+ key: config.key,
902
+ value: answer.value
903
+ });
904
+ if (error)
905
+ return fail(isSecret ? `value rejected by ${config.key} validation` : error);
906
+ return { ok: true, answer };
907
+ }
908
+ function result(question, status, isSecret, updatedAt, value) {
909
+ const { key, scope, scopeId } = question.config;
910
+ return {
911
+ key,
912
+ scope,
913
+ scopeId: scope === "global" ? null : scopeId ?? null,
914
+ isSecret,
915
+ status,
916
+ updatedAt,
917
+ ...!isSecret && value !== undefined ? { value } : {}
918
+ };
919
+ }
920
+ function skippedConfigResult(question) {
921
+ return result(question, "skipped", question.config.isSecret, null);
922
+ }
923
+ async function keptConfigResult(question) {
924
+ const row = await readConfigRow(question.config);
925
+ if (!row)
926
+ return null;
927
+ const isSecret = question.config.isSecret || row.isSecret;
928
+ return result(question, "kept", isSecret, row.updatedAt, row.value);
929
+ }
930
+ async function applyConfigAnswer(question, value) {
931
+ const { config } = question;
932
+ const existing = await readConfigRow(config);
933
+ const isSecret = config.isSecret || existing?.isSecret === true;
934
+ const written = await upsertSwarmConfigWithPolicyMirror({
935
+ scope: config.scope,
936
+ scopeId: config.scope === "global" ? null : config.scopeId ?? null,
937
+ key: config.key,
938
+ value,
939
+ isSecret,
940
+ envPath: existing?.envPath ?? null,
941
+ description: existing?.description ?? null
942
+ });
943
+ return result(question, "set", isSecret, written.lastUpdatedAt, value);
944
+ }
945
+ function formatConfigResult(value) {
946
+ const parsed = ConfigResultSchema.safeParse(value);
947
+ if (!parsed.success)
948
+ return null;
949
+ const r = parsed.data;
950
+ const where = `${r.scope}${r.scopeId ? `:${r.scopeId}` : ""}`;
951
+ const updated = r.updatedAt ? `, updated ${r.updatedAt}` : "";
952
+ const shown = !r.isSecret && r.value !== undefined ? ` = ${r.value}` : "";
953
+ return `${r.key} (${where}, ${r.isSecret ? "secret" : "plain"}): ${r.status}${updated}${shown}`;
954
+ }
955
+
956
+ // src/workflows/executors/human-in-the-loop.ts
460
957
  init_secret_scrubber();
461
958
 
462
959
  // src/workflows/approval-notifications.ts
@@ -489,7 +986,7 @@ function escapeSlackMrkdwn(text) {
489
986
  function buildSlackQuestionsSummary(questions, timeoutText = "") {
490
987
  const header = `*Questions:*
491
988
  `;
492
- const lines = questions.map((q) => `• ${escapeSlackMrkdwn(q.label.replace(/\s+/g, " ").trim())}`);
989
+ const lines = questions.map((q) => `• ${escapeSlackMrkdwn((q.label ?? "").replace(/\s+/g, " ").trim())}${q.type === "config" ? " (set on dashboard)" : ""}`);
493
990
  const fits = (shown, omitted) => {
494
991
  const tail = omitted > 0 ? `
495
992
  _…and ${omitted} more. Answer all of them on the dashboard._` : "";
@@ -687,6 +1184,8 @@ function formatResponses(questions, responses) {
687
1184
  let answerText;
688
1185
  if (answer == null) {
689
1186
  answerText = "(no answer)";
1187
+ } else if (q.type === "config") {
1188
+ answerText = formatConfigResult(answer) ?? "(unreadable config result)";
690
1189
  } else if (q.type === "approval") {
691
1190
  const a = answer;
692
1191
  answerText = a.approved ? "Approved" : "Rejected";
@@ -750,6 +1249,14 @@ var QuestionSchema = discriminatedUnion("type", [
750
1249
  required: boolean().default(true),
751
1250
  description: string2().optional(),
752
1251
  defaultValue: boolean().optional()
1252
+ }),
1253
+ object2({
1254
+ id: string2(),
1255
+ type: literal2("config"),
1256
+ label: string2().optional(),
1257
+ required: boolean().default(true),
1258
+ description: string2().optional(),
1259
+ config: ConfigQuestionSettingsSchema
753
1260
  })
754
1261
  ]);
755
1262
  var ApproverConfigSchema = object2({
@@ -841,7 +1348,17 @@ class HumanInTheLoopExecutor extends BaseExecutor {
841
1348
  const resolved = resolveHitlQuestions(input.config?.questions);
842
1349
  if (!resolved.ok)
843
1350
  return { status: "failed", error: resolved.error };
844
- return super.run({ ...input, config: { ...input.config, questions: resolved.questions } });
1351
+ const approvers = input.config?.approvers;
1352
+ const normalized = await normalizeApprovalQuestions(resolved.questions, {
1353
+ approvers: { policy: approvers?.policy }
1354
+ });
1355
+ if (!normalized.ok) {
1356
+ return {
1357
+ status: "failed",
1358
+ error: `human-in-the-loop questions are invalid: ${normalized.error}`
1359
+ };
1360
+ }
1361
+ return super.run({ ...input, config: { ...input.config, questions: normalized.questions } });
845
1362
  }
846
1363
  async execute(config, _context, meta) {
847
1364
  const { db } = this.deps;
@@ -2927,8 +3444,18 @@ init_db3();
2927
3444
 
2928
3445
  // src/workflows/checkpoint.ts
2929
3446
  init_db3();
2930
- async function checkpointStep(runId, stepId, nodeId, result, ctx) {
2931
- await getDbClient2().transaction(async () => {
3447
+ async function holdsFence(runId, stepId, fence) {
3448
+ const row = await getDbClient2().get(`SELECT 1 AS found
3449
+ FROM workflow_run_steps s
3450
+ JOIN workflow_runs r ON r.id = s.runId
3451
+ WHERE s.id = ? AND s.runId = ? AND s.status = 'running' AND s.retryCount = ?
3452
+ AND r.status IN ('running', 'waiting')`, [stepId, runId, fence.retryCount]);
3453
+ return row != null;
3454
+ }
3455
+ async function checkpointStep(runId, stepId, nodeId, result, ctx, options = {}) {
3456
+ return getDbClient2().transaction(async () => {
3457
+ if (options.fence && !await holdsFence(runId, stepId, options.fence))
3458
+ return false;
2932
3459
  await updateWorkflowRunStep2(stepId, {
2933
3460
  status: "completed",
2934
3461
  output: result.output,
@@ -2939,42 +3466,48 @@ async function checkpointStep(runId, stepId, nodeId, result, ctx) {
2939
3466
  await updateWorkflowRun2(runId, {
2940
3467
  context: ctx
2941
3468
  });
3469
+ return true;
2942
3470
  });
2943
3471
  }
2944
3472
  async function checkpointStepFailure(runId, stepId, error, retryCount, retryPolicy, options) {
2945
3473
  const now = new Date().toISOString();
2946
- if (retryPolicy && retryCount < retryPolicy.maxRetries) {
2947
- const delay = calculateBackoff(retryPolicy, retryCount);
2948
- const nextRetryAt = new Date(Date.now() + delay).toISOString();
3474
+ return getDbClient2().transaction(async () => {
3475
+ if (options?.fence && !await holdsFence(runId, stepId, options.fence)) {
3476
+ return { shouldRetry: false, stale: true };
3477
+ }
3478
+ if (retryPolicy && retryCount < retryPolicy.maxRetries) {
3479
+ const delay = calculateBackoff(retryPolicy, retryCount);
3480
+ const nextRetryAt = new Date(Date.now() + delay).toISOString();
3481
+ await updateWorkflowRunStep2(stepId, {
3482
+ status: "failed",
3483
+ error,
3484
+ output: options?.output,
3485
+ retryCount: retryCount + 1,
3486
+ maxRetries: retryPolicy.maxRetries,
3487
+ nextRetryAt
3488
+ });
3489
+ return { shouldRetry: true };
3490
+ }
2949
3491
  await updateWorkflowRunStep2(stepId, {
2950
3492
  status: "failed",
2951
3493
  error,
2952
- retryCount: retryCount + 1,
2953
- maxRetries: retryPolicy.maxRetries,
2954
- nextRetryAt
3494
+ output: options?.output,
3495
+ finishedAt: now,
3496
+ nextRetryAt: null
2955
3497
  });
2956
- return { shouldRetry: true };
2957
- }
2958
- await updateWorkflowRunStep2(stepId, {
2959
- status: "failed",
2960
- error,
2961
- finishedAt: now,
2962
- nextRetryAt: null
2963
- });
2964
- const markRunFailed = options?.markRunFailed ?? true;
2965
- if (markRunFailed) {
2966
- await getDbClient2().transaction(async () => {
3498
+ const markRunFailed = options?.markRunFailed ?? true;
3499
+ if (markRunFailed) {
2967
3500
  const run = await getWorkflowRun2(runId);
2968
- if (run?.status !== "running" && run?.status !== "waiting")
2969
- return;
2970
- await updateWorkflowRun2(runId, {
2971
- status: "failed",
2972
- error: `Step failed: ${error}`,
2973
- finishedAt: now
2974
- });
2975
- });
2976
- }
2977
- return { shouldRetry: false };
3501
+ if (run?.status === "running" || run?.status === "waiting") {
3502
+ await updateWorkflowRun2(runId, {
3503
+ status: "failed",
3504
+ error: `Step failed: ${error}`,
3505
+ finishedAt: now
3506
+ });
3507
+ }
3508
+ }
3509
+ return { shouldRetry: false };
3510
+ });
2978
3511
  }
2979
3512
  async function checkpointStepWaiting(runId, stepId, ctx, options = {}) {
2980
3513
  return getDbClient2().transaction(async () => {
@@ -3021,6 +3554,7 @@ var FOREACH_ITEMS_WAIT_FOR = "foreach.items";
3021
3554
 
3022
3555
  // src/workflows/engine.ts
3023
3556
  init_db3();
3557
+ init_workflow_runs();
3024
3558
  init_workflow_replay();
3025
3559
  init_telemetry();
3026
3560
 
@@ -3408,7 +3942,7 @@ async function resolveRenderedWorkflowInputs(input) {
3408
3942
  }
3409
3943
  async function startWorkflowExecution2(workflow, triggerData, registry, options = {}) {
3410
3944
  const preflight = preflightAutomation(workflowPreflightInput(workflow), await getAutomationSetupStates());
3411
- if (preflight.state === "needs_setup") {
3945
+ if (preflight.state === "needs_setup" && !options.parentStepId) {
3412
3946
  const { runId, recorded } = await recordWorkflowPreflightFailure({
3413
3947
  workflowId: workflow.id,
3414
3948
  triggerType: options.triggerType ?? "manual",
@@ -3420,52 +3954,74 @@ async function startWorkflowExecution2(workflow, triggerData, registry, options
3420
3954
  await notifyAutomationPreflightFailure(preflight);
3421
3955
  return runId;
3422
3956
  }
3423
- workflow = renderAutomationTokens(workflow, workflow.params ?? {});
3424
- if (workflow.triggerSchema) {
3425
- const validationErrors = validateJsonSchema(workflow.triggerSchema, triggerData);
3426
- if (validationErrors.length > 0) {
3427
- throw new TriggerSchemaError(validationErrors);
3957
+ let initial;
3958
+ if (preflight.state === "needs_setup") {
3959
+ initial = {
3960
+ status: "failed",
3961
+ error: preflight.failureReason,
3962
+ finishedAt: new Date().toISOString(),
3963
+ preflight: true
3964
+ };
3965
+ } else {
3966
+ workflow = renderAutomationTokens(workflow, workflow.params ?? {});
3967
+ if (workflow.triggerSchema) {
3968
+ const validationErrors = validateJsonSchema(workflow.triggerSchema, triggerData);
3969
+ if (validationErrors.length > 0) {
3970
+ throw new TriggerSchemaError(validationErrors);
3971
+ }
3972
+ }
3973
+ if (workflow.cooldown && await shouldSkipCooldown(workflow.id, workflow.cooldown)) {
3974
+ initial = { status: "skipped", error: "cooldown", finishedAt: new Date().toISOString() };
3975
+ } else {
3976
+ initial = await buildInitialRunState(workflow, triggerData, registry, options);
3428
3977
  }
3429
3978
  }
3430
- if (workflow.cooldown && await shouldSkipCooldown(workflow.id, workflow.cooldown)) {
3431
- const runId = crypto.randomUUID();
3432
- await createWorkflowRun2({
3433
- id: runId,
3979
+ const newRunId = crypto.randomUUID();
3980
+ const release = holdWorkflowRun(newRunId);
3981
+ try {
3982
+ const { runId, created, preflightAlert } = await createRunWithInitialState(workflow, triggerData, options, initial, newRunId);
3983
+ if (!created)
3984
+ return runId;
3985
+ if (initial.status === "failed" && initial.preflight) {
3986
+ if (preflightAlert)
3987
+ await notifyAutomationPreflightFailure(preflight);
3988
+ return runId;
3989
+ }
3990
+ if (initial.status === "skipped")
3991
+ return runId;
3992
+ telemetry.workflow("started", {
3434
3993
  workflowId: workflow.id,
3435
- triggerType: options.triggerType ?? "manual",
3436
- triggerData,
3437
- createdBy: options.requestedByUserId,
3438
- parentStepId: options.parentStepId
3439
- });
3440
- await updateWorkflowRun2(runId, {
3441
- status: "skipped",
3442
- error: "cooldown",
3443
- finishedAt: new Date().toISOString()
3994
+ nodeCount: workflow.definition.nodes.length,
3995
+ triggerType: options.triggerType ?? "manual"
3444
3996
  });
3997
+ if (initial.status)
3998
+ return runId;
3999
+ const walk = walkGraph(workflow.definition, runId, initial.context, findEntryNodes(workflow.definition), registry, workflow.id, getSecretInputKeys(workflow.input), options);
4000
+ if (options.parentStepId) {
4001
+ walk.catch((err) => console.error(`[workflows] Child workflow run ${runId} failed:`, err));
4002
+ return runId;
4003
+ }
4004
+ await walk;
3445
4005
  return runId;
4006
+ } finally {
4007
+ release();
3446
4008
  }
3447
- const runId = crypto.randomUUID();
3448
- await createWorkflowRun2({
3449
- id: runId,
3450
- workflowId: workflow.id,
3451
- triggerType: options.triggerType ?? "manual",
3452
- triggerData,
3453
- createdBy: options.requestedByUserId,
3454
- parentStepId: options.parentStepId
3455
- });
3456
- telemetry.workflow("started", {
3457
- workflowId: workflow.id,
3458
- nodeCount: workflow.definition.nodes.length,
3459
- triggerType: options.triggerType ?? "manual"
3460
- });
4009
+ }
4010
+
4011
+ class ParentStepNotCurrentError extends Error {
4012
+ constructor(parentStepId, attempt) {
4013
+ super(`Parent step ${parentStepId} (attempt ${attempt}) is no longer running; no child run started`);
4014
+ this.name = "ParentStepNotCurrentError";
4015
+ }
4016
+ }
4017
+ async function buildInitialRunState(workflow, triggerData, registry, options) {
3461
4018
  const notReady = await findWorkflowReadinessProblems(workflow.definition, registry);
3462
4019
  if (notReady.length > 0) {
3463
- await updateWorkflowRun2(runId, {
4020
+ return {
3464
4021
  status: "failed",
3465
4022
  error: formatReadinessRunError(notReady),
3466
4023
  finishedAt: new Date().toISOString()
3467
- });
3468
- return runId;
4024
+ };
3469
4025
  }
3470
4026
  const ctx = { trigger: triggerData };
3471
4027
  if (options.requestedByUserId) {
@@ -3479,23 +4035,40 @@ async function startWorkflowExecution2(workflow, triggerData, registry, options
3479
4035
  const resolved = await resolveRenderedWorkflowInputs(workflow.input);
3480
4036
  Object.assign(ctx, { input: resolved });
3481
4037
  } catch (err) {
3482
- await updateWorkflowRun2(runId, {
4038
+ return {
3483
4039
  status: "failed",
3484
4040
  error: `Input resolution failed: ${err}`,
3485
4041
  finishedAt: new Date().toISOString()
3486
- });
3487
- return runId;
4042
+ };
3488
4043
  }
3489
4044
  }
3490
- const entryNodes = findEntryNodes(workflow.definition);
3491
- const secretKeys = getSecretInputKeys(workflow.input);
3492
- const walk = walkGraph(workflow.definition, runId, ctx, entryNodes, registry, workflow.id, secretKeys, options);
3493
- if (options.parentStepId) {
3494
- walk.catch((err) => console.error(`[workflows] Child workflow run ${runId} failed:`, err));
3495
- return runId;
3496
- }
3497
- await walk;
3498
- return runId;
4045
+ return { context: ctx };
4046
+ }
4047
+ async function createRunWithInitialState(workflow, triggerData, options, initial, runId) {
4048
+ return getDbClient2().transaction(async () => {
4049
+ const { parentStepId } = options;
4050
+ const attempt = options.parentStepAttempt ?? 0;
4051
+ if (parentStepId) {
4052
+ if (!await isParentStepAttemptCurrent(parentStepId, attempt)) {
4053
+ throw new ParentStepNotCurrentError(parentStepId, attempt);
4054
+ }
4055
+ const existingId = await getChildWorkflowRunId(parentStepId, attempt);
4056
+ if (existingId)
4057
+ return { runId: existingId, created: false };
4058
+ }
4059
+ await createWorkflowRun2({
4060
+ id: runId,
4061
+ workflowId: workflow.id,
4062
+ triggerType: options.triggerType ?? "manual",
4063
+ triggerData,
4064
+ createdBy: options.requestedByUserId,
4065
+ parentStepId,
4066
+ parentStepAttempt: parentStepId ? attempt : undefined
4067
+ });
4068
+ await updateWorkflowRun2(runId, initial.status ? { status: initial.status, error: initial.error, finishedAt: initial.finishedAt } : { context: initial.context });
4069
+ const preflightAlert = initial.status === "failed" && initial.preflight ? await isFirstWorkflowPreflightFailureToday(workflow.id, runId) : undefined;
4070
+ return { runId, created: true, preflightAlert };
4071
+ });
3499
4072
  }
3500
4073
  var activeWalks = new Map;
3501
4074
  var executingSteps = new Set;
@@ -3518,6 +4091,9 @@ async function failRunOnUnreadableReplay(runId, err, stepId) {
3518
4091
  function isWorkflowRunActive(runId) {
3519
4092
  return activeWalks.has(runId);
3520
4093
  }
4094
+ function isStepExecuting(stepId) {
4095
+ return executingSteps.has(stepId);
4096
+ }
3521
4097
  function holdWorkflowRun(runId) {
3522
4098
  activeWalks.set(runId, (activeWalks.get(runId) ?? 0) + 1);
3523
4099
  let released = false;
@@ -3765,12 +4341,27 @@ async function executeStep(def, runId, ctx, node, registry, workflowId, secretKe
3765
4341
  if (current && (current.status !== "running" || executingSteps.has(current.id))) {
3766
4342
  return { current };
3767
4343
  }
4344
+ if (current && node.type === "sub-workflow") {
4345
+ claimedStepId = current.id;
4346
+ executingSteps.add(current.id);
4347
+ return {
4348
+ existingStep: current,
4349
+ stepId: current.id,
4350
+ fence: { retryCount: current.retryCount },
4351
+ deduped: false
4352
+ };
4353
+ }
3768
4354
  }
3769
4355
  const iteration = await getStepCountForNode2(runId, node.id);
3770
4356
  const idempotencyKey = `${runId}:${node.id}:${iteration}`;
3771
4357
  const existingStep = await getStepByIdempotencyKey2(idempotencyKey);
3772
4358
  if (existingStep && (existingStep.status === "completed" || existingStep.status === "waiting")) {
3773
- return { existingStep, stepId: existingStep.id, deduped: true };
4359
+ return {
4360
+ existingStep,
4361
+ stepId: existingStep.id,
4362
+ fence: { retryCount: existingStep.retryCount },
4363
+ deduped: true
4364
+ };
3774
4365
  }
3775
4366
  const latestForeachStep = node.type === "foreach" ? await getLatestStepForNode2(runId, node.id) : undefined;
3776
4367
  const reusableForeachStep = latestForeachStep && !FOREACH_TERMINAL_STEP_STATUSES.has(latestForeachStep.status) ? latestForeachStep : undefined;
@@ -3787,7 +4378,8 @@ async function executeStep(def, runId, ctx, node, registry, workflowId, secretKe
3787
4378
  }
3788
4379
  claimedStepId = stepId;
3789
4380
  executingSteps.add(stepId);
3790
- return { existingStep, stepId, deduped: false };
4381
+ const fence = { retryCount: reusableForeachStep?.retryCount ?? 0 };
4382
+ return { existingStep, stepId, fence, deduped: false };
3791
4383
  };
3792
4384
  const dedup = await getDbClient2().transaction(claimStep).catch((err) => {
3793
4385
  if (claimedStepId)
@@ -3811,7 +4403,7 @@ async function executeStep(def, runId, ctx, node, registry, workflowId, secretKe
3811
4403
  return { outcome: "waiting", successors: [] };
3812
4404
  }
3813
4405
  try {
3814
- return await runClaimedStep(def, runId, ctx, node, registry, dedup.existingStep, dedup.stepId, workflowId, options);
4406
+ return await runClaimedStep(def, runId, ctx, node, registry, dedup.existingStep, dedup.stepId, dedup.fence, workflowId, options);
3815
4407
  } catch (err) {
3816
4408
  if (await failRunOnUnreadableReplay(runId, err, dedup.stepId)) {
3817
4409
  return { outcome: "failed", successors: [] };
@@ -3821,11 +4413,14 @@ async function executeStep(def, runId, ctx, node, registry, workflowId, secretKe
3821
4413
  executingSteps.delete(dedup.stepId);
3822
4414
  }
3823
4415
  }
3824
- async function runClaimedStep(def, runId, ctx, node, registry, existingStep, stepId, workflowId, options = {}) {
4416
+ async function runClaimedStep(def, runId, ctx, node, registry, existingStep, stepId, fence, workflowId, options = {}) {
4417
+ const stale = { outcome: "failed", successors: [] };
3825
4418
  const executor = registry.get(node.type);
3826
4419
  const systemOneRetryProblems = systemOneRetryViolations(node);
3827
4420
  if (systemOneRetryProblems.length > 0) {
3828
- await checkpointStepFailure(runId, stepId, systemOneRetryProblems.join("; "), 0);
4421
+ await checkpointStepFailure(runId, stepId, systemOneRetryProblems.join("; "), 0, undefined, {
4422
+ fence
4423
+ });
3829
4424
  return { outcome: "failed", successors: [] };
3830
4425
  }
3831
4426
  const interpolationCtx = buildNodeInterpolationCtx(node, ctx);
@@ -3833,7 +4428,11 @@ async function runClaimedStep(def, runId, ctx, node, registry, existingStep, ste
3833
4428
  const inputErrors = validateJsonSchema(node.inputSchema, interpolationCtx);
3834
4429
  if (inputErrors.length > 0) {
3835
4430
  const errorMsg = `Input schema validation failed: ${inputErrors.join("; ")}`;
3836
- await checkpointStepFailure(runId, stepId, errorMsg, 0);
4431
+ const { stale: lost } = await checkpointStepFailure(runId, stepId, errorMsg, 0, undefined, {
4432
+ fence
4433
+ });
4434
+ if (lost)
4435
+ return stale;
3837
4436
  throw new Error(errorMsg);
3838
4437
  }
3839
4438
  }
@@ -3847,11 +4446,11 @@ async function runClaimedStep(def, runId, ctx, node, registry, existingStep, ste
3847
4446
  const executionCtx = { ...ctx, ...interpolationCtx };
3848
4447
  if (scriptBodyUnresolved && scriptBodyUnresolved.length > 0) {
3849
4448
  const errorMsg = scriptBodyInterpolationError(node.id, scriptBodyUnresolved);
3850
- await checkpointStepFailure(runId, stepId, errorMsg, 0);
4449
+ await checkpointStepFailure(runId, stepId, errorMsg, 0, undefined, { fence });
3851
4450
  return { outcome: "failed", successors: [] };
3852
4451
  }
3853
4452
  if (strictUnresolved && strictUnresolved.length > 0) {
3854
- await checkpointStepFailure(runId, stepId, systemOneUnresolvedError(node.id, strictUnresolved, node.type), 0);
4453
+ await checkpointStepFailure(runId, stepId, systemOneUnresolvedError(node.id, strictUnresolved, node.type), 0, undefined, { fence });
3855
4454
  return { outcome: "failed", successors: [] };
3856
4455
  }
3857
4456
  if (unresolved.length > 0) {
@@ -3884,19 +4483,20 @@ async function runClaimedStep(def, runId, ctx, node, registry, existingStep, ste
3884
4483
  const errorMsg = err instanceof Error ? err.message : String(err);
3885
4484
  const retryPolicy = node.retry || executor.retryPolicy;
3886
4485
  const currentRetryCount = existingStep?.retryCount || 0;
3887
- const { shouldRetry } = await checkpointStepFailure(runId, stepId, errorMsg, currentRetryCount, retryPolicy);
4486
+ const { shouldRetry, stale: lost } = await checkpointStepFailure(runId, stepId, errorMsg, currentRetryCount, retryPolicy, { fence });
4487
+ if (lost)
4488
+ return stale;
3888
4489
  if (!shouldRetry) {
3889
4490
  throw err;
3890
4491
  }
3891
4492
  return { outcome: "completed", successors: [] };
3892
4493
  }
3893
4494
  if (result.status === "failed") {
3894
- const retryPolicy = node.retry || executor.retryPolicy;
4495
+ const retryPolicy = result.retryable === false ? undefined : node.retry || executor.retryPolicy;
3895
4496
  const currentRetryCount = existingStep?.retryCount || 0;
3896
- const { shouldRetry } = await checkpointStepFailure(runId, stepId, result.error || "Executor returned failed status", currentRetryCount, retryPolicy);
3897
- if (result.output !== undefined) {
3898
- await updateWorkflowRunStep2(stepId, { output: result.output });
3899
- }
4497
+ const { shouldRetry, stale: lost } = await checkpointStepFailure(runId, stepId, result.error || "Executor returned failed status", currentRetryCount, retryPolicy, { fence, output: result.output });
4498
+ if (lost)
4499
+ return stale;
3900
4500
  if (!shouldRetry) {
3901
4501
  throw new Error(result.error || "Step execution failed");
3902
4502
  }
@@ -3917,7 +4517,7 @@ async function runClaimedStep(def, runId, ctx, node, registry, existingStep, ste
3917
4517
  }
3918
4518
  const waiting = await checkpointStepWaiting(runId, stepId, ctx);
3919
4519
  if (waiting && result.waitFor === "workflow.child.finished") {
3920
- const { resumeFromChildRun } = await import("./resume-a26kvr41.js");
4520
+ const { resumeFromChildRun } = await import("./resume-2dvq348g.js");
3921
4521
  await resumeFromChildRun(stepId, registry);
3922
4522
  }
3923
4523
  return { outcome: waiting ? "waiting" : "completed", successors: [] };
@@ -3926,7 +4526,11 @@ async function runClaimedStep(def, runId, ctx, node, registry, existingStep, ste
3926
4526
  const outputErrors = validateJsonSchema(node.outputSchema, result.output);
3927
4527
  if (outputErrors.length > 0) {
3928
4528
  const errorMsg = `Output schema validation failed: ${outputErrors.join("; ")}`;
3929
- await checkpointStepFailure(runId, stepId, errorMsg, 0);
4529
+ const { stale: lost } = await checkpointStepFailure(runId, stepId, errorMsg, 0, undefined, {
4530
+ fence
4531
+ });
4532
+ if (lost)
4533
+ return stale;
3930
4534
  throw new Error(errorMsg);
3931
4535
  }
3932
4536
  }
@@ -3935,7 +4539,10 @@ async function runClaimedStep(def, runId, ctx, node, registry, existingStep, ste
3935
4539
  validationResult = await runStepValidation(registry, node, result.output, executionCtx, meta);
3936
4540
  if (validationResult.outcome === "halt") {
3937
4541
  const errorMsg = "Validation failed (mustPass)";
3938
- await checkpointStepFailure(runId, stepId, errorMsg, 0, undefined, { markRunFailed: false });
4542
+ await checkpointStepFailure(runId, stepId, errorMsg, 0, undefined, {
4543
+ markRunFailed: false,
4544
+ fence
4545
+ });
3939
4546
  return { outcome: "failed", successors: [] };
3940
4547
  }
3941
4548
  if (validationResult.outcome === "retry") {
@@ -3946,12 +4553,15 @@ async function runClaimedStep(def, runId, ctx, node, registry, existingStep, ste
3946
4553
  }
3947
4554
  const retryPolicy = node.validation.retry || node.retry;
3948
4555
  const currentRetryCount = existingStep?.retryCount || 0;
3949
- await checkpointStepFailure(runId, stepId, "Validation failed, retrying", currentRetryCount, retryPolicy);
4556
+ const { stale: lost } = await checkpointStepFailure(runId, stepId, "Validation failed, retrying", currentRetryCount, retryPolicy, { fence });
4557
+ if (lost)
4558
+ return stale;
3950
4559
  return { outcome: "completed", successors: [] };
3951
4560
  }
3952
4561
  }
3953
4562
  result.nextPort = resolveValidationPort(node.next, result.nextPort, validationResult?.passed);
3954
- await checkpointStep(runId, stepId, node.id, result, ctx);
4563
+ if (!await checkpointStep(runId, stepId, node.id, result, ctx, { fence }))
4564
+ return stale;
3955
4565
  const successors = result.nextPort ? getSuccessors(def, node.id, result.nextPort) : getSuccessors(def, node.id);
3956
4566
  return { outcome: "completed", successors };
3957
4567
  }
@@ -4369,7 +4979,7 @@ async function runForeachDrive(runId, parentNodeId, registry) {
4369
4979
  if (drive.successors.length > 0) {
4370
4980
  await walkGraph(workflow.definition, runId, ctx, drive.successors, registry, workflow.id, getSecretInputKeys(workflow.input));
4371
4981
  } else {
4372
- const { finalizeOrWait } = await import("./resume-a26kvr41.js");
4982
+ const { finalizeOrWait } = await import("./resume-2dvq348g.js");
4373
4983
  await finalizeOrWait(runId);
4374
4984
  }
4375
4985
  return drive;
@@ -4511,7 +5121,7 @@ async function startChild(input) {
4511
5121
  const parked = await getDbClient2().get("UPDATE workflow_run_steps SET status = 'waiting' WHERE id = ? AND status = 'running' RETURNING id", [child.id]);
4512
5122
  if (parked && result.waitFor === "workflow.child.finished") {
4513
5123
  ownedChildren.delete(child.id);
4514
- const { resumeFromChildRun } = await import("./resume-a26kvr41.js");
5124
+ const { resumeFromChildRun } = await import("./resume-2dvq348g.js");
4515
5125
  await resumeFromChildRun(child.id, registry);
4516
5126
  }
4517
5127
  return null;
@@ -5122,4 +5732,4 @@ function applyDefinitionPatch(def, patch) {
5122
5732
  return { definition: patchedDef, errors };
5123
5733
  }
5124
5734
 
5125
- export { notifyAutomationPreflightFailure, automationIntegrationFixUrl, isNeedsSetupFailure, preflightAutomation, renderAutomationTokens, getAutomationSetupStates, schedulePreflightInput, listEnabledAutomationPreflightInputs, recordSchedulePreflightFailure, BaseExecutor, resolveSlackConversationId, dispatchApprovalNotifications, postApprovalCancellationUpdates, createApprovalFollowUpTask, HumanInTheLoopExecutor, resolveWorkflowLlmConfig, SYSTEM_ONE_PROVIDER_IDS, SystemOneExtractionExecutor, SYSTEM_ONE_DECISION_NODE_TYPE, SystemOneDecisionConfigSchema, SystemOneDecisionExecutor, checkpointStep, checkpointStepFailure, checkpointStepWaiting, FAILED_TASK_OUTPUT_PREFIX, FOREACH_ITEMS_WAIT_FOR, loadCompletedStepRouting, resolveInputValue, getSecretInputKeys, validateJsonSchema, findJsonSchemaShapeErrors, getMaxWorkflowStepsPerRun, findWorkflowReadinessProblems, formatReadinessProblems, workflowSaveWarnings, withSaveWarnings, runStepValidation, TriggerSchemaError, startWorkflowExecution2, failRunOnUnreadableReplay, isWorkflowRunActive, holdWorkflowRun, walkGraph, hasRunningStep, findReadyNodes, scriptBodyInterpolationError, ForeachExecutor, rebuildNodeExecutionInput, isForeachChildOwned, parkAndDriveForeach, startForeachDrive, runForeachDrive, FOREACH_TERMINAL_STEP_STATUSES, parseSyntheticNodeId, resolveForeachParent, settleForeachChild, resolveStepNode, stepFailureMode, getNextTargets, generateEdges, resolveValidationPort, getSuccessors, definitionNodeIds, validateDefinition, applyDefinitionPatch };
5735
+ export { notifyAutomationPreflightFailure, automationIntegrationFixUrl, isNeedsSetupFailure, preflightAutomation, renderAutomationTokens, getAutomationSetupStates, schedulePreflightInput, listEnabledAutomationPreflightInputs, recordSchedulePreflightFailure, BaseExecutor, AGENT_MAX_TASKS_CONFIG_KEY, reconcileAgentMaxTasksPolicy, resetAgentMaxTasksMirror, upsertSwarmConfigWithPolicyMirror, runtimeStaleThresholdMinutes, upsertRuntimeInstance, getRuntimeInstanceById, insertProvisioningRuntime, markRuntimeTerminated, touchRuntimeInstance, markRuntimeInstanceOffline, countActiveRuntimeInstancesForAgent, listRuntimeInstancesForAgent, setRuntimeCredentialReady, reconcileAgentStatusFromRuntimes, agentsWithLiveRuntime, expireStaleRuntimeInstances, listRuntimeInstancesForPool, ConfigQuestionSettingsSchema, ConfigStateSchema, readConfigState, normalizeApprovalQuestions, isConfigQuestion, parseConfigAnswer, skippedConfigResult, keptConfigResult, applyConfigAnswer, resolveSlackConversationId, dispatchApprovalNotifications, postApprovalCancellationUpdates, createApprovalFollowUpTask, HumanInTheLoopExecutor, resolveWorkflowLlmConfig, SYSTEM_ONE_PROVIDER_IDS, SystemOneExtractionExecutor, SYSTEM_ONE_DECISION_NODE_TYPE, SystemOneDecisionConfigSchema, SystemOneDecisionExecutor, checkpointStep, checkpointStepFailure, checkpointStepWaiting, FAILED_TASK_OUTPUT_PREFIX, FOREACH_ITEMS_WAIT_FOR, loadCompletedStepRouting, resolveInputValue, getSecretInputKeys, validateJsonSchema, findJsonSchemaShapeErrors, getMaxWorkflowStepsPerRun, findWorkflowReadinessProblems, formatReadinessProblems, workflowSaveWarnings, withSaveWarnings, runStepValidation, TriggerSchemaError, startWorkflowExecution2, ParentStepNotCurrentError, failRunOnUnreadableReplay, isWorkflowRunActive, isStepExecuting, holdWorkflowRun, walkGraph, hasRunningStep, findReadyNodes, scriptBodyInterpolationError, ForeachExecutor, rebuildNodeExecutionInput, isForeachChildOwned, parkAndDriveForeach, startForeachDrive, runForeachDrive, FOREACH_TERMINAL_STEP_STATUSES, parseSyntheticNodeId, resolveForeachParent, settleForeachChild, resolveStepNode, stepFailureMode, getNextTargets, generateEdges, resolveValidationPort, getSuccessors, definitionNodeIds, validateDefinition, applyDefinitionPatch };