stageflow 0.9.0 → 0.13.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 +6 -1
- package/dist/agent/activity.d.ts +9 -0
- package/dist/agent/agentBackend.d.ts +32 -0
- package/dist/agent/agentBackend.js +38 -0
- package/dist/agent/claudeActivity.d.ts +15 -0
- package/dist/agent/claudeActivity.js +58 -0
- package/dist/agent/claudeAdapter.d.ts +5 -0
- package/dist/agent/claudeAdapter.js +331 -0
- package/dist/agent/claudeSession.d.ts +18 -0
- package/dist/agent/claudeSession.js +54 -0
- package/dist/agent/claudeTools.d.ts +89 -0
- package/dist/agent/claudeTools.js +135 -0
- package/dist/agent/fakeAgent.js +24 -4
- package/dist/agent/piAdapter.d.ts +12 -7
- package/dist/agent/piAdapter.js +194 -44
- package/dist/agent/piIsolatedMcp.d.ts +23 -0
- package/dist/agent/piIsolatedMcp.js +223 -0
- package/dist/agent/piIsolatedMcpProbe.d.ts +15 -0
- package/dist/agent/piIsolatedMcpProbe.js +292 -0
- package/dist/agent/port.d.ts +52 -1
- package/dist/agent/port.js +1 -1
- package/dist/agent/resolveAgentPort.d.ts +26 -0
- package/dist/agent/resolveAgentPort.js +39 -0
- package/dist/cli/runCommand.js +2 -2
- package/dist/cli/runsCommand.d.ts +1 -1
- package/dist/cli/runsCommand.js +184 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +11 -5
- package/dist/config/browseCatalog.js +1 -1
- package/dist/config/createPipeline.d.ts +2 -1
- package/dist/config/createPipeline.js +34 -9
- package/dist/config/createStage.js +14 -9
- package/dist/config/loadPipeline.js +11 -1
- package/dist/config/loadStage.d.ts +1 -0
- package/dist/config/loadStage.js +207 -2
- package/dist/config/loadStageflowManifest.js +14 -1
- package/dist/config/mergePipelineIncludes.d.ts +1 -0
- package/dist/config/mergePipelineIncludes.js +14 -1
- package/dist/config/normalizePipelineStageEntry.d.ts +7 -2
- package/dist/config/normalizePipelineStageEntry.js +56 -7
- package/dist/config/parseCompletionContract.d.ts +8 -0
- package/dist/config/parseCompletionContract.js +218 -0
- package/dist/config/parsePreEmitChecks.d.ts +3 -0
- package/dist/config/parsePreEmitChecks.js +65 -0
- package/dist/config/pipelineNeeds.d.ts +19 -0
- package/dist/config/pipelineNeeds.js +151 -0
- package/dist/config/pipelineStageKeys.js +17 -1
- package/dist/config/resolveForkEmitContext.d.ts +6 -2
- package/dist/config/resolveForkEmitContext.js +9 -2
- package/dist/config/resolvePipelineDag.d.ts +2 -1
- package/dist/config/resolvePipelineDag.js +182 -39
- package/dist/config/resolveStageMcpServers.d.ts +35 -0
- package/dist/config/resolveStageMcpServers.js +207 -0
- package/dist/config/validateCatalog.d.ts +1 -1
- package/dist/config/validateCatalog.js +51 -0
- package/dist/config/validateCompletionContract.d.ts +4 -0
- package/dist/config/validateCompletionContract.js +52 -0
- package/dist/envelope/check.d.ts +1 -1
- package/dist/envelope/check.js +86 -13
- package/dist/envelope/cloneForks.d.ts +1 -1
- package/dist/envelope/cloneForks.js +24 -9
- package/dist/envelope/feedbackLoop.d.ts +4 -0
- package/dist/envelope/feedbackLoop.js +31 -0
- package/dist/envelope/payloadSchema.d.ts +7 -1
- package/dist/envelope/payloadSchema.js +170 -13
- package/dist/envelope/preEmitChecks.d.ts +29 -0
- package/dist/envelope/preEmitChecks.js +57 -0
- package/dist/hitl/qaTrail.d.ts +25 -0
- package/dist/hitl/qaTrail.js +51 -0
- package/dist/mcp/catalogTools.js +4 -2
- package/dist/mcp/controlTools.js +72 -0
- package/dist/mcp/readArtifact.d.ts +2 -0
- package/dist/mcp/readArtifact.js +25 -1
- package/dist/mcp/waitingGates.d.ts +2 -1
- package/dist/mcp/waitingGates.js +17 -1
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/projection/projectRun.d.ts +4 -1
- package/dist/projection/projectRun.js +7 -0
- package/dist/prompt/feedbackLoopContext.d.ts +2 -0
- package/dist/prompt/feedbackLoopContext.js +17 -0
- package/dist/prompt/priorEnvelope.d.ts +2 -2
- package/dist/prompt/priorEnvelope.js +4 -1
- package/dist/runstore/pipelineDagSnapshot.d.ts +1 -0
- package/dist/runstore/pipelineDagSnapshot.js +51 -11
- package/dist/runstore/port.d.ts +185 -3
- package/dist/runstore/port.js +42 -3
- package/dist/runstore/runProjection.d.ts +7 -3
- package/dist/runstore/runProjection.js +30 -15
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +25 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +626 -8
- package/dist/runstore/sqlite/schema.d.ts +1 -1
- package/dist/runstore/sqlite/schema.js +99 -0
- package/dist/runstore/stageInstanceId.d.ts +1 -1
- package/dist/runstore/stageInstanceId.js +3 -2
- package/dist/runstore/trackProjection.js +59 -27
- package/dist/runstore/verificationHistory.d.ts +19 -0
- package/dist/runstore/verificationHistory.js +30 -0
- package/dist/runtime/cloneSchedule.d.ts +40 -4
- package/dist/runtime/cloneSchedule.js +212 -28
- package/dist/runtime/completionCheckRunner.d.ts +137 -0
- package/dist/runtime/completionCheckRunner.js +365 -0
- package/dist/runtime/completionCheckoutBaseline.d.ts +3 -0
- package/dist/runtime/completionCheckoutBaseline.js +44 -0
- package/dist/runtime/envelopeRouting.d.ts +10 -3
- package/dist/runtime/envelopeRouting.js +146 -5
- package/dist/runtime/feedbackLoopCoordinator.d.ts +74 -0
- package/dist/runtime/feedbackLoopCoordinator.js +446 -0
- package/dist/runtime/feedbackLoopDecision.d.ts +44 -0
- package/dist/runtime/feedbackLoopDecision.js +207 -0
- package/dist/runtime/feedbackLoopRoute.d.ts +5 -0
- package/dist/runtime/feedbackLoopRoute.js +13 -0
- package/dist/runtime/feedbackLoopSchedule.d.ts +38 -0
- package/dist/runtime/feedbackLoopSchedule.js +72 -0
- package/dist/runtime/forkGeneration.d.ts +72 -0
- package/dist/runtime/forkGeneration.js +295 -0
- package/dist/runtime/gitCheckoutCapability.d.ts +7 -0
- package/dist/runtime/gitCheckoutCapability.js +97 -0
- package/dist/runtime/manualRecoveryState.d.ts +40 -0
- package/dist/runtime/manualRecoveryState.js +73 -0
- package/dist/runtime/pipelineScheduler.d.ts +13 -1
- package/dist/runtime/pipelineScheduler.js +328 -45
- package/dist/runtime/replayLifecycle.d.ts +107 -0
- package/dist/runtime/replayLifecycle.js +168 -0
- package/dist/runtime/runManager.d.ts +54 -1
- package/dist/runtime/runManager.js +471 -6
- package/dist/runtime/runRetryCoordinator.d.ts +6 -1
- package/dist/runtime/runRetryCoordinator.js +43 -9
- package/dist/runtime/stageAttemptBootstrap.d.ts +3 -1
- package/dist/runtime/stageAttemptBootstrap.js +117 -3
- package/dist/runtime/stageProcessLauncher.d.ts +1 -1
- package/dist/runtime/stageRecovery.js +1 -1
- package/dist/runtime/stageRunner.d.ts +4 -1
- package/dist/runtime/stageRunner.js +29 -3
- package/dist/runtime/stageWorker.js +69 -2
- package/dist/runtime/stageWorkerProtocol.d.ts +1 -1
- package/dist/runtime/verifiedStageExecution.d.ts +31 -0
- package/dist/runtime/verifiedStageExecution.js +127 -0
- package/dist/server/bootstrap.js +1 -0
- package/dist/server/http.js +137 -4
- package/dist/server/operatorResults.js +2 -0
- package/dist/server/projectMcpRoutes.d.ts +6 -0
- package/dist/server/projectMcpRoutes.js +38 -0
- package/dist/tools/askOperator.d.ts +161 -5
- package/dist/tools/askOperator.js +58 -25
- package/dist/tools/emitStageEnvelope.d.ts +12 -12
- package/dist/tools/emitStageEnvelope.js +112 -24
- package/dist/types/completion.d.ts +43 -0
- package/dist/types/completion.js +1 -0
- package/dist/types/envelope.d.ts +18 -0
- package/dist/types/forkChoice.d.ts +9 -4
- package/dist/types/forkChoice.js +1 -1
- package/dist/types/pipeline.d.ts +41 -2
- package/dist/types/preEmitCheck.d.ts +18 -0
- package/dist/types/preEmitCheck.js +1 -0
- package/dist/types/stage.d.ts +14 -0
- package/dist/types/stageflowManifest.d.ts +2 -0
- package/dist/ui/assets/index-Yzh42vhf.js +118 -0
- package/dist/ui/assets/{index-C3N7MXAC.css → index-uVHiae8s.css} +1 -1
- package/dist/ui/index.html +2 -2
- package/package.json +5 -1
- package/skills/stageflow-author/SKILL.md +20 -2
- package/skills/stageflow-author/references/catalog-mapping.md +88 -3
- package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
- package/skills/stageflow-author/references/stage-prompt-template.md +63 -4
- package/skills/stageflow-session-capture/references/catalog-authoring.md +4 -1
- package/dist/ui/assets/index-CSoGDA3A.js +0 -118
package/dist/config/loadStage.js
CHANGED
|
@@ -1,10 +1,70 @@
|
|
|
1
|
+
import { parseAgentField } from "../agent/agentBackend.js";
|
|
2
|
+
import { STAGEFLOW_MCP_SERVER_NAME } from "../agent/claudeTools.js";
|
|
3
|
+
import { CLONE_ACTIONS } from "../types/forkChoice.js";
|
|
1
4
|
import { STAGE_GATE_KINDS, } from "../types/stage.js";
|
|
2
5
|
import { compilePayloadSchema } from "../envelope/payloadSchema.js";
|
|
3
6
|
import { loadFailure, loadSuccess } from "./loadOutcome.js";
|
|
7
|
+
import { parsePreEmitChecks } from "./parsePreEmitChecks.js";
|
|
4
8
|
import { readYamlObject } from "./readYamlObject.js";
|
|
5
9
|
function isGateKind(value) {
|
|
6
10
|
return STAGE_GATE_KINDS.includes(value);
|
|
7
11
|
}
|
|
12
|
+
function isCloneAction(value) {
|
|
13
|
+
return CLONE_ACTIONS.includes(value);
|
|
14
|
+
}
|
|
15
|
+
function parseCloneActions(raw, label) {
|
|
16
|
+
if (raw === undefined)
|
|
17
|
+
return loadSuccess(undefined);
|
|
18
|
+
if (!Array.isArray(raw) || !raw.every((item) => typeof item === "string")) {
|
|
19
|
+
return loadFailure([
|
|
20
|
+
{
|
|
21
|
+
code: "stage.invalid_clone_actions",
|
|
22
|
+
message: `Invalid stage ${label}: clone_actions must be a non-empty array of strings`,
|
|
23
|
+
category: "stage",
|
|
24
|
+
},
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
if (raw.length === 0) {
|
|
28
|
+
return loadFailure([
|
|
29
|
+
{
|
|
30
|
+
code: "stage.invalid_clone_actions",
|
|
31
|
+
message: `Invalid stage ${label}: clone_actions must not be empty`,
|
|
32
|
+
category: "stage",
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
const actions = [];
|
|
37
|
+
for (const item of raw) {
|
|
38
|
+
if (!isCloneAction(item)) {
|
|
39
|
+
return loadFailure([
|
|
40
|
+
{
|
|
41
|
+
code: "stage.invalid_clone_actions",
|
|
42
|
+
message: `Invalid stage ${label}: unsupported clone_actions value "${item}" (allowed: ${CLONE_ACTIONS.join(", ")})`,
|
|
43
|
+
category: "stage",
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
47
|
+
actions.push(item);
|
|
48
|
+
}
|
|
49
|
+
return loadSuccess(actions);
|
|
50
|
+
}
|
|
51
|
+
function parseTimeoutMs(raw, label) {
|
|
52
|
+
if (raw === undefined)
|
|
53
|
+
return loadSuccess(undefined);
|
|
54
|
+
if (typeof raw !== "number" ||
|
|
55
|
+
!Number.isFinite(raw) ||
|
|
56
|
+
!Number.isInteger(raw) ||
|
|
57
|
+
raw <= 0) {
|
|
58
|
+
return loadFailure([
|
|
59
|
+
{
|
|
60
|
+
code: "stage.invalid_timeout_ms",
|
|
61
|
+
message: `Invalid stage ${label}: timeout_ms must be a positive integer (milliseconds)`,
|
|
62
|
+
category: "stage",
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
return loadSuccess(raw);
|
|
67
|
+
}
|
|
8
68
|
function parseGateKinds(raw, label) {
|
|
9
69
|
if (raw === undefined)
|
|
10
70
|
return loadSuccess(undefined);
|
|
@@ -30,7 +90,69 @@ function parseGateKinds(raw, label) {
|
|
|
30
90
|
}
|
|
31
91
|
kinds.push(item);
|
|
32
92
|
}
|
|
33
|
-
return loadSuccess(kinds
|
|
93
|
+
return loadSuccess(kinds);
|
|
94
|
+
}
|
|
95
|
+
export function parseStageMcp(raw, label, stageId) {
|
|
96
|
+
if (raw === undefined)
|
|
97
|
+
return loadSuccess(undefined);
|
|
98
|
+
if (!Array.isArray(raw)) {
|
|
99
|
+
return loadFailure([
|
|
100
|
+
{
|
|
101
|
+
code: "stage.invalid_mcp",
|
|
102
|
+
message: `Invalid stage ${label}: mcp must be an array of server names`,
|
|
103
|
+
category: "stage",
|
|
104
|
+
stageId,
|
|
105
|
+
},
|
|
106
|
+
]);
|
|
107
|
+
}
|
|
108
|
+
const names = [];
|
|
109
|
+
const seen = new Set();
|
|
110
|
+
for (const item of raw) {
|
|
111
|
+
if (typeof item !== "string") {
|
|
112
|
+
return loadFailure([
|
|
113
|
+
{
|
|
114
|
+
code: "stage.invalid_mcp",
|
|
115
|
+
message: `Invalid stage ${label}: mcp entries must be strings`,
|
|
116
|
+
category: "stage",
|
|
117
|
+
stageId,
|
|
118
|
+
},
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
const name = item.trim();
|
|
122
|
+
if (name === "") {
|
|
123
|
+
return loadFailure([
|
|
124
|
+
{
|
|
125
|
+
code: "stage.invalid_mcp",
|
|
126
|
+
message: `Invalid stage ${label}: mcp entries must be non-empty strings`,
|
|
127
|
+
category: "stage",
|
|
128
|
+
stageId,
|
|
129
|
+
},
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
if (name === STAGEFLOW_MCP_SERVER_NAME) {
|
|
133
|
+
return loadFailure([
|
|
134
|
+
{
|
|
135
|
+
code: "stage.invalid_mcp",
|
|
136
|
+
message: `Invalid stage ${label}: mcp must not include reserved name "${STAGEFLOW_MCP_SERVER_NAME}"`,
|
|
137
|
+
category: "stage",
|
|
138
|
+
stageId,
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
}
|
|
142
|
+
if (seen.has(name)) {
|
|
143
|
+
return loadFailure([
|
|
144
|
+
{
|
|
145
|
+
code: "stage.invalid_mcp",
|
|
146
|
+
message: `Invalid stage ${label}: mcp contains duplicate name "${name}"`,
|
|
147
|
+
category: "stage",
|
|
148
|
+
stageId,
|
|
149
|
+
},
|
|
150
|
+
]);
|
|
151
|
+
}
|
|
152
|
+
seen.add(name);
|
|
153
|
+
names.push(name);
|
|
154
|
+
}
|
|
155
|
+
return loadSuccess(names);
|
|
34
156
|
}
|
|
35
157
|
function parseStageFields(raw, label, entryId) {
|
|
36
158
|
if (typeof raw.system_prompt !== "string" ||
|
|
@@ -78,6 +200,46 @@ function parseStageFields(raw, label, entryId) {
|
|
|
78
200
|
}
|
|
79
201
|
stage.payload_schema = raw.payload_schema;
|
|
80
202
|
}
|
|
203
|
+
if (raw.clone_input_schema !== undefined) {
|
|
204
|
+
if (raw.clone_input_schema === null ||
|
|
205
|
+
typeof raw.clone_input_schema !== "object" ||
|
|
206
|
+
Array.isArray(raw.clone_input_schema)) {
|
|
207
|
+
return loadFailure([
|
|
208
|
+
{
|
|
209
|
+
code: "stage.invalid_clone_input_schema",
|
|
210
|
+
message: `Invalid stage ${label}: clone_input_schema must be an object`,
|
|
211
|
+
category: "stage",
|
|
212
|
+
stageId: entryId,
|
|
213
|
+
},
|
|
214
|
+
]);
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
compilePayloadSchema(raw.clone_input_schema);
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
221
|
+
return loadFailure([
|
|
222
|
+
{
|
|
223
|
+
code: "stage.invalid_clone_input_schema",
|
|
224
|
+
message: `Invalid stage ${label}: invalid clone_input_schema: ${message}`,
|
|
225
|
+
category: "stage",
|
|
226
|
+
stageId: entryId,
|
|
227
|
+
},
|
|
228
|
+
]);
|
|
229
|
+
}
|
|
230
|
+
stage.clone_input_schema = raw.clone_input_schema;
|
|
231
|
+
}
|
|
232
|
+
const cloneActionsOutcome = parseCloneActions(raw.clone_actions, label);
|
|
233
|
+
if (!cloneActionsOutcome.ok) {
|
|
234
|
+
const issues = cloneActionsOutcome.issues.map((issue) => ({
|
|
235
|
+
...issue,
|
|
236
|
+
stageId: entryId,
|
|
237
|
+
}));
|
|
238
|
+
return loadFailure(issues);
|
|
239
|
+
}
|
|
240
|
+
if (cloneActionsOutcome.value !== undefined) {
|
|
241
|
+
stage.clone_actions = cloneActionsOutcome.value;
|
|
242
|
+
}
|
|
81
243
|
const gateKindsOutcome = parseGateKinds(raw.gate_kinds, label);
|
|
82
244
|
if (!gateKindsOutcome.ok) {
|
|
83
245
|
const issues = gateKindsOutcome.issues.map((issue) => ({
|
|
@@ -86,8 +248,31 @@ function parseStageFields(raw, label, entryId) {
|
|
|
86
248
|
}));
|
|
87
249
|
return loadFailure(issues);
|
|
88
250
|
}
|
|
89
|
-
if (gateKindsOutcome.value)
|
|
251
|
+
if (gateKindsOutcome.value !== undefined) {
|
|
90
252
|
stage.gate_kinds = gateKindsOutcome.value;
|
|
253
|
+
}
|
|
254
|
+
const timeoutMsOutcome = parseTimeoutMs(raw.timeout_ms, label);
|
|
255
|
+
if (!timeoutMsOutcome.ok) {
|
|
256
|
+
const issues = timeoutMsOutcome.issues.map((issue) => ({
|
|
257
|
+
...issue,
|
|
258
|
+
stageId: entryId,
|
|
259
|
+
}));
|
|
260
|
+
return loadFailure(issues);
|
|
261
|
+
}
|
|
262
|
+
if (timeoutMsOutcome.value !== undefined) {
|
|
263
|
+
stage.timeout_ms = timeoutMsOutcome.value;
|
|
264
|
+
}
|
|
265
|
+
const preEmitChecksOutcome = parsePreEmitChecks(raw.pre_emit_checks, label);
|
|
266
|
+
if (!preEmitChecksOutcome.ok) {
|
|
267
|
+
const issues = preEmitChecksOutcome.issues.map((issue) => ({
|
|
268
|
+
...issue,
|
|
269
|
+
stageId: entryId,
|
|
270
|
+
}));
|
|
271
|
+
return loadFailure(issues);
|
|
272
|
+
}
|
|
273
|
+
if (preEmitChecksOutcome.value !== undefined) {
|
|
274
|
+
stage.pre_emit_checks = preEmitChecksOutcome.value;
|
|
275
|
+
}
|
|
91
276
|
if (raw.skill !== undefined) {
|
|
92
277
|
if (typeof raw.skill !== "string" || raw.skill.trim() === "") {
|
|
93
278
|
return loadFailure([
|
|
@@ -101,6 +286,26 @@ function parseStageFields(raw, label, entryId) {
|
|
|
101
286
|
}
|
|
102
287
|
stage.skill = raw.skill.trim();
|
|
103
288
|
}
|
|
289
|
+
const mcpOutcome = parseStageMcp(raw.mcp, label, entryId);
|
|
290
|
+
if (!mcpOutcome.ok)
|
|
291
|
+
return mcpOutcome;
|
|
292
|
+
if (mcpOutcome.value !== undefined) {
|
|
293
|
+
stage.mcp = mcpOutcome.value;
|
|
294
|
+
}
|
|
295
|
+
const agentField = parseAgentField(raw.agent);
|
|
296
|
+
if (!agentField.ok) {
|
|
297
|
+
return loadFailure([
|
|
298
|
+
{
|
|
299
|
+
code: "stage.invalid_agent",
|
|
300
|
+
message: `Invalid stage ${label}: ${agentField.message}`,
|
|
301
|
+
category: "stage",
|
|
302
|
+
stageId: entryId,
|
|
303
|
+
},
|
|
304
|
+
]);
|
|
305
|
+
}
|
|
306
|
+
if (agentField.value !== undefined) {
|
|
307
|
+
stage.agent = agentField.value;
|
|
308
|
+
}
|
|
104
309
|
return loadSuccess(stage);
|
|
105
310
|
}
|
|
106
311
|
export function loadStageFromObjectOutcome(raw, ctx) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { access, readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { parseAgentField } from "../agent/agentBackend.js";
|
|
4
5
|
import { loadFailure, loadSuccess } from "./loadOutcome.js";
|
|
5
6
|
const DEFAULT_PIPELINE_PATTERN = "*.pipeline.yaml";
|
|
6
7
|
const DEFAULT_TASK_PATTERN = "*.task.yaml";
|
|
@@ -108,6 +109,14 @@ export function parseStageflowManifestOutcome(yamlText, manifestPath, projectRoo
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
const patterns = parsePatterns(catalogRecord.patterns, issues);
|
|
112
|
+
let agent;
|
|
113
|
+
const agentField = parseAgentField(record.agent);
|
|
114
|
+
if (!agentField.ok) {
|
|
115
|
+
issues.push(catalogIssue("catalog.manifest_invalid", agentField.message));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
agent = agentField.value;
|
|
119
|
+
}
|
|
111
120
|
if (issues.length > 0) {
|
|
112
121
|
return loadFailure(issues);
|
|
113
122
|
}
|
|
@@ -127,7 +136,11 @@ export function parseStageflowManifestOutcome(yamlText, manifestPath, projectRoo
|
|
|
127
136
|
catalog.patterns.task = patterns.task;
|
|
128
137
|
}
|
|
129
138
|
}
|
|
130
|
-
const manifest = {
|
|
139
|
+
const manifest = {
|
|
140
|
+
version: 1,
|
|
141
|
+
catalog,
|
|
142
|
+
...(agent !== undefined ? { agent } : {}),
|
|
143
|
+
};
|
|
131
144
|
return loadSuccess({
|
|
132
145
|
path: manifestPath,
|
|
133
146
|
projectRoot,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { parseAgentField } from "../agent/agentBackend.js";
|
|
2
3
|
import { loadFailure, loadSuccess } from "./loadOutcome.js";
|
|
3
4
|
import { readYamlObject } from "./readYamlObject.js";
|
|
4
5
|
function isPlainObject(value) {
|
|
@@ -123,6 +124,18 @@ export async function mergePipelineStages(rootPath) {
|
|
|
123
124
|
]);
|
|
124
125
|
}
|
|
125
126
|
const pipelineId = raw.id;
|
|
127
|
+
const agentField = parseAgentField(raw.agent);
|
|
128
|
+
if (!agentField.ok) {
|
|
129
|
+
return loadFailure([
|
|
130
|
+
{
|
|
131
|
+
code: "pipeline.invalid_agent",
|
|
132
|
+
message: `Invalid pipeline ${absRoot}: ${agentField.message}`,
|
|
133
|
+
category: "pipeline",
|
|
134
|
+
pipelineId,
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
}
|
|
138
|
+
const agent = agentField.value;
|
|
126
139
|
const idLocations = new Map();
|
|
127
140
|
const entries = [];
|
|
128
141
|
const mergeResult = await visitPipelineFile(absRoot, [], idLocations, entries);
|
|
@@ -137,5 +150,5 @@ export async function mergePipelineStages(rootPath) {
|
|
|
137
150
|
},
|
|
138
151
|
]);
|
|
139
152
|
}
|
|
140
|
-
return loadSuccess({ entries, pipelineId });
|
|
153
|
+
return loadSuccess({ entries, pipelineId, ...(agent !== undefined ? { agent } : {}) });
|
|
141
154
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CompletionContract, RecoveryPolicy } from "../types/completion.js";
|
|
2
|
+
import type { NormalizedPipelineStageEntry, PipelineNeeds } from "../types/pipeline.js";
|
|
2
3
|
import { type LoadOutcome } from "./loadOutcome.js";
|
|
3
4
|
import type { RawMergedEntry } from "./mergePipelineIncludes.js";
|
|
4
5
|
export declare function inferIdFromUsesPath(usesPath: string): string | null;
|
|
@@ -8,11 +9,15 @@ export declare function normalizePipelineStageEntries(rawEntries: RawMergedEntry
|
|
|
8
9
|
}): LoadOutcome<NormalizedPipelineStageEntry[]>;
|
|
9
10
|
export declare function toWiringRefs(entries: NormalizedPipelineStageEntry[]): Array<{
|
|
10
11
|
id: string;
|
|
11
|
-
needs?:
|
|
12
|
+
needs?: PipelineNeeds;
|
|
12
13
|
fork?: {
|
|
13
14
|
select: "one" | "subset";
|
|
14
15
|
allow_none?: boolean;
|
|
15
16
|
};
|
|
16
17
|
clonable?: boolean;
|
|
17
18
|
clone_cap?: number;
|
|
19
|
+
completion?: CompletionContract;
|
|
20
|
+
recovery?: RecoveryPolicy;
|
|
21
|
+
feedback_loop?: NormalizedPipelineStageEntry["feedback_loop"];
|
|
22
|
+
replay_safe?: boolean;
|
|
18
23
|
}>;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { loadFailure, loadSuccess } from "./loadOutcome.js";
|
|
3
|
+
import { parseStageMcp } from "./loadStage.js";
|
|
3
4
|
import { BODY_KEYS, isAllowedPipelineStageEntryKey, } from "./pipelineStageKeys.js";
|
|
5
|
+
import { parseExecutionPolicy } from "./parseCompletionContract.js";
|
|
6
|
+
import { parsePipelineNeeds } from "./pipelineNeeds.js";
|
|
7
|
+
import { parseFeedbackLoopConfig } from "./resolvePipelineDag.js";
|
|
4
8
|
function isPlainObject(value) {
|
|
5
9
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
10
|
}
|
|
@@ -28,7 +32,7 @@ function extractBodyRaw(raw) {
|
|
|
28
32
|
return body;
|
|
29
33
|
}
|
|
30
34
|
function hasBodyKey(raw) {
|
|
31
|
-
return Object.keys(raw).some((key) => BODY_KEYS.has(key) && key !== "skill");
|
|
35
|
+
return Object.keys(raw).some((key) => BODY_KEYS.has(key) && key !== "skill" && key !== "mcp");
|
|
32
36
|
}
|
|
33
37
|
function readSkill(raw) {
|
|
34
38
|
if (raw.skill === undefined)
|
|
@@ -93,6 +97,10 @@ export function normalizePipelineStageEntries(rawEntries, ctx) {
|
|
|
93
97
|
return skillOutcome;
|
|
94
98
|
}
|
|
95
99
|
const skill = skillOutcome?.ok ? skillOutcome.value : undefined;
|
|
100
|
+
const mcpOutcome = parseStageMcp(raw.mcp, `entry at index ${index} in ${declaringPath}`);
|
|
101
|
+
if (!mcpOutcome.ok)
|
|
102
|
+
return mcpOutcome;
|
|
103
|
+
const mcp = mcpOutcome.value;
|
|
96
104
|
if (uses && hasBody) {
|
|
97
105
|
return loadFailure([
|
|
98
106
|
{
|
|
@@ -130,27 +138,49 @@ export function normalizePipelineStageEntries(rawEntries, ctx) {
|
|
|
130
138
|
},
|
|
131
139
|
]);
|
|
132
140
|
}
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
const policyOutcome = parseExecutionPolicy(raw, id);
|
|
142
|
+
if (!policyOutcome.ok)
|
|
143
|
+
return policyOutcome;
|
|
144
|
+
let needs;
|
|
145
|
+
let feedbackLoop;
|
|
146
|
+
if (raw.feedback_loop !== undefined) {
|
|
147
|
+
try {
|
|
148
|
+
feedbackLoop = parseFeedbackLoopConfig(raw.feedback_loop, id, ctx);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
135
151
|
return loadFailure([
|
|
136
152
|
{
|
|
137
153
|
code: "pipeline.dag_error",
|
|
138
|
-
message:
|
|
154
|
+
message: err instanceof Error ? err.message : String(err),
|
|
139
155
|
category: "pipeline",
|
|
140
156
|
pipelineId: ctx.pipelineId,
|
|
141
157
|
},
|
|
142
158
|
]);
|
|
143
159
|
}
|
|
144
|
-
|
|
160
|
+
}
|
|
161
|
+
if (raw.replay_safe !== undefined && typeof raw.replay_safe !== "boolean") {
|
|
162
|
+
return loadFailure([
|
|
163
|
+
{
|
|
164
|
+
code: "pipeline.dag_error",
|
|
165
|
+
message: `Pipeline ${ctx.pipelineId} (${ctx.path}): stage "${id}": replay_safe must be a boolean`,
|
|
166
|
+
category: "pipeline",
|
|
167
|
+
pipelineId: ctx.pipelineId,
|
|
168
|
+
},
|
|
169
|
+
]);
|
|
170
|
+
}
|
|
171
|
+
if (raw.needs !== undefined) {
|
|
172
|
+
const parsedNeeds = parsePipelineNeeds(raw.needs, id);
|
|
173
|
+
if (!parsedNeeds.ok) {
|
|
145
174
|
return loadFailure([
|
|
146
175
|
{
|
|
147
176
|
code: "pipeline.dag_error",
|
|
148
|
-
message: `Pipeline ${ctx.pipelineId} (${ctx.path}):
|
|
177
|
+
message: `Pipeline ${ctx.pipelineId} (${ctx.path}): ${parsedNeeds.message}`,
|
|
149
178
|
category: "pipeline",
|
|
150
179
|
pipelineId: ctx.pipelineId,
|
|
151
180
|
},
|
|
152
181
|
]);
|
|
153
182
|
}
|
|
183
|
+
needs = parsedNeeds.value;
|
|
154
184
|
}
|
|
155
185
|
let forkValue;
|
|
156
186
|
if (raw.fork !== undefined) {
|
|
@@ -178,11 +208,24 @@ export function normalizePipelineStageEntries(rawEntries, ctx) {
|
|
|
178
208
|
id,
|
|
179
209
|
declaringPath,
|
|
180
210
|
body,
|
|
181
|
-
...(
|
|
211
|
+
...(needs !== undefined ? { needs } : {}),
|
|
182
212
|
...(forkValue !== undefined ? { fork: forkValue } : {}),
|
|
183
213
|
...(raw.clonable !== undefined ? { clonable: raw.clonable } : {}),
|
|
184
214
|
...(raw.clone_cap !== undefined ? { clone_cap: raw.clone_cap } : {}),
|
|
215
|
+
...(policyOutcome.value.completion !== undefined
|
|
216
|
+
? { completion: policyOutcome.value.completion }
|
|
217
|
+
: {}),
|
|
218
|
+
...(policyOutcome.value.recovery !== undefined
|
|
219
|
+
? { recovery: policyOutcome.value.recovery }
|
|
220
|
+
: {}),
|
|
221
|
+
...(feedbackLoop !== undefined
|
|
222
|
+
? { feedback_loop: feedbackLoop }
|
|
223
|
+
: {}),
|
|
224
|
+
...(raw.replay_safe !== undefined
|
|
225
|
+
? { replay_safe: raw.replay_safe }
|
|
226
|
+
: {}),
|
|
185
227
|
...(skill !== undefined ? { skill } : {}),
|
|
228
|
+
...(mcp !== undefined ? { mcp } : {}),
|
|
186
229
|
};
|
|
187
230
|
const priorPath = normalized.find((e) => e.id === id)?.declaringPath;
|
|
188
231
|
if (priorPath) {
|
|
@@ -206,5 +249,11 @@ export function toWiringRefs(entries) {
|
|
|
206
249
|
...(entry.fork !== undefined ? { fork: entry.fork } : {}),
|
|
207
250
|
...(entry.clonable !== undefined ? { clonable: entry.clonable } : {}),
|
|
208
251
|
...(entry.clone_cap !== undefined ? { clone_cap: entry.clone_cap } : {}),
|
|
252
|
+
...(entry.completion !== undefined ? { completion: entry.completion } : {}),
|
|
253
|
+
...(entry.recovery !== undefined ? { recovery: entry.recovery } : {}),
|
|
254
|
+
...(entry.feedback_loop !== undefined
|
|
255
|
+
? { feedback_loop: entry.feedback_loop }
|
|
256
|
+
: {}),
|
|
257
|
+
...(entry.replay_safe !== undefined ? { replay_safe: entry.replay_safe } : {}),
|
|
209
258
|
}));
|
|
210
259
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CompletionContract, RecoveryPolicy } from "../types/completion.js";
|
|
2
|
+
import { type LoadOutcome } from "./loadOutcome.js";
|
|
3
|
+
type ExecutionPolicy = {
|
|
4
|
+
completion?: CompletionContract;
|
|
5
|
+
recovery?: RecoveryPolicy;
|
|
6
|
+
};
|
|
7
|
+
export declare function parseExecutionPolicy(raw: Record<string, unknown>, stageId: string): LoadOutcome<ExecutionPolicy>;
|
|
8
|
+
export {};
|