@bratsos/workflow-engine 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -13
- package/dist/{chunk-RZY5YRGL.js → chunk-2MWO6UVR.js} +2 -2
- package/dist/chunk-2MWO6UVR.js.map +1 -0
- package/dist/{chunk-PHLNTR5Z.js → chunk-DIADEUGZ.js} +21 -3
- package/dist/chunk-DIADEUGZ.js.map +1 -0
- package/dist/{chunk-ZYMT2PAO.js → chunk-HKGZ2WHJ.js} +2 -30
- package/dist/chunk-HKGZ2WHJ.js.map +1 -0
- package/dist/{chunk-3NEGRI4M.js → chunk-HOGDFLCG.js} +342 -114
- package/dist/chunk-HOGDFLCG.js.map +1 -0
- package/dist/{client-oLD5ilXp.d.ts → client-llB6XpHS.d.ts} +10 -81
- package/dist/client.d.ts +4 -3
- package/dist/client.js +1 -1
- package/dist/events-D_P24UaY.d.ts +105 -0
- package/dist/{index-CVkkGnxx.d.ts → index-sGgV8JNu.d.ts} +5 -1
- package/dist/index.d.ts +184 -32
- package/dist/index.js +41 -9
- package/dist/index.js.map +1 -1
- package/dist/{interface-TsryH4d7.d.ts → interface-DCdddCe0.d.ts} +7 -2
- package/dist/kernel/index.d.ts +6 -5
- package/dist/kernel/index.js +2 -1
- package/dist/kernel/testing/index.d.ts +3 -2
- package/dist/persistence/index.d.ts +2 -2
- package/dist/persistence/index.js +2 -2
- package/dist/persistence/prisma/index.d.ts +2 -2
- package/dist/persistence/prisma/index.js +2 -2
- package/dist/{plugins-DW266bhT.d.ts → plugins-Oyo_iu0l.d.ts} +16 -10
- package/dist/{ports-855bktyD.d.ts → ports-ChGnJcn2.d.ts} +5 -106
- package/dist/{stage-BPw7m9Wx.d.ts → stage-_7BKqqUG.d.ts} +2 -2
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.js +23 -5
- package/dist/testing/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/workflow-engine/SKILL.md +31 -12
- package/skills/workflow-engine/references/02-workflow-builder.md +2 -0
- package/skills/workflow-engine/references/03-runtime-setup.md +1 -1
- package/skills/workflow-engine/references/08-common-patterns.md +17 -3
- package/skills/workflow-engine/references/09-troubleshooting.md +12 -3
- package/dist/chunk-3NEGRI4M.js.map +0 -1
- package/dist/chunk-PHLNTR5Z.js.map +0 -1
- package/dist/chunk-RZY5YRGL.js.map +0 -1
- package/dist/chunk-ZYMT2PAO.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { StaleVersionError } from './chunk-2MWO6UVR.js';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
|
|
3
|
-
// src/core/types.ts
|
|
4
4
|
z.object({
|
|
5
5
|
batchId: z.string(),
|
|
6
6
|
statusUrl: z.string().optional(),
|
|
@@ -78,15 +78,44 @@ function createStorageShim(workflowRunId, workflowType, deps) {
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// src/kernel/helpers/resolve-execution-group-output.ts
|
|
82
|
+
function resolveExecutionGroupOutput(workflow, groupIndex, workflowContext) {
|
|
83
|
+
const stages = workflow.getStagesInExecutionGroup(groupIndex);
|
|
84
|
+
if (stages.length === 0) return void 0;
|
|
85
|
+
if (stages.length === 1) {
|
|
86
|
+
return workflowContext[stages[0].id];
|
|
87
|
+
}
|
|
88
|
+
const merged = {};
|
|
89
|
+
for (const stage of stages) {
|
|
90
|
+
if (workflowContext[stage.id] !== void 0) {
|
|
91
|
+
merged[stage.id] = workflowContext[stage.id];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return merged;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/kernel/helpers/save-stage-artifacts.ts
|
|
98
|
+
async function saveStageArtifacts(runId, workflowType, stageId, artifacts, deps) {
|
|
99
|
+
const artifactKeys = {};
|
|
100
|
+
for (const [artifactName, artifact] of Object.entries(artifacts)) {
|
|
101
|
+
const encodedName = encodeURIComponent(artifactName);
|
|
102
|
+
const key = `workflow-v2/${workflowType}/${runId}/${stageId}/artifacts/${encodedName}.json`;
|
|
103
|
+
await deps.blobStore.put(key, artifact);
|
|
104
|
+
artifactKeys[artifactName] = key;
|
|
105
|
+
}
|
|
106
|
+
return artifactKeys;
|
|
107
|
+
}
|
|
108
|
+
|
|
81
109
|
// src/kernel/handlers/job-execute.ts
|
|
82
110
|
function resolveStageInput(workflow, stageId, workflowRun, workflowContext) {
|
|
83
111
|
const groupIndex = workflow.getExecutionGroupIndex(stageId);
|
|
84
|
-
if (groupIndex
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
112
|
+
if (groupIndex <= 1) return workflowRun.input;
|
|
113
|
+
const prevOutput = resolveExecutionGroupOutput(
|
|
114
|
+
workflow,
|
|
115
|
+
groupIndex - 1,
|
|
116
|
+
workflowContext
|
|
117
|
+
);
|
|
118
|
+
return prevOutput ?? workflowRun.input;
|
|
90
119
|
}
|
|
91
120
|
function toOutboxEvents(workflowRunId, causationId, events) {
|
|
92
121
|
return events.map((event) => ({
|
|
@@ -112,6 +141,7 @@ async function handleJobExecute(command, deps) {
|
|
|
112
141
|
if (workflowRun.status !== "RUNNING") {
|
|
113
142
|
return {
|
|
114
143
|
outcome: "failed",
|
|
144
|
+
ghost: true,
|
|
115
145
|
error: `Run ${workflowRunId} is ${workflowRun.status}, expected RUNNING \u2014 ghost job discarded`,
|
|
116
146
|
_events: []
|
|
117
147
|
};
|
|
@@ -202,6 +232,15 @@ async function handleJobExecute(command, deps) {
|
|
|
202
232
|
workflowContext
|
|
203
233
|
};
|
|
204
234
|
const result = await stageDef.execute(context);
|
|
235
|
+
const currentRunStatus = await deps.persistence.getRunStatus(workflowRunId);
|
|
236
|
+
if (currentRunStatus !== "RUNNING") {
|
|
237
|
+
return {
|
|
238
|
+
outcome: "failed",
|
|
239
|
+
ghost: true,
|
|
240
|
+
error: `Run ${workflowRunId} was ${currentRunStatus} after stage execution \u2014 result discarded`,
|
|
241
|
+
_events: []
|
|
242
|
+
};
|
|
243
|
+
}
|
|
205
244
|
if (isSuspendedResult(result)) {
|
|
206
245
|
const { state, pollConfig, metrics } = result;
|
|
207
246
|
const nextPollAt = new Date(
|
|
@@ -241,12 +280,22 @@ async function handleJobExecute(command, deps) {
|
|
|
241
280
|
result.output,
|
|
242
281
|
deps
|
|
243
282
|
);
|
|
283
|
+
const artifactKeys = result.artifacts && Object.keys(result.artifacts).length > 0 ? await saveStageArtifacts(
|
|
284
|
+
workflowRunId,
|
|
285
|
+
workflowRun.workflowType,
|
|
286
|
+
stageId,
|
|
287
|
+
result.artifacts,
|
|
288
|
+
deps
|
|
289
|
+
) : void 0;
|
|
244
290
|
await deps.persistence.withTransaction(async (tx) => {
|
|
245
291
|
await tx.updateStage(stageRecord.id, {
|
|
246
292
|
status: "COMPLETED",
|
|
247
293
|
completedAt: deps.clock.now(),
|
|
248
294
|
duration,
|
|
249
|
-
outputData: {
|
|
295
|
+
outputData: {
|
|
296
|
+
_artifactKey: outputKey,
|
|
297
|
+
...artifactKeys ? { _artifactKeys: artifactKeys } : {}
|
|
298
|
+
},
|
|
250
299
|
metrics: result.metrics,
|
|
251
300
|
embeddingInfo: result.embeddings
|
|
252
301
|
});
|
|
@@ -362,6 +411,17 @@ async function handleRunCancel(command, deps) {
|
|
|
362
411
|
status: "CANCELLED",
|
|
363
412
|
completedAt: deps.clock.now()
|
|
364
413
|
});
|
|
414
|
+
const stages = await deps.persistence.getStagesByRun(command.workflowRunId);
|
|
415
|
+
for (const stage of stages) {
|
|
416
|
+
if (!TERMINAL_STATUSES.has(stage.status)) {
|
|
417
|
+
await deps.persistence.updateStage(stage.id, {
|
|
418
|
+
status: "CANCELLED",
|
|
419
|
+
completedAt: deps.clock.now(),
|
|
420
|
+
nextPollAt: null
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
await deps.jobTransport.cancelByRun(command.workflowRunId);
|
|
365
425
|
return {
|
|
366
426
|
cancelled: true,
|
|
367
427
|
_events: [
|
|
@@ -644,9 +704,10 @@ async function handleRunRerunFrom(command, deps) {
|
|
|
644
704
|
);
|
|
645
705
|
const deletedStageIds = stagesToDelete.map((s) => s.stageId);
|
|
646
706
|
for (const stage of stagesToDelete) {
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
707
|
+
const prefix = `workflow-v2/${run.workflowType}/${workflowRunId}/${stage.stageId}/`;
|
|
708
|
+
const keys = await deps.blobStore.list(prefix).catch(() => []);
|
|
709
|
+
for (const key of keys) {
|
|
710
|
+
await deps.blobStore.delete(key).catch(() => {
|
|
650
711
|
});
|
|
651
712
|
}
|
|
652
713
|
}
|
|
@@ -655,7 +716,12 @@ async function handleRunRerunFrom(command, deps) {
|
|
|
655
716
|
}
|
|
656
717
|
await deps.persistence.updateRun(workflowRunId, {
|
|
657
718
|
status: "RUNNING",
|
|
658
|
-
|
|
719
|
+
startedAt: deps.clock.now(),
|
|
720
|
+
completedAt: null,
|
|
721
|
+
duration: null,
|
|
722
|
+
output: null,
|
|
723
|
+
totalCost: 0,
|
|
724
|
+
totalTokens: 0
|
|
659
725
|
});
|
|
660
726
|
const targetStages = workflow.getStagesInExecutionGroup(targetGroup);
|
|
661
727
|
for (const stage of targetStages) {
|
|
@@ -694,6 +760,19 @@ async function handleRunRerunFrom(command, deps) {
|
|
|
694
760
|
// src/kernel/handlers/run-transition.ts
|
|
695
761
|
var TERMINAL_STATUSES2 = /* @__PURE__ */ new Set(["COMPLETED", "FAILED", "CANCELLED"]);
|
|
696
762
|
var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["RUNNING", "PENDING", "SUSPENDED"]);
|
|
763
|
+
async function claimRunTransition(run, deps) {
|
|
764
|
+
try {
|
|
765
|
+
await deps.persistence.updateRun(run.id, {
|
|
766
|
+
expectedVersion: run.version
|
|
767
|
+
});
|
|
768
|
+
return true;
|
|
769
|
+
} catch (error) {
|
|
770
|
+
if (error instanceof StaleVersionError) {
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
throw error;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
697
776
|
async function enqueueExecutionGroup(run, workflow, groupIndex, deps) {
|
|
698
777
|
const stages = workflow.getStagesInExecutionGroup(groupIndex);
|
|
699
778
|
const stagesToEnqueue = [];
|
|
@@ -742,6 +821,9 @@ async function handleRunTransition(command, deps) {
|
|
|
742
821
|
}
|
|
743
822
|
const stages = await deps.persistence.getStagesByRun(command.workflowRunId);
|
|
744
823
|
if (stages.length === 0) {
|
|
824
|
+
if (!await claimRunTransition(run, deps)) {
|
|
825
|
+
return { action: "noop", _events: [] };
|
|
826
|
+
}
|
|
745
827
|
await enqueueExecutionGroup(run, workflow, 1, deps);
|
|
746
828
|
events.push({
|
|
747
829
|
type: "workflow:started",
|
|
@@ -756,6 +838,9 @@ async function handleRunTransition(command, deps) {
|
|
|
756
838
|
}
|
|
757
839
|
const failedStage = stages.find((s) => s.status === "FAILED");
|
|
758
840
|
if (failedStage) {
|
|
841
|
+
if (!await claimRunTransition(run, deps)) {
|
|
842
|
+
return { action: "noop", _events: [] };
|
|
843
|
+
}
|
|
759
844
|
await deps.persistence.updateRun(command.workflowRunId, {
|
|
760
845
|
status: "FAILED",
|
|
761
846
|
completedAt: deps.clock.now()
|
|
@@ -774,6 +859,9 @@ async function handleRunTransition(command, deps) {
|
|
|
774
859
|
);
|
|
775
860
|
const nextGroupStages = workflow.getStagesInExecutionGroup(maxGroup + 1);
|
|
776
861
|
if (nextGroupStages.length > 0) {
|
|
862
|
+
if (!await claimRunTransition(run, deps)) {
|
|
863
|
+
return { action: "noop", _events: [] };
|
|
864
|
+
}
|
|
777
865
|
await enqueueExecutionGroup(run, workflow, maxGroup + 1, deps);
|
|
778
866
|
return {
|
|
779
867
|
action: "advanced",
|
|
@@ -791,10 +879,23 @@ async function handleRunTransition(command, deps) {
|
|
|
791
879
|
}
|
|
792
880
|
}
|
|
793
881
|
const duration = deps.clock.now().getTime() - run.createdAt.getTime();
|
|
882
|
+
const workflowContext = await loadWorkflowContext(
|
|
883
|
+
command.workflowRunId,
|
|
884
|
+
deps
|
|
885
|
+
);
|
|
886
|
+
const output = resolveExecutionGroupOutput(
|
|
887
|
+
workflow,
|
|
888
|
+
maxGroup,
|
|
889
|
+
workflowContext
|
|
890
|
+
);
|
|
891
|
+
if (!await claimRunTransition(run, deps)) {
|
|
892
|
+
return { action: "noop", _events: [] };
|
|
893
|
+
}
|
|
794
894
|
await deps.persistence.updateRun(command.workflowRunId, {
|
|
795
895
|
status: "COMPLETED",
|
|
796
896
|
completedAt: deps.clock.now(),
|
|
797
897
|
duration,
|
|
898
|
+
output,
|
|
798
899
|
totalCost,
|
|
799
900
|
totalTokens
|
|
800
901
|
});
|
|
@@ -803,6 +904,7 @@ async function handleRunTransition(command, deps) {
|
|
|
803
904
|
timestamp: deps.clock.now(),
|
|
804
905
|
workflowRunId: command.workflowRunId,
|
|
805
906
|
duration,
|
|
907
|
+
output,
|
|
806
908
|
totalCost,
|
|
807
909
|
totalTokens
|
|
808
910
|
});
|
|
@@ -810,8 +912,40 @@ async function handleRunTransition(command, deps) {
|
|
|
810
912
|
}
|
|
811
913
|
|
|
812
914
|
// src/kernel/handlers/stage-poll-suspended.ts
|
|
915
|
+
function toOutboxEvents2(workflowRunId, events) {
|
|
916
|
+
const causationId = crypto.randomUUID();
|
|
917
|
+
return events.map((event) => ({
|
|
918
|
+
workflowRunId,
|
|
919
|
+
eventType: event.type,
|
|
920
|
+
payload: event,
|
|
921
|
+
causationId,
|
|
922
|
+
occurredAt: event.timestamp
|
|
923
|
+
}));
|
|
924
|
+
}
|
|
925
|
+
async function markStageCancelled(stageId, deps) {
|
|
926
|
+
await deps.persistence.updateStage(stageId, {
|
|
927
|
+
status: "CANCELLED",
|
|
928
|
+
completedAt: deps.clock.now(),
|
|
929
|
+
nextPollAt: null
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
async function withClaimedRun(workflowRunId, expectedVersion, deps, fn) {
|
|
933
|
+
try {
|
|
934
|
+
const value = await deps.persistence.withTransaction(async (tx) => {
|
|
935
|
+
await tx.updateRun(workflowRunId, {
|
|
936
|
+
expectedVersion
|
|
937
|
+
});
|
|
938
|
+
return fn(tx);
|
|
939
|
+
});
|
|
940
|
+
return { status: "claimed", value };
|
|
941
|
+
} catch (error) {
|
|
942
|
+
if (error instanceof StaleVersionError) {
|
|
943
|
+
return { status: "stale" };
|
|
944
|
+
}
|
|
945
|
+
throw error;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
813
948
|
async function handleStagePollSuspended(command, deps) {
|
|
814
|
-
const events = [];
|
|
815
949
|
const maxChecks = command.maxChecks ?? 50;
|
|
816
950
|
const suspendedStages = await deps.persistence.getSuspendedStages(
|
|
817
951
|
deps.clock.now()
|
|
@@ -825,41 +959,57 @@ async function handleStagePollSuspended(command, deps) {
|
|
|
825
959
|
checked++;
|
|
826
960
|
const run = await deps.persistence.getRun(stageRecord.workflowRunId);
|
|
827
961
|
if (!run) continue;
|
|
962
|
+
if (run.status === "CANCELLED") {
|
|
963
|
+
await markStageCancelled(stageRecord.id, deps);
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
828
966
|
const workflow = deps.registry.getWorkflow(run.workflowId);
|
|
829
967
|
if (!workflow) {
|
|
830
|
-
await deps.persistence.
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
968
|
+
await deps.persistence.withTransaction(async (tx) => {
|
|
969
|
+
await tx.updateStage(stageRecord.id, {
|
|
970
|
+
status: "FAILED",
|
|
971
|
+
completedAt: deps.clock.now(),
|
|
972
|
+
errorMessage: `Workflow ${run.workflowId} not found in registry`
|
|
973
|
+
});
|
|
974
|
+
await tx.appendOutboxEvents(
|
|
975
|
+
toOutboxEvents2(stageRecord.workflowRunId, [
|
|
976
|
+
{
|
|
977
|
+
type: "stage:failed",
|
|
978
|
+
timestamp: deps.clock.now(),
|
|
979
|
+
workflowRunId: stageRecord.workflowRunId,
|
|
980
|
+
stageId: stageRecord.stageId,
|
|
981
|
+
stageName: stageRecord.stageName,
|
|
982
|
+
error: `Workflow ${run.workflowId} not found in registry`
|
|
983
|
+
}
|
|
984
|
+
])
|
|
985
|
+
);
|
|
834
986
|
});
|
|
835
987
|
failed++;
|
|
836
|
-
events.push({
|
|
837
|
-
type: "stage:failed",
|
|
838
|
-
timestamp: deps.clock.now(),
|
|
839
|
-
workflowRunId: stageRecord.workflowRunId,
|
|
840
|
-
stageId: stageRecord.stageId,
|
|
841
|
-
stageName: stageRecord.stageName,
|
|
842
|
-
error: `Workflow ${run.workflowId} not found in registry`
|
|
843
|
-
});
|
|
844
988
|
continue;
|
|
845
989
|
}
|
|
846
990
|
const stageDef = workflow.getStage(stageRecord.stageId);
|
|
847
991
|
if (!stageDef || !stageDef.checkCompletion) {
|
|
848
992
|
const errorMsg = !stageDef ? `Stage ${stageRecord.stageId} not found in workflow ${run.workflowId}` : `Stage ${stageRecord.stageId} does not support checkCompletion`;
|
|
849
|
-
await deps.persistence.
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
993
|
+
await deps.persistence.withTransaction(async (tx) => {
|
|
994
|
+
await tx.updateStage(stageRecord.id, {
|
|
995
|
+
status: "FAILED",
|
|
996
|
+
completedAt: deps.clock.now(),
|
|
997
|
+
errorMessage: errorMsg
|
|
998
|
+
});
|
|
999
|
+
await tx.appendOutboxEvents(
|
|
1000
|
+
toOutboxEvents2(stageRecord.workflowRunId, [
|
|
1001
|
+
{
|
|
1002
|
+
type: "stage:failed",
|
|
1003
|
+
timestamp: deps.clock.now(),
|
|
1004
|
+
workflowRunId: stageRecord.workflowRunId,
|
|
1005
|
+
stageId: stageRecord.stageId,
|
|
1006
|
+
stageName: stageRecord.stageName,
|
|
1007
|
+
error: errorMsg
|
|
1008
|
+
}
|
|
1009
|
+
])
|
|
1010
|
+
);
|
|
853
1011
|
});
|
|
854
1012
|
failed++;
|
|
855
|
-
events.push({
|
|
856
|
-
type: "stage:failed",
|
|
857
|
-
timestamp: deps.clock.now(),
|
|
858
|
-
workflowRunId: stageRecord.workflowRunId,
|
|
859
|
-
stageId: stageRecord.stageId,
|
|
860
|
-
stageName: stageRecord.stageName,
|
|
861
|
-
error: errorMsg
|
|
862
|
-
});
|
|
863
1013
|
continue;
|
|
864
1014
|
}
|
|
865
1015
|
const storage = createStorageShim(
|
|
@@ -892,31 +1042,51 @@ async function handleStagePollSuspended(command, deps) {
|
|
|
892
1042
|
checkContext
|
|
893
1043
|
);
|
|
894
1044
|
if (checkResult.error) {
|
|
895
|
-
await
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1045
|
+
const claimResult = await withClaimedRun(
|
|
1046
|
+
stageRecord.workflowRunId,
|
|
1047
|
+
run.version,
|
|
1048
|
+
deps,
|
|
1049
|
+
async (tx) => {
|
|
1050
|
+
await tx.updateStage(stageRecord.id, {
|
|
1051
|
+
status: "FAILED",
|
|
1052
|
+
completedAt: deps.clock.now(),
|
|
1053
|
+
errorMessage: checkResult.error,
|
|
1054
|
+
nextPollAt: null
|
|
1055
|
+
});
|
|
1056
|
+
await tx.updateRun(stageRecord.workflowRunId, {
|
|
1057
|
+
status: "FAILED",
|
|
1058
|
+
completedAt: deps.clock.now()
|
|
1059
|
+
});
|
|
1060
|
+
await tx.appendOutboxEvents(
|
|
1061
|
+
toOutboxEvents2(stageRecord.workflowRunId, [
|
|
1062
|
+
{
|
|
1063
|
+
type: "stage:failed",
|
|
1064
|
+
timestamp: deps.clock.now(),
|
|
1065
|
+
workflowRunId: stageRecord.workflowRunId,
|
|
1066
|
+
stageId: stageRecord.stageId,
|
|
1067
|
+
stageName: stageRecord.stageName,
|
|
1068
|
+
error: checkResult.error
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
type: "workflow:failed",
|
|
1072
|
+
timestamp: deps.clock.now(),
|
|
1073
|
+
workflowRunId: stageRecord.workflowRunId,
|
|
1074
|
+
error: checkResult.error
|
|
1075
|
+
}
|
|
1076
|
+
])
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
1080
|
+
if (claimResult.status === "stale") {
|
|
1081
|
+
const latestStatus = await deps.persistence.getRunStatus(
|
|
1082
|
+
stageRecord.workflowRunId
|
|
1083
|
+
);
|
|
1084
|
+
if (latestStatus === "CANCELLED") {
|
|
1085
|
+
await markStageCancelled(stageRecord.id, deps);
|
|
1086
|
+
}
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
905
1089
|
failed++;
|
|
906
|
-
events.push({
|
|
907
|
-
type: "stage:failed",
|
|
908
|
-
timestamp: deps.clock.now(),
|
|
909
|
-
workflowRunId: stageRecord.workflowRunId,
|
|
910
|
-
stageId: stageRecord.stageId,
|
|
911
|
-
stageName: stageRecord.stageName,
|
|
912
|
-
error: checkResult.error
|
|
913
|
-
});
|
|
914
|
-
events.push({
|
|
915
|
-
type: "workflow:failed",
|
|
916
|
-
timestamp: deps.clock.now(),
|
|
917
|
-
workflowRunId: stageRecord.workflowRunId,
|
|
918
|
-
error: checkResult.error
|
|
919
|
-
});
|
|
920
1090
|
} else if (checkResult.ready) {
|
|
921
1091
|
let outputRef;
|
|
922
1092
|
if (checkResult.output !== void 0) {
|
|
@@ -935,59 +1105,115 @@ async function handleStagePollSuspended(command, deps) {
|
|
|
935
1105
|
outputRef = { _artifactKey: outputKey };
|
|
936
1106
|
}
|
|
937
1107
|
const duration = deps.clock.now().getTime() - (stageRecord.startedAt?.getTime() ?? deps.clock.now().getTime());
|
|
938
|
-
await
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1108
|
+
const claimResult = await withClaimedRun(
|
|
1109
|
+
stageRecord.workflowRunId,
|
|
1110
|
+
run.version,
|
|
1111
|
+
deps,
|
|
1112
|
+
async (tx) => {
|
|
1113
|
+
await tx.updateStage(stageRecord.id, {
|
|
1114
|
+
status: "COMPLETED",
|
|
1115
|
+
completedAt: deps.clock.now(),
|
|
1116
|
+
duration,
|
|
1117
|
+
outputData: outputRef,
|
|
1118
|
+
nextPollAt: null,
|
|
1119
|
+
metrics: checkResult.metrics,
|
|
1120
|
+
embeddingInfo: checkResult.embeddings
|
|
1121
|
+
});
|
|
1122
|
+
await tx.appendOutboxEvents(
|
|
1123
|
+
toOutboxEvents2(stageRecord.workflowRunId, [
|
|
1124
|
+
{
|
|
1125
|
+
type: "stage:completed",
|
|
1126
|
+
timestamp: deps.clock.now(),
|
|
1127
|
+
workflowRunId: stageRecord.workflowRunId,
|
|
1128
|
+
stageId: stageRecord.stageId,
|
|
1129
|
+
stageName: stageRecord.stageName,
|
|
1130
|
+
duration
|
|
1131
|
+
}
|
|
1132
|
+
])
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
);
|
|
1136
|
+
if (claimResult.status === "stale") {
|
|
1137
|
+
const latestStatus = await deps.persistence.getRunStatus(
|
|
1138
|
+
stageRecord.workflowRunId
|
|
1139
|
+
);
|
|
1140
|
+
if (latestStatus === "CANCELLED") {
|
|
1141
|
+
await markStageCancelled(stageRecord.id, deps);
|
|
1142
|
+
}
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
947
1145
|
resumed++;
|
|
948
1146
|
resumedWorkflowRunIds.add(stageRecord.workflowRunId);
|
|
949
|
-
events.push({
|
|
950
|
-
type: "stage:completed",
|
|
951
|
-
timestamp: deps.clock.now(),
|
|
952
|
-
workflowRunId: stageRecord.workflowRunId,
|
|
953
|
-
stageId: stageRecord.stageId,
|
|
954
|
-
stageName: stageRecord.stageName,
|
|
955
|
-
duration
|
|
956
|
-
});
|
|
957
1147
|
} else {
|
|
958
1148
|
const pollInterval = checkResult.nextCheckIn ?? stageRecord.pollInterval ?? 6e4;
|
|
959
1149
|
const nextPollAt = new Date(deps.clock.now().getTime() + pollInterval);
|
|
960
|
-
await
|
|
961
|
-
|
|
962
|
-
|
|
1150
|
+
const claimResult = await withClaimedRun(
|
|
1151
|
+
stageRecord.workflowRunId,
|
|
1152
|
+
run.version,
|
|
1153
|
+
deps,
|
|
1154
|
+
async (tx) => {
|
|
1155
|
+
await tx.updateStage(stageRecord.id, {
|
|
1156
|
+
nextPollAt
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
);
|
|
1160
|
+
if (claimResult.status === "stale") {
|
|
1161
|
+
const latestStatus = await deps.persistence.getRunStatus(
|
|
1162
|
+
stageRecord.workflowRunId
|
|
1163
|
+
);
|
|
1164
|
+
if (latestStatus === "CANCELLED") {
|
|
1165
|
+
await markStageCancelled(stageRecord.id, deps);
|
|
1166
|
+
}
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
963
1169
|
}
|
|
964
1170
|
} catch (error) {
|
|
965
1171
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
966
|
-
await
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1172
|
+
const claimResult = await withClaimedRun(
|
|
1173
|
+
stageRecord.workflowRunId,
|
|
1174
|
+
run.version,
|
|
1175
|
+
deps,
|
|
1176
|
+
async (tx) => {
|
|
1177
|
+
await tx.updateStage(stageRecord.id, {
|
|
1178
|
+
status: "FAILED",
|
|
1179
|
+
completedAt: deps.clock.now(),
|
|
1180
|
+
errorMessage,
|
|
1181
|
+
nextPollAt: null
|
|
1182
|
+
});
|
|
1183
|
+
await tx.updateRun(stageRecord.workflowRunId, {
|
|
1184
|
+
status: "FAILED",
|
|
1185
|
+
completedAt: deps.clock.now()
|
|
1186
|
+
});
|
|
1187
|
+
await tx.appendOutboxEvents(
|
|
1188
|
+
toOutboxEvents2(stageRecord.workflowRunId, [
|
|
1189
|
+
{
|
|
1190
|
+
type: "stage:failed",
|
|
1191
|
+
timestamp: deps.clock.now(),
|
|
1192
|
+
workflowRunId: stageRecord.workflowRunId,
|
|
1193
|
+
stageId: stageRecord.stageId,
|
|
1194
|
+
stageName: stageRecord.stageName,
|
|
1195
|
+
error: errorMessage
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
type: "workflow:failed",
|
|
1199
|
+
timestamp: deps.clock.now(),
|
|
1200
|
+
workflowRunId: stageRecord.workflowRunId,
|
|
1201
|
+
error: errorMessage
|
|
1202
|
+
}
|
|
1203
|
+
])
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
);
|
|
1207
|
+
if (claimResult.status === "stale") {
|
|
1208
|
+
const latestStatus = await deps.persistence.getRunStatus(
|
|
1209
|
+
stageRecord.workflowRunId
|
|
1210
|
+
);
|
|
1211
|
+
if (latestStatus === "CANCELLED") {
|
|
1212
|
+
await markStageCancelled(stageRecord.id, deps);
|
|
1213
|
+
}
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
976
1216
|
failed++;
|
|
977
|
-
events.push({
|
|
978
|
-
type: "stage:failed",
|
|
979
|
-
timestamp: deps.clock.now(),
|
|
980
|
-
workflowRunId: stageRecord.workflowRunId,
|
|
981
|
-
stageId: stageRecord.stageId,
|
|
982
|
-
stageName: stageRecord.stageName,
|
|
983
|
-
error: errorMessage
|
|
984
|
-
});
|
|
985
|
-
events.push({
|
|
986
|
-
type: "workflow:failed",
|
|
987
|
-
timestamp: deps.clock.now(),
|
|
988
|
-
workflowRunId: stageRecord.workflowRunId,
|
|
989
|
-
error: errorMessage
|
|
990
|
-
});
|
|
991
1217
|
}
|
|
992
1218
|
}
|
|
993
1219
|
return {
|
|
@@ -995,7 +1221,7 @@ async function handleStagePollSuspended(command, deps) {
|
|
|
995
1221
|
resumed,
|
|
996
1222
|
failed,
|
|
997
1223
|
resumedWorkflowRunIds: [...resumedWorkflowRunIds],
|
|
998
|
-
_events:
|
|
1224
|
+
_events: []
|
|
999
1225
|
};
|
|
1000
1226
|
}
|
|
1001
1227
|
|
|
@@ -1041,6 +1267,14 @@ function createKernel(config) {
|
|
|
1041
1267
|
const { _events: _, ...publicResult } = result;
|
|
1042
1268
|
return publicResult;
|
|
1043
1269
|
}
|
|
1270
|
+
if (command.type === "stage.pollSuspended") {
|
|
1271
|
+
const result = await handleStagePollSuspended(
|
|
1272
|
+
command,
|
|
1273
|
+
deps
|
|
1274
|
+
);
|
|
1275
|
+
const { _events: _, ...publicResult } = result;
|
|
1276
|
+
return publicResult;
|
|
1277
|
+
}
|
|
1044
1278
|
if (command.type === "job.execute") {
|
|
1045
1279
|
const jobCommand = command;
|
|
1046
1280
|
const jobIdempotencyKey = jobCommand.idempotencyKey;
|
|
@@ -1121,12 +1355,6 @@ function createKernel(config) {
|
|
|
1121
1355
|
txDeps
|
|
1122
1356
|
);
|
|
1123
1357
|
break;
|
|
1124
|
-
case "stage.pollSuspended":
|
|
1125
|
-
result = await handleStagePollSuspended(
|
|
1126
|
-
command,
|
|
1127
|
-
txDeps
|
|
1128
|
-
);
|
|
1129
|
-
break;
|
|
1130
1358
|
case "lease.reapStale":
|
|
1131
1359
|
result = await handleLeaseReapStale(
|
|
1132
1360
|
command,
|
|
@@ -1209,5 +1437,5 @@ function createPluginRunner(config) {
|
|
|
1209
1437
|
}
|
|
1210
1438
|
|
|
1211
1439
|
export { IdempotencyInProgressError, createKernel, createPluginRunner, definePlugin, loadWorkflowContext, saveStageOutput };
|
|
1212
|
-
//# sourceMappingURL=chunk-
|
|
1213
|
-
//# sourceMappingURL=chunk-
|
|
1440
|
+
//# sourceMappingURL=chunk-HOGDFLCG.js.map
|
|
1441
|
+
//# sourceMappingURL=chunk-HOGDFLCG.js.map
|