@desplega.ai/agent-swarm 2.5.2 → 2.5.4
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-csh0zz3b.js → acp-adapter-ek38cwsv.js} +5 -5
- package/dist/{actions-6xx079d1.js → actions-92mepq03.js} +19 -19
- package/dist/{amp-adapter-8n6f0hz8.js → amp-adapter-9gaxg015.js} +4 -4
- package/dist/{app-ft6r6v23.js → app-ctn8gsgx.js} +3 -3
- package/dist/{assistant-vt2cyfzd.js → assistant-zyr5kz47.js} +21 -21
- package/dist/{boot-reembed-tgrfy27t.js → boot-reembed-dwzfttdr.js} +1 -1
- package/dist/{boot-reembed-r3e808ky.js → boot-reembed-w75maga2.js} +1 -1
- package/dist/{boot-scrub-sweep-masdwxkm.js → boot-scrub-sweep-e9kq43kf.js} +3 -3
- package/dist/{boot-seeds-0n82qc7q.js → boot-seeds-x28v3fhb.js} +4 -4
- package/dist/{claude-adapter-s7j47k06.js → claude-adapter-f0afqnne.js} +5 -5
- package/dist/{claude-managed-adapter-gt6pa35d.js → claude-managed-adapter-x81a7r65.js} +3 -3
- package/dist/{claude-managed-setup-3hzrgrnk.js → claude-managed-setup-bqzwjd9x.js} +2 -2
- package/dist/{claude-sdk-session-0zrdzyx2.js → claude-sdk-session-7b7whkaq.js} +9307 -9254
- package/dist/{cli-a5ebc95n.js → cli-0shx9j93.js} +16 -13
- package/dist/{cli-4qkg9968.js → cli-19202kew.js} +4 -4
- package/dist/{cli-cyfgxnvb.js → cli-1r5ad7j5.js} +19 -17
- package/dist/{cli-jmcjbjkt.js → cli-25n3mv5d.js} +9 -7
- package/dist/cli-2655k16q.js +140 -0
- package/dist/{cli-wadzapwb.js → cli-277c31dw.js} +1 -1
- package/dist/{cli-n6kmqngw.js → cli-4b2z6n0m.js} +29 -7
- package/dist/{cli-7ccf7akh.js → cli-5bnsc0e1.js} +9 -7
- package/dist/{cli-qrjv7t18.js → cli-621ce7kw.js} +1 -1
- package/dist/{cli-xzge46sj.js → cli-64mh9mf3.js} +20 -6
- package/dist/{cli-b6tnjdvx.js → cli-6a1xg1p7.js} +6 -2
- package/dist/{cli-ydg6ep51.js → cli-6n8axjv1.js} +1 -1
- package/dist/{cli-e91vshb1.js → cli-7p4jxgtt.js} +5 -5
- package/dist/{cli-f3pfn4x3.js → cli-9j129brq.js} +9 -3
- package/dist/{cli-xw9q173h.js → cli-9s05422w.js} +1 -1
- package/dist/{cli-15xd0j7w.js → cli-abs8ndtr.js} +12 -12
- package/dist/{cli-h5d22pfs.js → cli-asq5ydm4.js} +219 -309
- package/dist/{cli-w9etnnxk.js → cli-bf8ma3f1.js} +120 -50
- package/dist/{cli-0vgy5wfw.js → cli-bvpgr9ny.js} +2 -2
- package/dist/{cli-3e9vs93g.js → cli-c61mbdds.js} +4 -4
- package/dist/{cli-e5bxnjps.js → cli-c952c7ac.js} +5 -5
- package/dist/{cli-p859hcc8.js → cli-dew6x255.js} +1 -1
- package/dist/{cli-tx00c0h6.js → cli-dqygeeh5.js} +17 -1
- package/dist/{cli-gpv60278.js → cli-e42vss7b.js} +2 -2
- package/dist/{cli-b63d08rs.js → cli-eftzyh9y.js} +1 -1
- package/dist/{cli-fgn7yg5r.js → cli-ethgy7ha.js} +1 -1
- package/dist/{cli-86zpfje5.js → cli-ff1tm9n9.js} +1 -1
- package/dist/{cli-cf63dnxj.js → cli-g9vd9ece.js} +37 -12
- package/dist/{cli-j3dwd4kz.js → cli-gbfxp48k.js} +770 -126
- package/dist/{cli-n2918sed.js → cli-gn5af0x5.js} +21 -21
- package/dist/{cli-d172svcw.js → cli-gwhjmh8g.js} +4 -4
- package/dist/{cli-stxdrqs2.js → cli-hh5we352.js} +24 -5
- package/dist/{cli-0ebey9hg.js → cli-j6rktc91.js} +1 -1
- package/dist/{cli-csh8fnbg.js → cli-jb30dzvk.js} +473 -58
- package/dist/{cli-vaxw3bg0.js → cli-jbnn4vxb.js} +3 -3
- package/dist/{cli-ha22h0eg.js → cli-k4tjsdx9.js} +45 -40
- package/dist/{cli-f55b6ky0.js → cli-m4ecdxvz.js} +40 -13
- package/dist/{cli-mntxtkhn.js → cli-mz9afxxf.js} +2 -1
- package/dist/{cli-ta8bd7th.js → cli-pzatvw94.js} +1 -1
- package/dist/{cli-3qv8naq8.js → cli-rr7zx19j.js} +1591 -353
- package/dist/{cli-b1tv22wr.js → cli-s682zgqz.js} +1 -1
- package/dist/{cli-bpbcpd8b.js → cli-sbwsxvtd.js} +1 -1
- package/dist/{cli-0xk505e8.js → cli-sxgtf5fw.js} +1 -1
- package/dist/{cli-6y43v79h.js → cli-tsma7p8v.js} +1 -1
- package/dist/{cli-edtm741g.js → cli-wghjkr0e.js} +5 -5
- package/dist/{cli-aaqnqpyw.js → cli-wx2nj19m.js} +30 -11
- package/dist/{cli-y3dzwyy3.js → cli-x0f2v69c.js} +180 -60
- package/dist/{cli-kcnwn85g.js → cli-xtrz6bzs.js} +7 -7
- package/dist/{cli-14v1sxxy.js → cli-xzfq4at3.js} +7 -7
- package/dist/cli.js +20 -20
- package/dist/{codex-adapter-brh2tcxr.js → codex-adapter-wch12q99.js} +3 -3
- package/dist/{codex-hook-e2he7aca.js → codex-hook-brpnycn5.js} +1 -1
- package/dist/{codex-session-runner-0dnj21qs.js → codex-session-runner-41pfcr95.js} +2 -2
- package/dist/{commands-1cnvseqx.js → commands-9849dkj6.js} +2 -2
- package/dist/{controller-kc7s9xhn.js → controller-ja0sd95p.js} +29 -3
- package/dist/{controllers-connect-jzx7t4bn.js → controllers-connect-xp05dj6x.js} +20 -20
- package/dist/{cursor-adapter-qpwp5sfg.js → cursor-adapter-623qxqew.js} +1 -2
- package/dist/{db-4vq88jkc.js → db-47vj40dy.js} +3 -3
- package/dist/{db-v5ytg5pw.js → db-w759qmx0.js} +2 -2
- package/dist/{dispatcher-trtcrqae.js → dispatcher-7b8e4z2j.js} +17 -17
- package/dist/{docker-nfy2gc8t.js → docker-s84hjs91.js} +2 -2
- package/dist/{dsh-adapter-f20xwn1z.js → dsh-adapter-2ezw3xea.js} +2 -3
- package/dist/{external-channel-n9bymc2n.js → external-channel-e2s028da.js} +2 -2
- package/dist/{grok-adapter-4kpmmnr6.js → grok-adapter-ys7gaqee.js} +6 -6
- package/dist/{handlers-ptsraqa8.js → handlers-wy1dr6ja.js} +20 -20
- package/dist/{heartbeat-60qtxtjf.js → heartbeat-w3mntghj.js} +20 -20
- package/dist/{hook-rbktcstg.js → hook-3jjywnpb.js} +9 -3
- package/dist/{hook-ngtvh729.js → hook-g7kvv6x9.js} +3 -2
- package/dist/{http-z8xy72gp.js → http-tkp7d4kb.js} +2984 -2483
- package/dist/{index-r70q0xjx.js → index-1ef1myq2.js} +17 -17
- package/dist/{index-a9nsavhx.js → index-3n8m9ea2.js} +1 -1
- package/dist/{index-cbxkmfc5.js → index-9pn1pfkf.js} +1 -1
- package/dist/{index-s193j5fh.js → index-nmm79qgr.js} +20 -20
- package/dist/{index-6srwvd5b.js → index-p4xfz37z.js} +1 -1
- package/dist/{index-06qkmcx6.js → index-q7m26kvs.js} +33 -23
- package/dist/{index-1cscynkb.js → index-xdaahthb.js} +17 -17
- package/dist/{keepalive-a9srcphj.js → keepalive-v4ta8wgg.js} +3 -3
- package/dist/{lead-3devneqy.js → lead-sawnyqxk.js} +35 -35
- package/dist/{maintenance-0er16h2y.js → maintenance-84n1ex21.js} +1 -1
- package/dist/{oauth-refresh-sweep-b65jcyw2.js → oauth-refresh-sweep-vjnnandw.js} +2 -2
- package/dist/{onboard-wqywz5py.js → onboard-sfmpe4np.js} +2 -2
- package/dist/{opencode-adapter-e56086m1.js → opencode-adapter-jmpst4q4.js} +4 -4
- package/dist/{otel-impl-vk32s884.js → otel-impl-v6cc0zve.js} +1 -1
- package/dist/{pi-mono-adapter-tzps8f38.js → pi-mono-adapter-fk8fkcf6.js} +33 -6
- package/dist/{pricing-refresh-t27anv4z.js → pricing-refresh-fsk1e1zc.js} +3 -3
- package/dist/{rbac-roles-452665y0.js → rbac-roles-prmkdnkw.js} +2 -2
- package/dist/{rbac-roles-bmcgsxjn.js → rbac-roles-zxmxqa39.js} +1 -1
- package/dist/{render-v2-0ttsk2j4.js → render-v2-pjwxvnkw.js} +3 -3
- package/dist/{resume-a26kvr41.js → resume-frdtgfa0.js} +9 -5
- package/dist/{session-access-dgh9nkwk.js → session-access-fpez4y5q.js} +4 -4
- package/dist/{setup-9vc9qahm.js → setup-mq6m3m8q.js} +1 -1
- package/dist/{work-objects-vn8nkxwn.js → work-objects-kfahhjsq.js} +20 -20
- package/dist/{worker-1x1h8vnm.js → worker-v5e5rxj3.js} +35 -35
- package/openapi.json +3078 -2424
- package/package.json +7 -7
- package/plugin/commands/create-pr.md +2 -2
- package/plugin/pi-skills/create-pr/SKILL.md +2 -2
- package/src/be/approval-config-question.ts +389 -0
- package/src/be/automation-preflight.ts +37 -3
- package/src/be/db/approvals.ts +48 -0
- package/src/be/db/session-tree.ts +73 -15
- package/src/be/db/skill-search.ts +87 -0
- package/src/be/db/tasks/read.ts +29 -2
- package/src/be/db/tasks/write.ts +15 -5
- package/src/be/db/workflow-idempotency.ts +183 -0
- package/src/be/db/workflow-runs.ts +366 -8
- package/src/be/db.ts +92 -22
- package/src/be/migrations/239_skills_fts.sql +40 -0
- package/src/be/migrations/240_workflow_run_parent_step_attempt.sql +37 -0
- package/src/be/migrations/241_skills_meta_fts.sql +39 -0
- package/src/be/migrations/242_workflow_idempotency_keys.sql +40 -0
- package/src/be/sealed-json.ts +1 -0
- package/src/be/seed-scripts/catalog/compound-insights.inline.ts +2 -2
- package/src/be/seed-scripts/catalog/compound-insights.ts +2 -2
- package/src/be/seed-skills/bundled-files.generated.json +1 -1
- package/src/be/sql-connections.ts +7 -1
- package/src/be/swarm-config-guard.ts +25 -0
- package/src/be/users.ts +9 -0
- package/src/commands/controller.ts +33 -0
- package/src/commands/runner.ts +6 -2
- package/src/extensions/apply-task-create.ts +57 -11
- package/src/extensions/catalog.generated.json +4 -4
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +25 -0
- package/src/extensions/dispatcher.ts +4 -0
- package/src/extensions/post-bridge.ts +8 -0
- package/src/hooks/context-bound-note.ts +196 -0
- package/src/hooks/hook.ts +12 -0
- package/src/http/approval-requests.ts +209 -38
- package/src/http/config.ts +70 -53
- package/src/http/idempotency-key.ts +117 -0
- package/src/http/sessions.ts +27 -2
- package/src/http/workflows.ts +129 -24
- package/src/integrations/chat-sdk/inbound.ts +5 -4
- package/src/prompts/context-bound-note.ts +48 -0
- package/src/prompts/session-templates.ts +25 -1
- package/src/providers/amp-adapter.ts +30 -8
- package/src/providers/claude-adapter.ts +29 -4
- package/src/providers/claude-managed-adapter.ts +26 -0
- package/src/providers/claude-sdk-session.ts +10 -0
- package/src/providers/claude-session-events.ts +13 -0
- package/src/providers/codex-adapter.ts +12 -0
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +18 -0
- package/src/providers/pi-mono-adapter.ts +35 -0
- package/src/runtime-providers/docker.ts +6 -2
- package/src/scripts-runtime/types/swarm-extension.d.ts +35 -0
- package/src/tests/amp-adapter.test.ts +97 -0
- package/src/tests/approval-config-question.test.ts +702 -0
- package/src/tests/approval-requests.test.ts +10 -7
- package/src/tests/approval-sweeps.test.ts +12 -6
- package/src/tests/base-prompt.test.ts +1 -1
- package/src/tests/chat-sdk-inbound.test.ts +20 -0
- package/src/tests/claude-hook-output.test.ts +79 -1
- package/src/tests/claude-managed-adapter.test.ts +275 -0
- package/src/tests/codex-adapter.test.ts +163 -0
- package/src/tests/context-bound-note.test.ts +269 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/entrypoint-gitlab-credential-helper.test.ts +273 -0
- package/src/tests/extensions-example-comms-adherence-gate.test.ts +321 -12
- package/src/tests/extensions-pre-task.test.ts +273 -0
- package/src/tests/fetch-resolved-env.test.ts +17 -0
- package/src/tests/fixtures/extensions/record-blocks/hooks.ts +10 -0
- package/src/tests/fixtures/extensions/record-blocks/manifest.json +7 -0
- package/src/tests/fixtures/fake-amp.ts +22 -8
- package/src/tests/fixtures/held-template-render.ts +42 -0
- package/src/tests/fixtures/workflow-idempotency-trigger.child.ts +73 -0
- package/src/tests/list-endpoint-slimming.test.ts +33 -0
- package/src/tests/migration-240-workflow-run-parent-step-attempt.test.ts +55 -0
- package/src/tests/openapi-route-headers.test.ts +2 -0
- package/src/tests/opencode-adapter.test.ts +134 -0
- package/src/tests/pi-mono-adapter.test.ts +105 -0
- package/src/tests/prompt-template-session.test.ts +4 -3
- package/src/tests/runner-push-delivery.test.ts +25 -0
- package/src/tests/runtime-controller-liveness.test.ts +133 -0
- package/src/tests/runtime-providers-docker.test.ts +2 -0
- package/src/tests/script-connections.test.ts +9 -0
- package/src/tests/script-run-config-scope.test.ts +91 -3
- package/src/tests/session-logs.test.ts +70 -0
- package/src/tests/sessions-tree.test.ts +205 -1
- package/src/tests/skill-search-long-content.test.ts +193 -0
- package/src/tests/skill-search.test.ts +173 -22
- package/src/tests/workflow-cascade-cancel.test.ts +1169 -0
- package/src/tests/workflow-foreach-supersede-retry.test.ts +488 -0
- package/src/tests/workflow-idempotency.test.ts +915 -0
- package/src/tests/workflow-sub-workflow.test.ts +538 -1
- package/src/tools/request-human-input.ts +27 -5
- package/src/tools/workflows/cancel-workflow-run.ts +1 -1
- package/src/types.ts +7 -0
- package/src/utils/context-window.ts +8 -6
- package/src/workflows/approval-cancel.ts +15 -13
- package/src/workflows/approval-notifications.ts +15 -4
- package/src/workflows/checkpoint.ts +72 -35
- package/src/workflows/engine.ts +262 -93
- package/src/workflows/executors/base.ts +6 -0
- package/src/workflows/executors/foreach.ts +2 -2
- package/src/workflows/executors/human-in-the-loop.ts +25 -1
- package/src/workflows/executors/sub-workflow.ts +45 -16
- package/src/workflows/foreach-driver.ts +10 -3
- package/src/workflows/foreach-join.ts +29 -6
- package/src/workflows/recovery.ts +68 -1
- package/src/workflows/resume.ts +228 -77
- package/src/workflows/retry-poller.ts +74 -7
- package/src/workflows/task-step-routing.ts +29 -7
- package/templates/ai-toolbox.manifest.json +8 -8
- package/templates/community/open-prompt-showreel/PLAYBOOK.md +5 -6
- package/templates/community/open-prompt-showreel/lead-prompt.md +1 -1
- package/templates/community/open-prompt-showreel/skills/open-prompt-showreel/SKILL.md +4 -4
- package/templates/extensions/comms-adherence-gate/README.md +38 -13
- package/templates/extensions/comms-adherence-gate/hooks.ts +272 -69
- package/templates/extensions/comms-adherence-gate/manifest.json +2 -2
- package/templates/skills/agent-browser/SKILL.md +4 -4
- package/templates/skills/agent-browser/content.md +4 -4
- package/templates/skills/code-quality/config.json +1 -1
- package/templates/skills/code-quality/content.md +2 -2
- package/templates/skills/delegate-work/content.md +29 -25
- package/templates/skills/delegate-work/files/scripts/codex-exec.sh +5 -5
- package/templates/skills/qa/content.md +2 -2
- package/templates/skills/researching/content.md +2 -0
- package/templates/skills/turso-interaction/content.md +1 -1
- package/templates/skills/work-on-task/SKILL.md +1 -1
- package/templates/skills/work-on-task/content.md +1 -1
- package/templates/skills/workflow-iterate/content.md +8 -1
|
@@ -4,16 +4,15 @@ import {
|
|
|
4
4
|
getApprovalRequestById,
|
|
5
5
|
getDbClient,
|
|
6
6
|
getTaskById,
|
|
7
|
-
getWorkflowRun,
|
|
8
|
-
listCancelledApprovalRequestsForRun,
|
|
9
7
|
resolveApprovalRequest,
|
|
10
8
|
} from "../be/db";
|
|
9
|
+
import { getWorkflowRunStatus } from "../be/db/workflow-runs";
|
|
11
10
|
import { can, type RbacPrincipal } from "../rbac";
|
|
12
11
|
import {
|
|
13
12
|
createApprovalFollowUpTask,
|
|
14
13
|
postApprovalCancellationUpdates,
|
|
15
14
|
} from "./approval-notifications";
|
|
16
|
-
import { cancelWorkflowRunRows } from "./resume";
|
|
15
|
+
import { cancelWorkflowRunRows, postTreeApprovalCancellations } from "./resume";
|
|
17
16
|
|
|
18
17
|
export type CancelApprovalRequestResult =
|
|
19
18
|
| { ok: true; request: ApprovalRequest; alreadyCancelled: boolean; runCancelled: boolean }
|
|
@@ -126,25 +125,28 @@ export async function cancelApprovalRequestAndRun(
|
|
|
126
125
|
if (!request) return null;
|
|
127
126
|
|
|
128
127
|
// The request is cancelled first so it keeps this reason; the run cancel
|
|
129
|
-
// then skips it because it is no longer pending.
|
|
128
|
+
// then skips it because it is no longer pending. The run's child tree is
|
|
129
|
+
// cancelled with it.
|
|
130
130
|
let runCancelled = false;
|
|
131
|
+
let treeRunIds: string[] = [];
|
|
131
132
|
const runId = request.workflowRunId;
|
|
132
133
|
if (runId) {
|
|
133
|
-
const
|
|
134
|
-
if (
|
|
135
|
-
|
|
134
|
+
const status = await getWorkflowRunStatus(runId);
|
|
135
|
+
if (status && LIVE_RUN_STATUSES.has(status)) {
|
|
136
|
+
const result = await cancelWorkflowRunRows(runId, data.reason);
|
|
137
|
+
runCancelled = result.cancelled;
|
|
138
|
+
treeRunIds = result.runIds;
|
|
136
139
|
}
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
const slackReason = data.slackReason ?? data.reason;
|
|
140
143
|
client.afterCommit(async () => {
|
|
141
144
|
await postApprovalCancellationUpdates([request], slackReason);
|
|
142
|
-
if (runCancelled
|
|
143
|
-
// Other requests the
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
);
|
|
147
|
-
await postApprovalCancellationUpdates(others, data.reason);
|
|
145
|
+
if (runCancelled) {
|
|
146
|
+
// Other requests the tree cancel closed get the run's reason.
|
|
147
|
+
await postTreeApprovalCancellations(treeRunIds, data.reason, {
|
|
148
|
+
exceptRequestId: request.id,
|
|
149
|
+
});
|
|
148
150
|
}
|
|
149
151
|
});
|
|
150
152
|
return { request, runCancelled };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatConfigResult } from "../be/approval-config-question";
|
|
1
2
|
import {
|
|
2
3
|
type ApprovalRequest,
|
|
3
4
|
claimApprovalCancellationNotification,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
releaseApprovalCancellationNotificationClaim,
|
|
9
10
|
updateApprovalRequestNotifications,
|
|
10
11
|
} from "../be/db";
|
|
12
|
+
import { approvalCancellationNotificationKey } from "../be/db/approvals";
|
|
11
13
|
import { resolveTemplate } from "../prompts/resolver";
|
|
12
14
|
import { getSlackApp } from "../slack/app";
|
|
13
15
|
import { resolveSlackConversationId } from "../slack/dm";
|
|
@@ -34,11 +36,15 @@ function escapeSlackMrkdwn(text: string): string {
|
|
|
34
36
|
* question stays answerable on the dashboard page the card's button opens.
|
|
35
37
|
*/
|
|
36
38
|
export function buildSlackQuestionsSummary(
|
|
37
|
-
questions: ReadonlyArray<{ label
|
|
39
|
+
questions: ReadonlyArray<{ label?: string; type?: string }>,
|
|
38
40
|
timeoutText = "",
|
|
39
41
|
): string {
|
|
40
42
|
const header = "*Questions:*\n";
|
|
41
|
-
|
|
43
|
+
// A config question is only ever answered on the dashboard, never in Slack.
|
|
44
|
+
const lines = questions.map(
|
|
45
|
+
(q) =>
|
|
46
|
+
`• ${escapeSlackMrkdwn((q.label ?? "").replace(/\s+/g, " ").trim())}${q.type === "config" ? " (set on dashboard)" : ""}`,
|
|
47
|
+
);
|
|
42
48
|
const fits = (shown: string[], omitted: number) => {
|
|
43
49
|
const tail =
|
|
44
50
|
omitted > 0 ? `\n_…and ${omitted} more. Answer all of them on the dashboard._` : "";
|
|
@@ -71,7 +77,7 @@ export interface ApprovalNotificationChannel {
|
|
|
71
77
|
export interface ApprovalNotificationsInput {
|
|
72
78
|
requestId: string;
|
|
73
79
|
title: string;
|
|
74
|
-
questions: ReadonlyArray<{ label
|
|
80
|
+
questions: ReadonlyArray<{ label?: string; type?: string }>;
|
|
75
81
|
timeoutSeconds?: number;
|
|
76
82
|
notifications: ReadonlyArray<ApprovalNotificationChannel>;
|
|
77
83
|
}
|
|
@@ -258,7 +264,10 @@ export async function postApprovalCancellationUpdates(
|
|
|
258
264
|
|
|
259
265
|
for (const notification of slackThreads) {
|
|
260
266
|
if (notification.channel !== "slack" || !notification.messageTs) continue;
|
|
261
|
-
const notificationKey =
|
|
267
|
+
const notificationKey = approvalCancellationNotificationKey(
|
|
268
|
+
notification.target,
|
|
269
|
+
notification.messageTs,
|
|
270
|
+
);
|
|
262
271
|
const claim = await claimApprovalCancellationNotification(approval.id, notificationKey);
|
|
263
272
|
if (!claim) continue;
|
|
264
273
|
try {
|
|
@@ -355,6 +364,8 @@ function formatResponses(
|
|
|
355
364
|
let answerText: string;
|
|
356
365
|
if (answer == null) {
|
|
357
366
|
answerText = "(no answer)";
|
|
367
|
+
} else if (q.type === "config") {
|
|
368
|
+
answerText = formatConfigResult(answer) ?? "(unreadable config result)";
|
|
358
369
|
} else if (q.type === "approval") {
|
|
359
370
|
const a = answer as { approved?: boolean; comment?: string };
|
|
360
371
|
answerText = a.approved ? "Approved" : "Rejected";
|
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
import { getDbClient, getWorkflowRun, updateWorkflowRun, updateWorkflowRunStep } from "../be/db";
|
|
2
|
+
import { getRunLineageBlock } from "../be/db/workflow-runs";
|
|
2
3
|
import type { RetryPolicy } from "../types";
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The execution a checkpoint belongs to: the step attempt (`retryCount`) that
|
|
7
|
+
* claimed the row. A fenced checkpoint writes only while that attempt still
|
|
8
|
+
* owns the row: the step is `running` with that `retryCount` in a `running` or
|
|
9
|
+
* `waiting` run with no cancelled ancestor. A cancel, a newer attempt or
|
|
10
|
+
* another settle that committed meanwhile wins, and the stale execution
|
|
11
|
+
* writes nothing.
|
|
12
|
+
*/
|
|
13
|
+
export type StepFence = { retryCount: number };
|
|
14
|
+
|
|
15
|
+
async function holdsFence(runId: string, stepId: string, fence: StepFence): Promise<boolean> {
|
|
16
|
+
const row = await getDbClient().get<{ found: number }>(
|
|
17
|
+
`SELECT 1 AS found
|
|
18
|
+
FROM workflow_run_steps s
|
|
19
|
+
JOIN workflow_runs r ON r.id = s.runId
|
|
20
|
+
WHERE s.id = ? AND s.runId = ? AND s.status = 'running' AND s.retryCount = ?
|
|
21
|
+
AND r.status IN ('running', 'waiting')`,
|
|
22
|
+
[stepId, runId, fence.retryCount],
|
|
23
|
+
);
|
|
24
|
+
return row != null && (await getRunLineageBlock(runId)) == null;
|
|
25
|
+
}
|
|
26
|
+
|
|
4
27
|
/**
|
|
5
28
|
* Checkpoint a successful step — atomic DB write of step result + run context.
|
|
29
|
+
* Returns false, writing nothing, when `fence` no longer holds.
|
|
6
30
|
*/
|
|
7
31
|
export async function checkpointStep(
|
|
8
32
|
runId: string,
|
|
@@ -10,8 +34,10 @@ export async function checkpointStep(
|
|
|
10
34
|
nodeId: string,
|
|
11
35
|
result: { output?: unknown; nextPort?: string },
|
|
12
36
|
ctx: Record<string, unknown>,
|
|
13
|
-
|
|
14
|
-
|
|
37
|
+
options: { fence?: StepFence } = {},
|
|
38
|
+
): Promise<boolean> {
|
|
39
|
+
return getDbClient().transaction(async () => {
|
|
40
|
+
if (options.fence && !(await holdsFence(runId, stepId, options.fence))) return false;
|
|
15
41
|
await updateWorkflowRunStep(stepId, {
|
|
16
42
|
status: "completed",
|
|
17
43
|
output: result.output,
|
|
@@ -24,11 +50,14 @@ export async function checkpointStep(
|
|
|
24
50
|
await updateWorkflowRun(runId, {
|
|
25
51
|
context: ctx,
|
|
26
52
|
});
|
|
53
|
+
return true;
|
|
27
54
|
});
|
|
28
55
|
}
|
|
29
56
|
|
|
30
57
|
/**
|
|
31
58
|
* Checkpoint a step failure — marks step failed, calculates retry if applicable.
|
|
59
|
+
* With `fence`, a stale execution writes nothing and gets `stale: true`.
|
|
60
|
+
* `output` is kept on the failed step for observability, in the same write.
|
|
32
61
|
*/
|
|
33
62
|
export async function checkpointStepFailure(
|
|
34
63
|
runId: string,
|
|
@@ -36,49 +65,56 @@ export async function checkpointStepFailure(
|
|
|
36
65
|
error: string,
|
|
37
66
|
retryCount: number,
|
|
38
67
|
retryPolicy?: RetryPolicy,
|
|
39
|
-
options?: { markRunFailed?: boolean },
|
|
40
|
-
): Promise<{ shouldRetry: boolean }> {
|
|
68
|
+
options?: { markRunFailed?: boolean; fence?: StepFence; output?: unknown },
|
|
69
|
+
): Promise<{ shouldRetry: boolean; stale?: true }> {
|
|
41
70
|
const now = new Date().toISOString();
|
|
42
71
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
72
|
+
return getDbClient().transaction(async () => {
|
|
73
|
+
if (options?.fence && !(await holdsFence(runId, stepId, options.fence))) {
|
|
74
|
+
return { shouldRetry: false, stale: true as const };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (retryPolicy && retryCount < retryPolicy.maxRetries) {
|
|
78
|
+
const delay = calculateBackoff(retryPolicy, retryCount);
|
|
79
|
+
const nextRetryAt = new Date(Date.now() + delay).toISOString();
|
|
80
|
+
|
|
81
|
+
await updateWorkflowRunStep(stepId, {
|
|
82
|
+
status: "failed",
|
|
83
|
+
error,
|
|
84
|
+
output: options?.output,
|
|
85
|
+
retryCount: retryCount + 1,
|
|
86
|
+
maxRetries: retryPolicy.maxRetries,
|
|
87
|
+
nextRetryAt,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return { shouldRetry: true };
|
|
91
|
+
}
|
|
46
92
|
|
|
93
|
+
// No retries left — mark step failed, and optionally the run too
|
|
94
|
+
// Clear nextRetryAt so the poller stops picking this step up
|
|
47
95
|
await updateWorkflowRunStep(stepId, {
|
|
48
96
|
status: "failed",
|
|
49
97
|
error,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
nextRetryAt,
|
|
98
|
+
output: options?.output,
|
|
99
|
+
finishedAt: now,
|
|
100
|
+
nextRetryAt: null,
|
|
53
101
|
});
|
|
54
102
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// No retries left — mark step failed, and optionally the run too
|
|
59
|
-
// Clear nextRetryAt so the poller stops picking this step up
|
|
60
|
-
await updateWorkflowRunStep(stepId, {
|
|
61
|
-
status: "failed",
|
|
62
|
-
error,
|
|
63
|
-
finishedAt: now,
|
|
64
|
-
nextRetryAt: null,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const markRunFailed = options?.markRunFailed ?? true;
|
|
68
|
-
if (markRunFailed) {
|
|
69
|
-
// A cancel or finalization that committed while the step ran wins.
|
|
70
|
-
await getDbClient().transaction(async () => {
|
|
103
|
+
const markRunFailed = options?.markRunFailed ?? true;
|
|
104
|
+
if (markRunFailed) {
|
|
105
|
+
// A cancel or finalization that committed while the step ran wins.
|
|
71
106
|
const run = await getWorkflowRun(runId);
|
|
72
|
-
if (run?.status
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
if (run?.status === "running" || run?.status === "waiting") {
|
|
108
|
+
await updateWorkflowRun(runId, {
|
|
109
|
+
status: "failed",
|
|
110
|
+
error: `Step failed: ${error}`,
|
|
111
|
+
finishedAt: now,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
80
115
|
|
|
81
|
-
|
|
116
|
+
return { shouldRetry: false };
|
|
117
|
+
});
|
|
82
118
|
}
|
|
83
119
|
|
|
84
120
|
/**
|
|
@@ -95,6 +131,7 @@ export async function checkpointStepWaiting(
|
|
|
95
131
|
options: { keepRunStatus?: boolean } = {},
|
|
96
132
|
): Promise<boolean> {
|
|
97
133
|
return getDbClient().transaction(async () => {
|
|
134
|
+
if (await getRunLineageBlock(runId)) return false;
|
|
98
135
|
const step = await getDbClient().get<{ id: string }>(
|
|
99
136
|
`UPDATE workflow_run_steps
|
|
100
137
|
SET status = 'waiting'
|