stageflow 0.18.0 → 0.23.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 +2 -1
- package/dist/agent/activity.d.ts +9 -0
- package/dist/agent/activityObserver.d.ts +3 -0
- package/dist/agent/activityObserver.js +6 -1
- package/dist/agent/claudeAdapter.js +38 -0
- package/dist/agent/fakeAgent.js +3 -1
- package/dist/agent/piAdapter.js +40 -4
- package/dist/agent/port.d.ts +3 -1
- package/dist/agent/providerInspect.d.ts +19 -0
- package/dist/agent/providerInspect.js +35 -0
- package/dist/agent/stageTimeout.d.ts +9 -0
- package/dist/agent/stageTimeout.js +24 -0
- package/dist/agent/streamLogRedact.d.ts +2 -0
- package/dist/agent/streamLogRedact.js +13 -0
- package/dist/cli/artifactCommand.js +2 -2
- package/dist/cli/envelopeCommand.js +2 -2
- package/dist/cli/exportRunCommand.js +2 -2
- package/dist/cli/graphCommand.d.ts +15 -0
- package/dist/cli/graphCommand.js +79 -0
- package/dist/cli/graphRender.d.ts +2 -0
- package/dist/cli/graphRender.js +85 -0
- package/dist/cli/hostClient.d.ts +43 -0
- package/dist/cli/hostClient.js +182 -0
- package/dist/cli/providersCommand.js +5 -3
- package/dist/cli/runCommand.d.ts +6 -5
- package/dist/cli/runCommand.js +23 -31
- package/dist/cli/runOutput.d.ts +1 -1
- package/dist/cli/runsCommand.d.ts +4 -8
- package/dist/cli/runsCommand.js +65 -89
- package/dist/cli.js +13 -5
- package/dist/config/describePipeline.d.ts +26 -0
- package/dist/config/describePipeline.js +37 -0
- package/dist/config/loadPipeline.d.ts +13 -1
- package/dist/config/loadPipeline.js +34 -4
- package/dist/config/mergePipelineIncludes.d.ts +11 -0
- package/dist/config/mergePipelineIncludes.js +75 -0
- package/dist/config/validateCatalog.d.ts +2 -2
- package/dist/config/validateCatalog.js +24 -17
- package/dist/mcp/catalogTools.js +146 -52
- package/dist/mcp/controlTools.js +94 -19
- package/dist/mcp/deps.d.ts +3 -0
- package/dist/mcp/projectMcpTools.d.ts +3 -0
- package/dist/mcp/projectMcpTools.js +25 -0
- package/dist/mcp/providerTools.d.ts +3 -0
- package/dist/mcp/providerTools.js +17 -0
- package/dist/mcp/readArtifact.d.ts +9 -0
- package/dist/mcp/readArtifact.js +11 -0
- package/dist/mcp/server.js +9 -1
- package/dist/mcp/tailStreamLog.d.ts +8 -0
- package/dist/mcp/tailStreamLog.js +28 -0
- package/dist/mcp/toolResults.d.ts +17 -0
- package/dist/mcp/toolResults.js +15 -0
- package/dist/mcp/tools.js +4 -0
- package/dist/mcp/waitRun.js +4 -4
- package/dist/mcp/waitingGates.d.ts +1 -0
- package/dist/mcp/waitingGates.js +3 -2
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/projection/projectRun.d.ts +3 -0
- package/dist/projection/projectRun.js +7 -0
- package/dist/runstore/port.d.ts +3 -1
- package/dist/runstore/port.js +2 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +2 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +15 -1
- package/dist/runstore/workspaceLayout.d.ts +1 -0
- package/dist/runstore/workspaceLayout.js +3 -0
- package/dist/runtime/feedbackLoopCoordinator.d.ts +9 -1
- package/dist/runtime/feedbackLoopCoordinator.js +50 -20
- package/dist/runtime/feedbackLoopDecision.js +33 -1
- package/dist/runtime/forkGeneration.d.ts +4 -0
- package/dist/runtime/forkGeneration.js +16 -0
- package/dist/runtime/pipelineRunner.d.ts +3 -3
- package/dist/runtime/pipelineRunner.js +3 -1
- package/dist/runtime/pipelineScheduler.js +23 -1
- package/dist/runtime/reopenSkipped.d.ts +13 -0
- package/dist/runtime/reopenSkipped.js +56 -0
- package/dist/runtime/resumeReconstruct.js +2 -2
- package/dist/runtime/resumeTimedOut.d.ts +33 -0
- package/dist/runtime/resumeTimedOut.js +165 -0
- package/dist/runtime/runManager.d.ts +3 -1
- package/dist/runtime/runManager.js +110 -14
- package/dist/runtime/settingsFile.d.ts +2 -0
- package/dist/runtime/settingsFile.js +19 -0
- package/dist/runtime/stageAttemptBootstrap.d.ts +3 -0
- package/dist/runtime/stageAttemptBootstrap.js +8 -2
- package/dist/runtime/stageRecovery.js +2 -2
- package/dist/runtime/stageRunner.js +6 -2
- package/dist/runtime/stageStreamLog.d.ts +17 -0
- package/dist/runtime/stageStreamLog.js +89 -0
- package/dist/runtime/stageWorker.js +2 -1
- package/dist/server/bootstrap.js +9 -2
- package/dist/server/ensureGlobalService.d.ts +35 -0
- package/dist/server/ensureGlobalService.js +138 -0
- package/dist/server/http.d.ts +20 -1
- package/dist/server/http.js +452 -386
- package/dist/server/mcpHost.js +7 -8
- package/dist/server/operatorResults.d.ts +10 -0
- package/dist/server/operatorResults.js +24 -0
- package/dist/server/providerRoutes.d.ts +0 -6
- package/dist/server/providerRoutes.js +10 -18
- package/dist/tools/askOperator.d.ts +1 -0
- package/dist/tools/askOperator.js +14 -11
- package/dist/tools/emitStageEnvelope.d.ts +1 -0
- package/dist/tools/emitStageEnvelope.js +3 -1
- package/dist/tools/prepareToolArguments.d.ts +4 -0
- package/dist/tools/prepareToolArguments.js +79 -0
- package/dist/types/pipeline.d.ts +8 -0
- package/dist/ui/assets/{index-DsgVjLUS.css → index-CSsDued1.css} +1 -1
- package/dist/ui/assets/index-Da5EHU6M.js +119 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/skills/stageflow/references/control-surface.md +2 -2
- package/skills/stageflow-run/SKILL.md +27 -9
- package/skills/stageflow-run/references/mcp-call.md +1 -1
- package/skills/stageflow-run/scripts/mcp-call.mjs +9 -0
- package/dist/ui/assets/index-DwKGaQvQ.js +0 -119
package/dist/server/mcpHost.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import { bootstrapStageflowHost, } from "./bootstrap.js";
|
|
2
2
|
import { createHttpHost, DEFAULT_PORT, } from "./createHttpHost.js";
|
|
3
|
+
import { createOperatorRoutes } from "./http.js";
|
|
3
4
|
export async function startMcpServer(options) {
|
|
4
5
|
const host = options.host ?? "127.0.0.1";
|
|
5
6
|
const port = options.port ?? DEFAULT_PORT;
|
|
6
7
|
const boot = await bootstrapStageflowHost(options);
|
|
8
|
+
const { manager, store, cwd, agentDir, rootDir } = boot;
|
|
9
|
+
const providerAuthContext = boot.providerAuthContext;
|
|
7
10
|
return createHttpHost({
|
|
8
11
|
boot,
|
|
9
12
|
host,
|
|
10
13
|
port,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
json(res, 404, { error: "Not found" });
|
|
17
|
-
return true;
|
|
18
|
-
},
|
|
14
|
+
// Headless daemon: same REST API surface as `sf ui` (this is what lets
|
|
15
|
+
// `sf run`/`sf runs *` talk to an auto-started `sf mcp` over HTTP), just
|
|
16
|
+
// without serving the console's static UI files.
|
|
17
|
+
routes: createOperatorRoutes({ manager, store, cwd, agentDir, rootDir, providerAuthContext }),
|
|
19
18
|
});
|
|
20
19
|
}
|
|
21
20
|
export { DEFAULT_PORT };
|
|
@@ -11,3 +11,13 @@ export declare function mapStartFailure(result: Extract<StartRunResult, {
|
|
|
11
11
|
}>): {
|
|
12
12
|
error: string;
|
|
13
13
|
} & Record<string, unknown>;
|
|
14
|
+
export type StoreLookupPolicy = "run" | "artifact" | "envelope";
|
|
15
|
+
export type StoreLookupKind = "not_found" | "denied" | "error";
|
|
16
|
+
export type StoreLookupMapped = {
|
|
17
|
+
error: string;
|
|
18
|
+
status: number;
|
|
19
|
+
kind: StoreLookupKind;
|
|
20
|
+
};
|
|
21
|
+
export declare function mapStoreLookupError(err: unknown, opts: {
|
|
22
|
+
policy: StoreLookupPolicy;
|
|
23
|
+
}): StoreLookupMapped;
|
|
@@ -22,3 +22,27 @@ export function mapStartFailure(result) {
|
|
|
22
22
|
const { ok: _ok, status: _status, reason, ...rest } = result;
|
|
23
23
|
return { error: reason, ...rest };
|
|
24
24
|
}
|
|
25
|
+
export function mapStoreLookupError(err, opts) {
|
|
26
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
27
|
+
if (opts.policy === "artifact") {
|
|
28
|
+
if (error === "Artifact path denied") {
|
|
29
|
+
return { error, status: 400, kind: "denied" };
|
|
30
|
+
}
|
|
31
|
+
const notFound = error.startsWith("Run not found") ||
|
|
32
|
+
error.startsWith("Artifact not found") ||
|
|
33
|
+
/no such|not found/i.test(error);
|
|
34
|
+
return {
|
|
35
|
+
error,
|
|
36
|
+
status: notFound ? 404 : 400,
|
|
37
|
+
kind: notFound ? "not_found" : "error",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const notFound = opts.policy === "envelope"
|
|
41
|
+
? /not found|no such|envelope/i.test(error)
|
|
42
|
+
: /not found|no such|unknown run/i.test(error);
|
|
43
|
+
return {
|
|
44
|
+
error,
|
|
45
|
+
status: notFound ? 404 : 500,
|
|
46
|
+
kind: notFound ? "not_found" : "error",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -6,10 +6,4 @@ export type ProviderRoutesCtx = {
|
|
|
6
6
|
json: (res: ServerResponse, status: number, body: unknown) => void;
|
|
7
7
|
providerAuthContext?: ProviderAuthContext;
|
|
8
8
|
};
|
|
9
|
-
export declare function providerAuthErrorBody(err: unknown): {
|
|
10
|
-
status: number;
|
|
11
|
-
body: {
|
|
12
|
-
error: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
9
|
export declare function handleProviderRoutes(req: IncomingMessage, res: ServerResponse, ctx: ProviderRoutesCtx): Promise<boolean>;
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import { defaultContext, detectPiHome, getAuthStatus, listProviders, loginWithApiKey, logoutProvider,
|
|
1
|
+
import { defaultContext, detectPiHome, getAuthStatus, listProviders, loginWithApiKey, logoutProvider, } from "../agent/providerAuth.js";
|
|
2
|
+
import { mapProviderAuthError } from "../agent/providerInspect.js";
|
|
2
3
|
import { answerLoginSession, cancelLoginSession, getLoginSession, startOAuthLoginSession, } from "../agent/oauthSessionManager.js";
|
|
3
|
-
export function providerAuthErrorBody(err) {
|
|
4
|
-
if (err instanceof ProviderAuthError) {
|
|
5
|
-
return { status: err.status, body: { error: err.message } };
|
|
6
|
-
}
|
|
7
|
-
return {
|
|
8
|
-
status: 500,
|
|
9
|
-
body: { error: "Provider auth operation failed" },
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
4
|
export async function handleProviderRoutes(req, res, ctx) {
|
|
13
5
|
const { cwd, readJsonBody, json } = ctx;
|
|
14
6
|
const authCtx = ctx.providerAuthContext ?? defaultContext;
|
|
@@ -20,7 +12,7 @@ export async function handleProviderRoutes(req, res, ctx) {
|
|
|
20
12
|
json(res, 200, detectPiHome(cwd));
|
|
21
13
|
}
|
|
22
14
|
catch (err) {
|
|
23
|
-
const mapped =
|
|
15
|
+
const mapped = mapProviderAuthError(err);
|
|
24
16
|
json(res, mapped.status, mapped.body);
|
|
25
17
|
}
|
|
26
18
|
return true;
|
|
@@ -30,7 +22,7 @@ export async function handleProviderRoutes(req, res, ctx) {
|
|
|
30
22
|
json(res, 200, await listProviders(cwd, authCtx));
|
|
31
23
|
}
|
|
32
24
|
catch (err) {
|
|
33
|
-
const mapped =
|
|
25
|
+
const mapped = mapProviderAuthError(err);
|
|
34
26
|
json(res, mapped.status, mapped.body);
|
|
35
27
|
}
|
|
36
28
|
return true;
|
|
@@ -44,7 +36,7 @@ export async function handleProviderRoutes(req, res, ctx) {
|
|
|
44
36
|
});
|
|
45
37
|
}
|
|
46
38
|
catch (err) {
|
|
47
|
-
const mapped =
|
|
39
|
+
const mapped = mapProviderAuthError(err);
|
|
48
40
|
json(res, mapped.status, mapped.body);
|
|
49
41
|
}
|
|
50
42
|
return true;
|
|
@@ -70,7 +62,7 @@ export async function handleProviderRoutes(req, res, ctx) {
|
|
|
70
62
|
json(res, 200, { ok: true, session });
|
|
71
63
|
}
|
|
72
64
|
catch (err) {
|
|
73
|
-
const mapped =
|
|
65
|
+
const mapped = mapProviderAuthError(err);
|
|
74
66
|
json(res, mapped.status, mapped.body);
|
|
75
67
|
}
|
|
76
68
|
return true;
|
|
@@ -89,7 +81,7 @@ export async function handleProviderRoutes(req, res, ctx) {
|
|
|
89
81
|
json(res, 200, { ok: true, provider });
|
|
90
82
|
}
|
|
91
83
|
catch (err) {
|
|
92
|
-
const mapped =
|
|
84
|
+
const mapped = mapProviderAuthError(err);
|
|
93
85
|
json(res, mapped.status, mapped.body);
|
|
94
86
|
}
|
|
95
87
|
return true;
|
|
@@ -135,7 +127,7 @@ export async function handleProviderRoutes(req, res, ctx) {
|
|
|
135
127
|
json(res, 200, { ok: true, session });
|
|
136
128
|
}
|
|
137
129
|
catch (err) {
|
|
138
|
-
const mapped =
|
|
130
|
+
const mapped = mapProviderAuthError(err);
|
|
139
131
|
json(res, mapped.status, mapped.body);
|
|
140
132
|
}
|
|
141
133
|
return true;
|
|
@@ -157,7 +149,7 @@ export async function handleProviderRoutes(req, res, ctx) {
|
|
|
157
149
|
json(res, 200, { ok: true, session });
|
|
158
150
|
}
|
|
159
151
|
catch (err) {
|
|
160
|
-
const mapped =
|
|
152
|
+
const mapped = mapProviderAuthError(err);
|
|
161
153
|
json(res, mapped.status, mapped.body);
|
|
162
154
|
}
|
|
163
155
|
return true;
|
|
@@ -177,7 +169,7 @@ export async function handleProviderRoutes(req, res, ctx) {
|
|
|
177
169
|
json(res, 200, { ok: true, provider });
|
|
178
170
|
}
|
|
179
171
|
catch (err) {
|
|
180
|
-
const mapped =
|
|
172
|
+
const mapped = mapProviderAuthError(err);
|
|
181
173
|
json(res, mapped.status, mapped.body);
|
|
182
174
|
}
|
|
183
175
|
return true;
|
|
@@ -330,6 +330,7 @@ export declare function createAskOperatorTool(options: AskOperatorToolOptions):
|
|
|
330
330
|
artifacts: Type.TArray<Type.TString>;
|
|
331
331
|
id: Type.TOptional<Type.TString>;
|
|
332
332
|
}>)[]]>;
|
|
333
|
+
prepareArguments: (args: unknown) => never;
|
|
333
334
|
execute: (_toolCallId: string, params: unknown) => Promise<{
|
|
334
335
|
content: {
|
|
335
336
|
type: "text";
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
11
|
import { Type } from "typebox";
|
|
12
|
+
import { prepareAskOperatorArguments } from "./prepareToolArguments.js";
|
|
12
13
|
export class AskOperatorError extends Error {
|
|
13
14
|
constructor(message) {
|
|
14
15
|
super(message);
|
|
@@ -168,10 +169,11 @@ function parseSubQuestion(value, path) {
|
|
|
168
169
|
};
|
|
169
170
|
}
|
|
170
171
|
export function parseAskOperatorParams(value) {
|
|
171
|
-
|
|
172
|
+
const prepared = prepareAskOperatorArguments(value);
|
|
173
|
+
if (!isRecord(prepared)) {
|
|
172
174
|
throw new AskOperatorError("ask_operator params must be an object");
|
|
173
175
|
}
|
|
174
|
-
const kind =
|
|
176
|
+
const kind = prepared.kind;
|
|
175
177
|
if (typeof kind !== "string") {
|
|
176
178
|
throw new AskOperatorError("kind is required");
|
|
177
179
|
}
|
|
@@ -181,36 +183,36 @@ export function parseAskOperatorParams(value) {
|
|
|
181
183
|
kind !== "artifact_backed") {
|
|
182
184
|
throw new AskOperatorError(`unsupported prompt kind "${kind}" (allowed: free_text, confirm, multi_question, artifact_backed)`);
|
|
183
185
|
}
|
|
184
|
-
const id = optionalId(
|
|
186
|
+
const id = optionalId(prepared.id, "id");
|
|
185
187
|
if (kind === "free_text" || kind === "confirm") {
|
|
186
188
|
return {
|
|
187
189
|
kind,
|
|
188
|
-
message: requireNonEmptyString(
|
|
190
|
+
message: requireNonEmptyString(prepared.message, "message"),
|
|
189
191
|
...(id !== undefined ? { id } : {}),
|
|
190
192
|
};
|
|
191
193
|
}
|
|
192
194
|
if (kind === "artifact_backed") {
|
|
193
|
-
if (!Array.isArray(
|
|
195
|
+
if (!Array.isArray(prepared.artifacts)) {
|
|
194
196
|
throw new AskOperatorError("artifacts must be an array");
|
|
195
197
|
}
|
|
196
|
-
if (
|
|
198
|
+
if (prepared.artifacts.length < 1) {
|
|
197
199
|
throw new AskOperatorError("artifacts must contain at least one path");
|
|
198
200
|
}
|
|
199
|
-
const artifacts =
|
|
201
|
+
const artifacts = prepared.artifacts.map((item, index) => requireNonEmptyString(item, `artifacts[${index}]`));
|
|
200
202
|
return {
|
|
201
203
|
kind,
|
|
202
|
-
message: requireNonEmptyString(
|
|
204
|
+
message: requireNonEmptyString(prepared.message, "message"),
|
|
203
205
|
artifacts,
|
|
204
206
|
...(id !== undefined ? { id } : {}),
|
|
205
207
|
};
|
|
206
208
|
}
|
|
207
|
-
if (!Array.isArray(
|
|
209
|
+
if (!Array.isArray(prepared.questions)) {
|
|
208
210
|
throw new AskOperatorError("questions must be an array");
|
|
209
211
|
}
|
|
210
|
-
if (
|
|
212
|
+
if (prepared.questions.length < 1) {
|
|
211
213
|
throw new AskOperatorError("questions must contain at least one item");
|
|
212
214
|
}
|
|
213
|
-
const questions =
|
|
215
|
+
const questions = prepared.questions.map((item, index) => parseSubQuestion(item, `questions[${index}]`));
|
|
214
216
|
return {
|
|
215
217
|
kind,
|
|
216
218
|
questions,
|
|
@@ -401,6 +403,7 @@ export function createAskOperatorTool(options) {
|
|
|
401
403
|
label: "Ask operator",
|
|
402
404
|
description: `Ask the operator a question and wait for their answer. Supports ${kindList} prompts. Does not complete the stage — call emit_stage_envelope when finished.`,
|
|
403
405
|
parameters: askOperatorParamsSchemaFor(allowedKinds),
|
|
406
|
+
prepareArguments: prepareAskOperatorArguments,
|
|
404
407
|
execute: async (_toolCallId, params) => {
|
|
405
408
|
let prompt;
|
|
406
409
|
try {
|
|
@@ -37,6 +37,7 @@ export declare function createEmitStageEnvelopeTool(capture: EmitCapture, payloa
|
|
|
37
37
|
stage_id: Type.TOptional<Type.TString>;
|
|
38
38
|
notes: Type.TOptional<Type.TString>;
|
|
39
39
|
}>;
|
|
40
|
+
prepareArguments: (args: unknown) => never;
|
|
40
41
|
execute: (_toolCallId: string, params: unknown) => Promise<{
|
|
41
42
|
content: {
|
|
42
43
|
type: "text";
|
|
@@ -15,6 +15,7 @@ import { assertFeedbackLoopAction } from "../envelope/feedbackLoop.js";
|
|
|
15
15
|
import { normalizeForkChoice } from "../envelope/forkChoice.js";
|
|
16
16
|
import { assertEnvelopePayload, compilePayloadSchema, } from "../envelope/payloadSchema.js";
|
|
17
17
|
import { assertPreEmitChecks, } from "../envelope/preEmitChecks.js";
|
|
18
|
+
import { prepareEmitStageEnvelopeArguments } from "./prepareToolArguments.js";
|
|
18
19
|
function stringLiteralsSchema(values) {
|
|
19
20
|
if (values.length === 1) {
|
|
20
21
|
return Type.Literal(values[0]);
|
|
@@ -78,12 +79,13 @@ export function createEmitStageEnvelopeTool(capture, payloadSchema, forkEmitCont
|
|
|
78
79
|
stage_id: Type.Optional(Type.String()),
|
|
79
80
|
notes: Type.Optional(Type.String()),
|
|
80
81
|
}),
|
|
82
|
+
prepareArguments: prepareEmitStageEnvelopeArguments,
|
|
81
83
|
execute: async (_toolCallId, params) => {
|
|
82
84
|
if (capture.envelope) {
|
|
83
85
|
return toolResult("Envelope already accepted; later emits are ignored.", { advancing: isAdvancingEnvelope(capture.envelope), error: "" }, { terminate: true });
|
|
84
86
|
}
|
|
85
87
|
try {
|
|
86
|
-
const envelope = assertRequiredEnvelope(params);
|
|
88
|
+
const envelope = assertRequiredEnvelope(prepareEmitStageEnvelopeArguments(params));
|
|
87
89
|
assertFeedbackLoopAction(envelope, feedbackLoopEmitContext);
|
|
88
90
|
const isSendBack = envelope.feedback_loop?.action === "send_back";
|
|
89
91
|
if (envelope.status !== "failure" &&
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function coerceJsonArrayField(args: Record<string, unknown>, key: string): void;
|
|
2
|
+
export declare function coerceJsonObjectField(args: Record<string, unknown>, key: string): void;
|
|
3
|
+
export declare function prepareAskOperatorArguments(input: unknown): unknown;
|
|
4
|
+
export declare function prepareEmitStageEnvelopeArguments(input: unknown): unknown;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
function isRecord(value) {
|
|
2
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function parseJsonArrayString(value) {
|
|
5
|
+
if (typeof value !== "string")
|
|
6
|
+
return undefined;
|
|
7
|
+
const trimmed = value.trim();
|
|
8
|
+
if (!trimmed.startsWith("["))
|
|
9
|
+
return undefined;
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(trimmed);
|
|
12
|
+
return Array.isArray(parsed) ? parsed : undefined;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function parseJsonObjectString(value) {
|
|
19
|
+
if (typeof value !== "string")
|
|
20
|
+
return undefined;
|
|
21
|
+
const trimmed = value.trim();
|
|
22
|
+
if (!trimmed.startsWith("{"))
|
|
23
|
+
return undefined;
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(trimmed);
|
|
26
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function coerceJsonArrayField(args, key) {
|
|
33
|
+
const parsed = parseJsonArrayString(args[key]);
|
|
34
|
+
if (parsed !== undefined) {
|
|
35
|
+
args[key] = parsed;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function coerceJsonObjectField(args, key) {
|
|
39
|
+
const parsed = parseJsonObjectString(args[key]);
|
|
40
|
+
if (parsed !== undefined) {
|
|
41
|
+
args[key] = parsed;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function coerceJsonObjectArrayElements(args, key) {
|
|
45
|
+
const value = args[key];
|
|
46
|
+
if (!Array.isArray(value))
|
|
47
|
+
return;
|
|
48
|
+
args[key] = value.map((item) => parseJsonObjectString(item) ?? item);
|
|
49
|
+
}
|
|
50
|
+
export function prepareAskOperatorArguments(input) {
|
|
51
|
+
if (!isRecord(input))
|
|
52
|
+
return input;
|
|
53
|
+
const args = { ...input };
|
|
54
|
+
coerceJsonArrayField(args, "questions");
|
|
55
|
+
coerceJsonObjectArrayElements(args, "questions");
|
|
56
|
+
coerceJsonArrayField(args, "artifacts");
|
|
57
|
+
return args;
|
|
58
|
+
}
|
|
59
|
+
export function prepareEmitStageEnvelopeArguments(input) {
|
|
60
|
+
if (!isRecord(input))
|
|
61
|
+
return input;
|
|
62
|
+
const args = { ...input };
|
|
63
|
+
coerceJsonObjectField(args, "payload");
|
|
64
|
+
coerceJsonObjectField(args, "feedback_loop");
|
|
65
|
+
coerceJsonArrayField(args, "artifacts");
|
|
66
|
+
coerceJsonArrayField(args, "fork_choice");
|
|
67
|
+
coerceJsonArrayField(args, "checklist_attestations");
|
|
68
|
+
coerceJsonObjectArrayElements(args, "checklist_attestations");
|
|
69
|
+
if (Array.isArray(args.checklist_attestations)) {
|
|
70
|
+
args.checklist_attestations = args.checklist_attestations.map((item) => {
|
|
71
|
+
if (!isRecord(item))
|
|
72
|
+
return item;
|
|
73
|
+
const next = { ...item };
|
|
74
|
+
coerceJsonArrayField(next, "items");
|
|
75
|
+
return next;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return args;
|
|
79
|
+
}
|
package/dist/types/pipeline.d.ts
CHANGED
|
@@ -10,6 +10,14 @@ export type PipelineConfig = {
|
|
|
10
10
|
/** Pipeline-file `$ref` root (`#/schemas/<name>`). */
|
|
11
11
|
schemas?: Record<string, unknown>;
|
|
12
12
|
};
|
|
13
|
+
/** An inline pipeline definition authored directly in a `start_run` call, with no backing file. */
|
|
14
|
+
export type InlinePipelineDefinition = {
|
|
15
|
+
id: string;
|
|
16
|
+
stages: Array<Record<string, unknown>>;
|
|
17
|
+
agent?: unknown;
|
|
18
|
+
model?: unknown;
|
|
19
|
+
schemas?: unknown;
|
|
20
|
+
};
|
|
13
21
|
export type PipelineForkConfig = {
|
|
14
22
|
select: "one" | "subset";
|
|
15
23
|
allow_none: boolean;
|