@kb-labs/workflow-daemon 2.116.13 → 2.117.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +102 -4
- package/dist/index.js.map +1 -1
- package/package.json +21 -21
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { randomUUID } from 'crypto';
|
|
|
19
19
|
async function createWorkflowWorker(options) {
|
|
20
20
|
const {
|
|
21
21
|
engine,
|
|
22
|
+
workflowService,
|
|
22
23
|
cliApi,
|
|
23
24
|
logger,
|
|
24
25
|
platform,
|
|
@@ -46,6 +47,58 @@ async function createWorkflowWorker(options) {
|
|
|
46
47
|
workspaceRoot,
|
|
47
48
|
defaultTimeout
|
|
48
49
|
});
|
|
50
|
+
async function invokeChildWorkflow(input) {
|
|
51
|
+
if (!workflowService) {
|
|
52
|
+
throw new Error("Workflow invocation is unavailable: WorkflowService was not configured");
|
|
53
|
+
}
|
|
54
|
+
if (!input.workflowRef.startsWith("workspace:")) {
|
|
55
|
+
throw new Error(`Unsupported child workflow reference: workflow:${input.workflowRef}`);
|
|
56
|
+
}
|
|
57
|
+
const workflowId = input.workflowRef.slice("workspace:".length);
|
|
58
|
+
const parentWorkflowId = input.parentRun.metadata?.["workflowId"] ?? input.parentRun.name;
|
|
59
|
+
const ancestors = input.parentRun.metadata?.workflowAncestors ?? [parentWorkflowId];
|
|
60
|
+
if (ancestors.includes(workflowId)) {
|
|
61
|
+
throw new Error(`Child workflow cycle detected: ${[...ancestors, workflowId].join(" -> ")}`);
|
|
62
|
+
}
|
|
63
|
+
const parentDepth = input.parentRun.metadata?.workflowDepth ?? 0;
|
|
64
|
+
if (parentDepth >= engine.maxWorkflowDepth) {
|
|
65
|
+
throw new Error(`Child workflow depth exceeds maxWorkflowDepth=${engine.maxWorkflowDepth}`);
|
|
66
|
+
}
|
|
67
|
+
const workflow = await workflowService.get(workflowId);
|
|
68
|
+
const spec = workflow?.input;
|
|
69
|
+
if (!workflow || !spec?.jobs) {
|
|
70
|
+
throw new Error(`Child workflow not found or is not executable: ${workflowId}`);
|
|
71
|
+
}
|
|
72
|
+
const child = await engine.runFromSpec(spec, {
|
|
73
|
+
trigger: {
|
|
74
|
+
type: "workflow",
|
|
75
|
+
actor: input.parentRun.trigger.actor,
|
|
76
|
+
payload: input.inputs,
|
|
77
|
+
parentRunId: input.parentRun.id,
|
|
78
|
+
parentJobId: input.parentJob.id,
|
|
79
|
+
parentStepId: input.parentStep.id,
|
|
80
|
+
invokedByWorkflowId: parentWorkflowId
|
|
81
|
+
},
|
|
82
|
+
inputs: input.inputs,
|
|
83
|
+
env: input.parentRun.env,
|
|
84
|
+
metadata: {
|
|
85
|
+
workflowId,
|
|
86
|
+
parentRunId: input.parentRun.id,
|
|
87
|
+
parentJobId: input.parentJob.id,
|
|
88
|
+
parentStepId: input.parentStep.id,
|
|
89
|
+
workflowDepth: parentDepth + 1,
|
|
90
|
+
rootRunId: input.parentRun.metadata?.rootRunId ?? input.parentRun.id,
|
|
91
|
+
workflowAncestors: [...ancestors, workflowId],
|
|
92
|
+
// A composed quality workflow must inspect and repair the exact change
|
|
93
|
+
// prepared by its parent, not a fresh worktree at main. The owner keeps
|
|
94
|
+
// lifecycle responsibility; children only borrow this binding.
|
|
95
|
+
executionWorkspace: input.parentRun.metadata?.["executionWorkspace"],
|
|
96
|
+
workspaceOwnerRunId: input.parentRun.metadata?.["workspaceOwnerRunId"] ?? input.parentRun.id
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
await engine.markStepWaitingChild(input.parentRun.id, input.parentJob.id, input.parentStep.id, child.id);
|
|
100
|
+
await engine.reconcileChildInvocation(child.id);
|
|
101
|
+
}
|
|
49
102
|
async function processJob() {
|
|
50
103
|
const entry = await engine.nextJob();
|
|
51
104
|
if (!entry) {
|
|
@@ -118,7 +171,17 @@ async function createWorkflowWorker(options) {
|
|
|
118
171
|
const wsProvider2 = platform.getAdapter("workspace");
|
|
119
172
|
let runWorkspace = wsProvider2 ? workspaceRoot : process.env["KB_PROJECT_ROOT"] ?? workspaceRoot;
|
|
120
173
|
let provisionedWorkspaceId;
|
|
121
|
-
|
|
174
|
+
const workspaceMetadata = run.metadata?.["executionWorkspace"];
|
|
175
|
+
const inheritedWorkspace = typeof run.metadata?.["workspaceOwnerRunId"] === "string" && run.metadata?.["workspaceOwnerRunId"] !== run.id;
|
|
176
|
+
if (workspaceMetadata && typeof workspaceMetadata.rootPath === "string" && typeof workspaceMetadata.workspaceId === "string") {
|
|
177
|
+
runWorkspace = workspaceMetadata.rootPath;
|
|
178
|
+
provisionedWorkspaceId = inheritedWorkspace ? void 0 : workspaceMetadata.workspaceId;
|
|
179
|
+
jobLogger.info("Workspace binding restored", {
|
|
180
|
+
workspaceId: workspaceMetadata.workspaceId,
|
|
181
|
+
ownerRunId: run.metadata?.["workspaceOwnerRunId"] ?? run.id,
|
|
182
|
+
inherited: inheritedWorkspace
|
|
183
|
+
});
|
|
184
|
+
} else if (wsProvider2) {
|
|
122
185
|
const wsId = `wt_${run.id.slice(0, 8)}`;
|
|
123
186
|
try {
|
|
124
187
|
const ws = await wsProvider2.materialize({
|
|
@@ -133,8 +196,16 @@ async function createWorkflowWorker(options) {
|
|
|
133
196
|
}
|
|
134
197
|
});
|
|
135
198
|
if (ws.rootPath) {
|
|
136
|
-
|
|
199
|
+
const rootPath = ws.rootPath;
|
|
200
|
+
runWorkspace = rootPath;
|
|
137
201
|
provisionedWorkspaceId = ws.workspaceId;
|
|
202
|
+
await engine.getStateStore().updateRun(run.id, (draft) => {
|
|
203
|
+
draft.metadata = {
|
|
204
|
+
...draft.metadata ?? {},
|
|
205
|
+
executionWorkspace: { workspaceId: ws.workspaceId, rootPath },
|
|
206
|
+
workspaceOwnerRunId: run.id
|
|
207
|
+
};
|
|
208
|
+
});
|
|
138
209
|
jobLogger.info("Workspace provisioned", {
|
|
139
210
|
workspaceId: ws.workspaceId,
|
|
140
211
|
provider: ws.provider,
|
|
@@ -174,6 +245,9 @@ async function createWorkflowWorker(options) {
|
|
|
174
245
|
if (step.status === "success") {
|
|
175
246
|
continue;
|
|
176
247
|
}
|
|
248
|
+
if (step.status === "waiting_child") {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
177
251
|
const freshRun = await engine.getRun(run.id);
|
|
178
252
|
if (freshRun?.status === "cancelled") {
|
|
179
253
|
jobLogger.info("[worker] Run cancelled \u2014 stopping step execution", {
|
|
@@ -332,6 +406,16 @@ async function createWorkflowWorker(options) {
|
|
|
332
406
|
}
|
|
333
407
|
continue;
|
|
334
408
|
}
|
|
409
|
+
if (step.spec.uses?.startsWith("workflow:")) {
|
|
410
|
+
await invokeChildWorkflow({
|
|
411
|
+
parentRun: run,
|
|
412
|
+
parentJob: job,
|
|
413
|
+
parentStep: step,
|
|
414
|
+
workflowRef: step.spec.uses.slice("workflow:".length),
|
|
415
|
+
inputs: interpolatedWith ?? {}
|
|
416
|
+
});
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
335
419
|
if (step.spec.uses === "builtin:gate") {
|
|
336
420
|
const gateInput = interpolatedWith ?? {};
|
|
337
421
|
const currentIteration = step.metadata?.["iterations"] ?? 0;
|
|
@@ -1957,7 +2041,11 @@ var WorkflowHostService = class {
|
|
|
1957
2041
|
actor: request.trigger?.user,
|
|
1958
2042
|
payload: userInputs
|
|
1959
2043
|
},
|
|
1960
|
-
inputs: resolvedInputs
|
|
2044
|
+
inputs: resolvedInputs,
|
|
2045
|
+
metadata: {
|
|
2046
|
+
workflowId: id,
|
|
2047
|
+
workflowAncestors: [id]
|
|
2048
|
+
}
|
|
1961
2049
|
});
|
|
1962
2050
|
return {
|
|
1963
2051
|
runId: run.id,
|
|
@@ -1990,7 +2078,11 @@ var WorkflowHostService = class {
|
|
|
1990
2078
|
actor: "cli-rerun",
|
|
1991
2079
|
payload: resolvedInputs
|
|
1992
2080
|
},
|
|
1993
|
-
inputs: resolvedInputs
|
|
2081
|
+
inputs: resolvedInputs,
|
|
2082
|
+
metadata: {
|
|
2083
|
+
workflowId,
|
|
2084
|
+
workflowAncestors: [workflowId]
|
|
2085
|
+
}
|
|
1994
2086
|
});
|
|
1995
2087
|
return {
|
|
1996
2088
|
runId: run.id,
|
|
@@ -3249,6 +3341,11 @@ async function bootstrap(_cwd = process.cwd()) {
|
|
|
3249
3341
|
await engine.cleanupStaleRuns();
|
|
3250
3342
|
bootstrapLogger.info("Resuming interrupted jobs");
|
|
3251
3343
|
await engine.resumeInterruptedJobs();
|
|
3344
|
+
bootstrapLogger.info("Reconciling persisted child workflow invocations");
|
|
3345
|
+
const reconciledChildren = await engine.reconcileChildInvocations();
|
|
3346
|
+
if (reconciledChildren > 0) {
|
|
3347
|
+
bootstrapLogger.info("Reconciled child workflow invocations", { count: reconciledChildren });
|
|
3348
|
+
}
|
|
3252
3349
|
bootstrapLogger.info("Creating JobBroker");
|
|
3253
3350
|
const jobBroker = new JobBroker(
|
|
3254
3351
|
engine,
|
|
@@ -3311,6 +3408,7 @@ async function bootstrap(_cwd = process.cwd()) {
|
|
|
3311
3408
|
bootstrapLogger.info("Creating WorkflowWorker");
|
|
3312
3409
|
const worker = await createWorkflowWorker({
|
|
3313
3410
|
engine,
|
|
3411
|
+
workflowService,
|
|
3314
3412
|
cliApi,
|
|
3315
3413
|
logger: createWorkflowLogger("worker", "workflow.worker"),
|
|
3316
3414
|
analytics: platform.analytics,
|