stageflow 0.8.0 → 0.12.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 +24 -14
- 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 +301 -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 +6 -3
- package/dist/agent/piAdapter.js +129 -21
- package/dist/agent/port.d.ts +50 -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 +22 -0
- package/dist/cli/runsCommand.js +773 -0
- package/dist/cli/validateOutput.d.ts +2 -2
- package/dist/cli/validateOutput.js +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +43 -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 +10 -1
- package/dist/config/loadStage.js +138 -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 +49 -6
- 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 +16 -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/validateCatalog.d.ts +1 -1
- 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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp/catalogTools.js +4 -2
- package/dist/mcp/controlTools.js +72 -0
- package/dist/mcp/waitingGates.d.ts +2 -1
- package/dist/mcp/waitingGates.js +17 -1
- package/dist/package-meta.d.ts +1 -0
- package/dist/package-meta.js +1 -0
- 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 +61 -1
- package/dist/runtime/runManager.js +501 -7
- package/dist/runtime/runRetryCoordinator.d.ts +8 -1
- package/dist/runtime/runRetryCoordinator.js +47 -10
- package/dist/runtime/stageAttemptBootstrap.d.ts +3 -1
- package/dist/runtime/stageAttemptBootstrap.js +79 -1
- 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 +29 -0
- package/dist/runtime/verifiedStageExecution.js +119 -0
- package/dist/server/bootstrap.js +1 -0
- package/dist/server/http.js +114 -0
- package/dist/server/operatorResults.js +2 -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 +39 -2
- package/dist/types/preEmitCheck.d.ts +18 -0
- package/dist/types/preEmitCheck.js +1 -0
- package/dist/types/stage.d.ts +13 -0
- package/dist/types/stageflowManifest.d.ts +2 -0
- package/dist/ui/assets/index-CXGz4nL7.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 +2 -1
- package/skills/stageflow/references/control-surface.md +4 -2
- package/skills/stageflow-author/references/catalog-mapping.md +26 -2
- package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
- package/skills/stageflow-run/SKILL.md +56 -25
- package/skills/stageflow-run/references/mcp-call.md +2 -2
- package/skills/stageflow-run/references/native-question-ui.md +78 -0
- package/dist/ui/assets/index-CSoGDA3A.js +0 -118
package/dist/server/http.js
CHANGED
|
@@ -9,6 +9,7 @@ import { browseCatalog } from "../config/browseCatalog.js";
|
|
|
9
9
|
import { listExtensions } from "../config/listExtensions.js";
|
|
10
10
|
import { listSkills } from "../config/listSkills.js";
|
|
11
11
|
import { readRunArtifact } from "../mcp/readArtifact.js";
|
|
12
|
+
import { readStageVerificationHistory } from "../runstore/verificationHistory.js";
|
|
12
13
|
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
13
14
|
import { INVALID_SLOT_COUNT_MESSAGE, parseSlotCount, } from "../runtime/settingsFile.js";
|
|
14
15
|
import { isTaskFile } from "../runtime/taskInput.js";
|
|
@@ -87,7 +88,10 @@ function isMutatingApi(method, pathname) {
|
|
|
87
88
|
pathname === "/api/pipelines" ||
|
|
88
89
|
/^\/api\/runs\/[^/]+\/rerun$/.test(pathname) ||
|
|
89
90
|
/^\/api\/runs\/[^/]+\/stages\/[^/]+\/answer$/.test(pathname) ||
|
|
91
|
+
/^\/api\/runs\/[^/]+\/stages\/[^/]+\/feedback-decision$/.test(pathname) ||
|
|
90
92
|
/^\/api\/runs\/[^/]+\/stages\/[^/]+\/retry$/.test(pathname) ||
|
|
93
|
+
/^\/api\/runs\/[^/]+\/stages\/[^/]+\/recovery$/.test(pathname) ||
|
|
94
|
+
/^\/api\/runs\/[^/]+\/stages\/[^/]+\/recovery\/stop$/.test(pathname) ||
|
|
91
95
|
/^\/api\/runs\/[^/]+\/stages\/[^/]+\/abandon$/.test(pathname) ||
|
|
92
96
|
/^\/api\/providers\/[^/]+\/login$/.test(pathname) ||
|
|
93
97
|
/^\/api\/providers\/[^/]+\/login\/[^/]+\/answer$/.test(pathname) ||
|
|
@@ -166,6 +170,9 @@ function assertLoopbackHttpAccess(req, res) {
|
|
|
166
170
|
}
|
|
167
171
|
export function defaultUiDistDir() {
|
|
168
172
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
173
|
+
if (path.basename(path.dirname(here)) === "src") {
|
|
174
|
+
return path.resolve(here, "../../dist/ui");
|
|
175
|
+
}
|
|
169
176
|
return path.resolve(here, "../ui");
|
|
170
177
|
}
|
|
171
178
|
export async function startUiServer(options) {
|
|
@@ -219,6 +226,20 @@ export async function startUiServer(options) {
|
|
|
219
226
|
}
|
|
220
227
|
return true;
|
|
221
228
|
}
|
|
229
|
+
const verificationMatch = pathname.match(/^\/api\/runs\/([^/]+)\/stages\/([^/]+)\/verification$/);
|
|
230
|
+
if (method === "GET" && verificationMatch) {
|
|
231
|
+
const runId = decodeURIComponent(verificationMatch[1] ?? "");
|
|
232
|
+
const stageId = decodeURIComponent(verificationMatch[2] ?? "");
|
|
233
|
+
try {
|
|
234
|
+
json(res, 200, await readStageVerificationHistory(store, runId, stageId));
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
238
|
+
const notFound = /not found|no such/i.test(message);
|
|
239
|
+
json(res, notFound ? 404 : 500, { error: message });
|
|
240
|
+
}
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
222
243
|
if (method === "GET" && pathname.startsWith("/api/runs/")) {
|
|
223
244
|
const rest = pathname.slice("/api/runs/".length);
|
|
224
245
|
if (rest && !rest.includes("/")) {
|
|
@@ -290,6 +311,55 @@ export async function startUiServer(options) {
|
|
|
290
311
|
json(res, 202, { ok: true });
|
|
291
312
|
return true;
|
|
292
313
|
}
|
|
314
|
+
const feedbackDecisionMatch = pathname.match(/^\/api\/runs\/([^/]+)\/stages\/([^/]+)\/feedback-decision$/);
|
|
315
|
+
if (method === "POST" && feedbackDecisionMatch) {
|
|
316
|
+
const runId = decodeURIComponent(feedbackDecisionMatch[1] ?? "");
|
|
317
|
+
const stageId = decodeURIComponent(feedbackDecisionMatch[2] ?? "");
|
|
318
|
+
let body;
|
|
319
|
+
try {
|
|
320
|
+
body = await readJsonBody(req);
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
327
|
+
json(res, 400, { error: "Body must be a JSON object" });
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
const raw = body;
|
|
331
|
+
if (raw.decision !== "extend" &&
|
|
332
|
+
raw.decision !== "continue" &&
|
|
333
|
+
raw.decision !== "abandon") {
|
|
334
|
+
json(res, 400, {
|
|
335
|
+
error: "decision must be extend, continue, or abandon",
|
|
336
|
+
});
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
if (raw.loopId !== undefined && typeof raw.loopId !== "string") {
|
|
340
|
+
json(res, 400, { error: "loopId must be a string" });
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
if (raw.reason !== undefined && typeof raw.reason !== "string") {
|
|
344
|
+
json(res, 400, { error: "reason must be a string" });
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
const result = await manager.decideFeedbackLoop(runId, stageId, {
|
|
348
|
+
decision: raw.decision,
|
|
349
|
+
...(raw.loopId !== undefined ? { loopId: raw.loopId } : {}),
|
|
350
|
+
...(raw.reason !== undefined ? { reason: raw.reason } : {}),
|
|
351
|
+
});
|
|
352
|
+
if (!result.ok) {
|
|
353
|
+
json(res, result.status ?? 500, { error: result.reason });
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
json(res, 202, {
|
|
357
|
+
ok: true,
|
|
358
|
+
effect: result.effect,
|
|
359
|
+
loopId: result.loopId,
|
|
360
|
+
});
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
293
363
|
const retryMatch = pathname.match(/^\/api\/runs\/([^/]+)\/stages\/([^/]+)\/retry$/);
|
|
294
364
|
if (method === "POST" && retryMatch) {
|
|
295
365
|
await readJsonBody(req);
|
|
@@ -307,6 +377,50 @@ export async function startUiServer(options) {
|
|
|
307
377
|
});
|
|
308
378
|
return true;
|
|
309
379
|
}
|
|
380
|
+
const recoverMatch = pathname.match(/^\/api\/runs\/([^/]+)\/stages\/([^/]+)\/recovery$/);
|
|
381
|
+
if (method === "POST" && recoverMatch) {
|
|
382
|
+
let body;
|
|
383
|
+
try {
|
|
384
|
+
body = await readJsonBody(req);
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
const guidance = body !== null && typeof body === "object" && !Array.isArray(body)
|
|
391
|
+
? body.guidance
|
|
392
|
+
: undefined;
|
|
393
|
+
if (guidance !== undefined && typeof guidance !== "string") {
|
|
394
|
+
json(res, 400, { error: "guidance must be a string" });
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
const runId = decodeURIComponent(recoverMatch[1] ?? "");
|
|
398
|
+
const stageId = decodeURIComponent(recoverMatch[2] ?? "");
|
|
399
|
+
const result = await manager.recoverManualStage(runId, stageId, guidance);
|
|
400
|
+
if (!result.ok) {
|
|
401
|
+
json(res, result.status ?? 500, mapRetryStageFailure(result));
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
json(res, 202, {
|
|
405
|
+
runId: result.runId,
|
|
406
|
+
stageId: result.stageId,
|
|
407
|
+
attemptIndex: result.attemptIndex,
|
|
408
|
+
});
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
const stopRecoveryMatch = pathname.match(/^\/api\/runs\/([^/]+)\/stages\/([^/]+)\/recovery\/stop$/);
|
|
412
|
+
if (method === "POST" && stopRecoveryMatch) {
|
|
413
|
+
await readJsonBody(req);
|
|
414
|
+
const runId = decodeURIComponent(stopRecoveryMatch[1] ?? "");
|
|
415
|
+
const stageId = decodeURIComponent(stopRecoveryMatch[2] ?? "");
|
|
416
|
+
const result = await manager.stopManualRecovery(runId, stageId);
|
|
417
|
+
if (!result.ok) {
|
|
418
|
+
json(res, result.status ?? 500, { error: result.reason });
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
json(res, 202, result);
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
310
424
|
const abandonMatch = pathname.match(/^\/api\/runs\/([^/]+)\/stages\/([^/]+)\/abandon$/);
|
|
311
425
|
if (method === "POST" && abandonMatch) {
|
|
312
426
|
await readJsonBody(req);
|
|
@@ -9,6 +9,8 @@ export function inferRetryStageErrorCode(reason) {
|
|
|
9
9
|
return "run_not_retryable";
|
|
10
10
|
if (/stage is not failed/i.test(reason))
|
|
11
11
|
return "stage_not_failed";
|
|
12
|
+
if (/manual recovery/i.test(reason))
|
|
13
|
+
return "manual_recovery_required";
|
|
12
14
|
return undefined;
|
|
13
15
|
}
|
|
14
16
|
export function mapRetryStageFailure(result) {
|
|
@@ -107,6 +107,96 @@ export declare const askOperatorParamsSchema: Type.TUnion<[Type.TObject<{
|
|
|
107
107
|
artifacts: Type.TArray<Type.TString>;
|
|
108
108
|
id: Type.TOptional<Type.TString>;
|
|
109
109
|
}>]>;
|
|
110
|
+
/**
|
|
111
|
+
* Narrows the ask_operator params schema to only the declared gate kinds
|
|
112
|
+
* (stage `gate_kinds` allowlist). Omitted/empty falls back to the full
|
|
113
|
+
* union so back-compat stages keep accepting every kind.
|
|
114
|
+
*/
|
|
115
|
+
export declare function askOperatorParamsSchemaFor(allowedKinds?: readonly AskOperatorKind[]): Type.TObject<{
|
|
116
|
+
kind: Type.TLiteral<"free_text">;
|
|
117
|
+
message: Type.TString;
|
|
118
|
+
id: Type.TOptional<Type.TString>;
|
|
119
|
+
}> | Type.TObject<{
|
|
120
|
+
kind: Type.TLiteral<"confirm">;
|
|
121
|
+
message: Type.TString;
|
|
122
|
+
id: Type.TOptional<Type.TString>;
|
|
123
|
+
}> | Type.TObject<{
|
|
124
|
+
kind: Type.TLiteral<"multi_question">;
|
|
125
|
+
questions: Type.TArray<Type.TObject<{
|
|
126
|
+
kind: Type.TUnion<[Type.TLiteral<"free_text">, Type.TLiteral<"confirm">]>;
|
|
127
|
+
message: Type.TString;
|
|
128
|
+
id: Type.TOptional<Type.TString>;
|
|
129
|
+
}>>;
|
|
130
|
+
id: Type.TOptional<Type.TString>;
|
|
131
|
+
}> | Type.TObject<{
|
|
132
|
+
kind: Type.TLiteral<"artifact_backed">;
|
|
133
|
+
message: Type.TString;
|
|
134
|
+
artifacts: Type.TArray<Type.TString>;
|
|
135
|
+
id: Type.TOptional<Type.TString>;
|
|
136
|
+
}> | Type.TUnion<[Type.TObject<{
|
|
137
|
+
kind: Type.TLiteral<"free_text">;
|
|
138
|
+
message: Type.TString;
|
|
139
|
+
id: Type.TOptional<Type.TString>;
|
|
140
|
+
}> | Type.TObject<{
|
|
141
|
+
kind: Type.TLiteral<"confirm">;
|
|
142
|
+
message: Type.TString;
|
|
143
|
+
id: Type.TOptional<Type.TString>;
|
|
144
|
+
}> | Type.TObject<{
|
|
145
|
+
kind: Type.TLiteral<"multi_question">;
|
|
146
|
+
questions: Type.TArray<Type.TObject<{
|
|
147
|
+
kind: Type.TUnion<[Type.TLiteral<"free_text">, Type.TLiteral<"confirm">]>;
|
|
148
|
+
message: Type.TString;
|
|
149
|
+
id: Type.TOptional<Type.TString>;
|
|
150
|
+
}>>;
|
|
151
|
+
id: Type.TOptional<Type.TString>;
|
|
152
|
+
}> | Type.TObject<{
|
|
153
|
+
kind: Type.TLiteral<"artifact_backed">;
|
|
154
|
+
message: Type.TString;
|
|
155
|
+
artifacts: Type.TArray<Type.TString>;
|
|
156
|
+
id: Type.TOptional<Type.TString>;
|
|
157
|
+
}>, Type.TObject<{
|
|
158
|
+
kind: Type.TLiteral<"free_text">;
|
|
159
|
+
message: Type.TString;
|
|
160
|
+
id: Type.TOptional<Type.TString>;
|
|
161
|
+
}> | Type.TObject<{
|
|
162
|
+
kind: Type.TLiteral<"confirm">;
|
|
163
|
+
message: Type.TString;
|
|
164
|
+
id: Type.TOptional<Type.TString>;
|
|
165
|
+
}> | Type.TObject<{
|
|
166
|
+
kind: Type.TLiteral<"multi_question">;
|
|
167
|
+
questions: Type.TArray<Type.TObject<{
|
|
168
|
+
kind: Type.TUnion<[Type.TLiteral<"free_text">, Type.TLiteral<"confirm">]>;
|
|
169
|
+
message: Type.TString;
|
|
170
|
+
id: Type.TOptional<Type.TString>;
|
|
171
|
+
}>>;
|
|
172
|
+
id: Type.TOptional<Type.TString>;
|
|
173
|
+
}> | Type.TObject<{
|
|
174
|
+
kind: Type.TLiteral<"artifact_backed">;
|
|
175
|
+
message: Type.TString;
|
|
176
|
+
artifacts: Type.TArray<Type.TString>;
|
|
177
|
+
id: Type.TOptional<Type.TString>;
|
|
178
|
+
}>, ...(Type.TObject<{
|
|
179
|
+
kind: Type.TLiteral<"free_text">;
|
|
180
|
+
message: Type.TString;
|
|
181
|
+
id: Type.TOptional<Type.TString>;
|
|
182
|
+
}> | Type.TObject<{
|
|
183
|
+
kind: Type.TLiteral<"confirm">;
|
|
184
|
+
message: Type.TString;
|
|
185
|
+
id: Type.TOptional<Type.TString>;
|
|
186
|
+
}> | Type.TObject<{
|
|
187
|
+
kind: Type.TLiteral<"multi_question">;
|
|
188
|
+
questions: Type.TArray<Type.TObject<{
|
|
189
|
+
kind: Type.TUnion<[Type.TLiteral<"free_text">, Type.TLiteral<"confirm">]>;
|
|
190
|
+
message: Type.TString;
|
|
191
|
+
id: Type.TOptional<Type.TString>;
|
|
192
|
+
}>>;
|
|
193
|
+
id: Type.TOptional<Type.TString>;
|
|
194
|
+
}> | Type.TObject<{
|
|
195
|
+
kind: Type.TLiteral<"artifact_backed">;
|
|
196
|
+
message: Type.TString;
|
|
197
|
+
artifacts: Type.TArray<Type.TString>;
|
|
198
|
+
id: Type.TOptional<Type.TString>;
|
|
199
|
+
}>)[]]>;
|
|
110
200
|
export declare const askOperatorAnswerSchema: Type.TUnion<[Type.TObject<{
|
|
111
201
|
promptId: Type.TString;
|
|
112
202
|
kind: Type.TLiteral<"free_text">;
|
|
@@ -142,25 +232,28 @@ export declare const parseAnswer: typeof parseAskOperatorAnswer;
|
|
|
142
232
|
export type AskOperatorWaitBridge = {
|
|
143
233
|
requestWait: (prompt: AskOperatorPrompt) => Promise<unknown>;
|
|
144
234
|
};
|
|
145
|
-
export type AskOperatorToolOptions = AskOperatorWaitBridge
|
|
235
|
+
export type AskOperatorToolOptions = AskOperatorWaitBridge & {
|
|
236
|
+
allowedKinds?: readonly AskOperatorKind[];
|
|
237
|
+
};
|
|
146
238
|
export type AskOperatorToolDetails = {
|
|
147
239
|
prompt: AskOperatorPrompt | null;
|
|
148
240
|
answer: AskOperatorAnswer | null;
|
|
149
241
|
error: string;
|
|
150
242
|
};
|
|
243
|
+
export declare function summarizeAnswer(answer: AskOperatorAnswer): string;
|
|
151
244
|
export declare function createAskOperatorTool(options: AskOperatorToolOptions): {
|
|
152
245
|
name: string;
|
|
153
246
|
label: string;
|
|
154
247
|
description: string;
|
|
155
|
-
parameters: Type.
|
|
248
|
+
parameters: Type.TObject<{
|
|
156
249
|
kind: Type.TLiteral<"free_text">;
|
|
157
250
|
message: Type.TString;
|
|
158
251
|
id: Type.TOptional<Type.TString>;
|
|
159
|
-
}
|
|
252
|
+
}> | Type.TObject<{
|
|
160
253
|
kind: Type.TLiteral<"confirm">;
|
|
161
254
|
message: Type.TString;
|
|
162
255
|
id: Type.TOptional<Type.TString>;
|
|
163
|
-
}
|
|
256
|
+
}> | Type.TObject<{
|
|
164
257
|
kind: Type.TLiteral<"multi_question">;
|
|
165
258
|
questions: Type.TArray<Type.TObject<{
|
|
166
259
|
kind: Type.TUnion<[Type.TLiteral<"free_text">, Type.TLiteral<"confirm">]>;
|
|
@@ -168,12 +261,75 @@ export declare function createAskOperatorTool(options: AskOperatorToolOptions):
|
|
|
168
261
|
id: Type.TOptional<Type.TString>;
|
|
169
262
|
}>>;
|
|
170
263
|
id: Type.TOptional<Type.TString>;
|
|
264
|
+
}> | Type.TObject<{
|
|
265
|
+
kind: Type.TLiteral<"artifact_backed">;
|
|
266
|
+
message: Type.TString;
|
|
267
|
+
artifacts: Type.TArray<Type.TString>;
|
|
268
|
+
id: Type.TOptional<Type.TString>;
|
|
269
|
+
}> | Type.TUnion<[Type.TObject<{
|
|
270
|
+
kind: Type.TLiteral<"free_text">;
|
|
271
|
+
message: Type.TString;
|
|
272
|
+
id: Type.TOptional<Type.TString>;
|
|
273
|
+
}> | Type.TObject<{
|
|
274
|
+
kind: Type.TLiteral<"confirm">;
|
|
275
|
+
message: Type.TString;
|
|
276
|
+
id: Type.TOptional<Type.TString>;
|
|
277
|
+
}> | Type.TObject<{
|
|
278
|
+
kind: Type.TLiteral<"multi_question">;
|
|
279
|
+
questions: Type.TArray<Type.TObject<{
|
|
280
|
+
kind: Type.TUnion<[Type.TLiteral<"free_text">, Type.TLiteral<"confirm">]>;
|
|
281
|
+
message: Type.TString;
|
|
282
|
+
id: Type.TOptional<Type.TString>;
|
|
283
|
+
}>>;
|
|
284
|
+
id: Type.TOptional<Type.TString>;
|
|
285
|
+
}> | Type.TObject<{
|
|
286
|
+
kind: Type.TLiteral<"artifact_backed">;
|
|
287
|
+
message: Type.TString;
|
|
288
|
+
artifacts: Type.TArray<Type.TString>;
|
|
289
|
+
id: Type.TOptional<Type.TString>;
|
|
171
290
|
}>, Type.TObject<{
|
|
291
|
+
kind: Type.TLiteral<"free_text">;
|
|
292
|
+
message: Type.TString;
|
|
293
|
+
id: Type.TOptional<Type.TString>;
|
|
294
|
+
}> | Type.TObject<{
|
|
295
|
+
kind: Type.TLiteral<"confirm">;
|
|
296
|
+
message: Type.TString;
|
|
297
|
+
id: Type.TOptional<Type.TString>;
|
|
298
|
+
}> | Type.TObject<{
|
|
299
|
+
kind: Type.TLiteral<"multi_question">;
|
|
300
|
+
questions: Type.TArray<Type.TObject<{
|
|
301
|
+
kind: Type.TUnion<[Type.TLiteral<"free_text">, Type.TLiteral<"confirm">]>;
|
|
302
|
+
message: Type.TString;
|
|
303
|
+
id: Type.TOptional<Type.TString>;
|
|
304
|
+
}>>;
|
|
305
|
+
id: Type.TOptional<Type.TString>;
|
|
306
|
+
}> | Type.TObject<{
|
|
307
|
+
kind: Type.TLiteral<"artifact_backed">;
|
|
308
|
+
message: Type.TString;
|
|
309
|
+
artifacts: Type.TArray<Type.TString>;
|
|
310
|
+
id: Type.TOptional<Type.TString>;
|
|
311
|
+
}>, ...(Type.TObject<{
|
|
312
|
+
kind: Type.TLiteral<"free_text">;
|
|
313
|
+
message: Type.TString;
|
|
314
|
+
id: Type.TOptional<Type.TString>;
|
|
315
|
+
}> | Type.TObject<{
|
|
316
|
+
kind: Type.TLiteral<"confirm">;
|
|
317
|
+
message: Type.TString;
|
|
318
|
+
id: Type.TOptional<Type.TString>;
|
|
319
|
+
}> | Type.TObject<{
|
|
320
|
+
kind: Type.TLiteral<"multi_question">;
|
|
321
|
+
questions: Type.TArray<Type.TObject<{
|
|
322
|
+
kind: Type.TUnion<[Type.TLiteral<"free_text">, Type.TLiteral<"confirm">]>;
|
|
323
|
+
message: Type.TString;
|
|
324
|
+
id: Type.TOptional<Type.TString>;
|
|
325
|
+
}>>;
|
|
326
|
+
id: Type.TOptional<Type.TString>;
|
|
327
|
+
}> | Type.TObject<{
|
|
172
328
|
kind: Type.TLiteral<"artifact_backed">;
|
|
173
329
|
message: Type.TString;
|
|
174
330
|
artifacts: Type.TArray<Type.TString>;
|
|
175
331
|
id: Type.TOptional<Type.TString>;
|
|
176
|
-
}>]>;
|
|
332
|
+
}>)[]]>;
|
|
177
333
|
execute: (_toolCallId: string, params: unknown) => Promise<{
|
|
178
334
|
content: {
|
|
179
335
|
type: "text";
|
|
@@ -30,29 +30,53 @@ const multiQuestionItemSchema = Type.Object({
|
|
|
30
30
|
message: Type.String({ minLength: 1 }),
|
|
31
31
|
id: Type.Optional(Type.String()),
|
|
32
32
|
});
|
|
33
|
+
const freeTextParamsSchema = Type.Object({
|
|
34
|
+
kind: Type.Literal("free_text"),
|
|
35
|
+
message: Type.String({ minLength: 1 }),
|
|
36
|
+
id: Type.Optional(Type.String()),
|
|
37
|
+
});
|
|
38
|
+
const confirmParamsSchema = Type.Object({
|
|
39
|
+
kind: Type.Literal("confirm"),
|
|
40
|
+
message: Type.String({ minLength: 1 }),
|
|
41
|
+
id: Type.Optional(Type.String()),
|
|
42
|
+
});
|
|
43
|
+
const multiQuestionParamsSchema = Type.Object({
|
|
44
|
+
kind: Type.Literal("multi_question"),
|
|
45
|
+
questions: Type.Array(multiQuestionItemSchema, { minItems: 1 }),
|
|
46
|
+
id: Type.Optional(Type.String()),
|
|
47
|
+
});
|
|
48
|
+
const artifactBackedParamsSchema = Type.Object({
|
|
49
|
+
kind: Type.Literal("artifact_backed"),
|
|
50
|
+
message: Type.String({ minLength: 1 }),
|
|
51
|
+
artifacts: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
|
|
52
|
+
id: Type.Optional(Type.String()),
|
|
53
|
+
});
|
|
54
|
+
const KIND_PARAM_SCHEMAS = {
|
|
55
|
+
free_text: freeTextParamsSchema,
|
|
56
|
+
confirm: confirmParamsSchema,
|
|
57
|
+
multi_question: multiQuestionParamsSchema,
|
|
58
|
+
artifact_backed: artifactBackedParamsSchema,
|
|
59
|
+
};
|
|
33
60
|
export const askOperatorParamsSchema = Type.Union([
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}),
|
|
39
|
-
Type.Object({
|
|
40
|
-
kind: Type.Literal("confirm"),
|
|
41
|
-
message: Type.String({ minLength: 1 }),
|
|
42
|
-
id: Type.Optional(Type.String()),
|
|
43
|
-
}),
|
|
44
|
-
Type.Object({
|
|
45
|
-
kind: Type.Literal("multi_question"),
|
|
46
|
-
questions: Type.Array(multiQuestionItemSchema, { minItems: 1 }),
|
|
47
|
-
id: Type.Optional(Type.String()),
|
|
48
|
-
}),
|
|
49
|
-
Type.Object({
|
|
50
|
-
kind: Type.Literal("artifact_backed"),
|
|
51
|
-
message: Type.String({ minLength: 1 }),
|
|
52
|
-
artifacts: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
|
|
53
|
-
id: Type.Optional(Type.String()),
|
|
54
|
-
}),
|
|
61
|
+
freeTextParamsSchema,
|
|
62
|
+
confirmParamsSchema,
|
|
63
|
+
multiQuestionParamsSchema,
|
|
64
|
+
artifactBackedParamsSchema,
|
|
55
65
|
]);
|
|
66
|
+
/**
|
|
67
|
+
* Narrows the ask_operator params schema to only the declared gate kinds
|
|
68
|
+
* (stage `gate_kinds` allowlist). Omitted/empty falls back to the full
|
|
69
|
+
* union so back-compat stages keep accepting every kind.
|
|
70
|
+
*/
|
|
71
|
+
export function askOperatorParamsSchemaFor(allowedKinds) {
|
|
72
|
+
if (!allowedKinds || allowedKinds.length === 0) {
|
|
73
|
+
return askOperatorParamsSchema;
|
|
74
|
+
}
|
|
75
|
+
const schemas = allowedKinds.map((kind) => KIND_PARAM_SCHEMAS[kind]);
|
|
76
|
+
if (schemas.length === 1)
|
|
77
|
+
return schemas[0];
|
|
78
|
+
return Type.Union(schemas);
|
|
79
|
+
}
|
|
56
80
|
const decisionSchema = Type.Union([
|
|
57
81
|
Type.Literal("accept"),
|
|
58
82
|
Type.Literal("reject"),
|
|
@@ -352,7 +376,7 @@ function toolResult(text, details, options) {
|
|
|
352
376
|
...(options?.isError ? { isError: true } : {}),
|
|
353
377
|
};
|
|
354
378
|
}
|
|
355
|
-
function summarizeAnswer(answer) {
|
|
379
|
+
export function summarizeAnswer(answer) {
|
|
356
380
|
if (answer.kind === "free_text") {
|
|
357
381
|
return `Operator answered: ${answer.text}`;
|
|
358
382
|
}
|
|
@@ -365,16 +389,25 @@ function summarizeAnswer(answer) {
|
|
|
365
389
|
return `Operator answered ${count} question${count === 1 ? "" : "s"}`;
|
|
366
390
|
}
|
|
367
391
|
export function createAskOperatorTool(options) {
|
|
368
|
-
const { requestWait } = options;
|
|
392
|
+
const { requestWait, allowedKinds } = options;
|
|
393
|
+
const allowed = allowedKinds && allowedKinds.length > 0
|
|
394
|
+
? new Set(allowedKinds)
|
|
395
|
+
: null;
|
|
396
|
+
const kindList = allowed
|
|
397
|
+
? [...allowed].join(", ")
|
|
398
|
+
: ASK_OPERATOR_KINDS.join(", ");
|
|
369
399
|
return {
|
|
370
400
|
name: "ask_operator",
|
|
371
401
|
label: "Ask operator",
|
|
372
|
-
description:
|
|
373
|
-
parameters:
|
|
402
|
+
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
|
+
parameters: askOperatorParamsSchemaFor(allowedKinds),
|
|
374
404
|
execute: async (_toolCallId, params) => {
|
|
375
405
|
let prompt;
|
|
376
406
|
try {
|
|
377
407
|
const parsed = parseAskOperatorParams(params);
|
|
408
|
+
if (allowed && !allowed.has(parsed.kind)) {
|
|
409
|
+
throw new AskOperatorError(`unsupported prompt kind "${parsed.kind}" (allowed: ${kindList})`);
|
|
410
|
+
}
|
|
378
411
|
prompt = normalizePromptIds(parsed);
|
|
379
412
|
}
|
|
380
413
|
catch (err) {
|
|
@@ -10,13 +10,16 @@
|
|
|
10
10
|
* deadlocks waitForIdle so later pipeline stages never start.
|
|
11
11
|
*/
|
|
12
12
|
import { Type } from "typebox";
|
|
13
|
+
import type { TSchema } from "typebox/type";
|
|
14
|
+
import { type PreEmitCheckOptions } from "../envelope/preEmitChecks.js";
|
|
13
15
|
import type { StageEnvelope } from "../types/envelope.js";
|
|
14
|
-
import type {
|
|
16
|
+
import type { FeedbackLoopConfig } from "../types/pipeline.js";
|
|
17
|
+
import { type CloneEmitContext, type ForkEmitContext } from "../types/forkChoice.js";
|
|
15
18
|
export type EmitCapture = {
|
|
16
19
|
envelope?: StageEnvelope;
|
|
17
20
|
error?: string;
|
|
18
21
|
};
|
|
19
|
-
export declare function createEmitStageEnvelopeTool(capture: EmitCapture, payloadSchema?: unknown, forkEmitContext?: ForkEmitContext, cloneEmitContext?: CloneEmitContext): {
|
|
22
|
+
export declare function createEmitStageEnvelopeTool(capture: EmitCapture, payloadSchema?: unknown, forkEmitContext?: ForkEmitContext, cloneEmitContext?: CloneEmitContext, preEmitCheckOptions?: PreEmitCheckOptions, feedbackLoopEmitContext?: FeedbackLoopConfig): {
|
|
20
23
|
name: string;
|
|
21
24
|
label: string;
|
|
22
25
|
description: string;
|
|
@@ -24,17 +27,14 @@ export declare function createEmitStageEnvelopeTool(capture: EmitCapture, payloa
|
|
|
24
27
|
status: Type.TUnion<[Type.TLiteral<"success">, Type.TLiteral<"failure">]>;
|
|
25
28
|
summary: Type.TString;
|
|
26
29
|
artifacts: Type.TArray<Type.TString>;
|
|
27
|
-
payload: Type.TSchema
|
|
30
|
+
payload: Type.TOptional<TSchema>;
|
|
28
31
|
fork_choice: Type.TArray<Type.TString>;
|
|
29
|
-
clone_forks?: Type.TArray<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
envelope: Type.TUnknown;
|
|
36
|
-
}>>>;
|
|
37
|
-
}>> | undefined;
|
|
32
|
+
clone_forks?: Type.TArray<TSchema> | undefined;
|
|
33
|
+
feedback_loop?: Type.TOptional<TSchema> | undefined;
|
|
34
|
+
checklist_attestations: Type.TOptional<Type.TArray<Type.TObject<{
|
|
35
|
+
check_id: Type.TString;
|
|
36
|
+
items: Type.TArray<Type.TString>;
|
|
37
|
+
}>>>;
|
|
38
38
|
stage_id: Type.TOptional<Type.TString>;
|
|
39
39
|
notes: Type.TOptional<Type.TString>;
|
|
40
40
|
}>;
|