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
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK tool wrappers for the stage tool set.
|
|
3
|
+
*
|
|
4
|
+
* The actual envelope/artifact business logic lives in
|
|
5
|
+
* `tools/emitStageEnvelope.ts` / `tools/writeStageArtifact.ts` and is fully
|
|
6
|
+
* SDK-agnostic (typed `params: unknown`, no Pi imports) — this file reuses
|
|
7
|
+
* those `execute` functions as-is and only translates the *declaration*
|
|
8
|
+
* layer (typebox → Zod) and the *result* layer (drops Pi-only `terminate`/
|
|
9
|
+
* `details` fields, keeping just the MCP `CallToolResult` shape).
|
|
10
|
+
*
|
|
11
|
+
* `ask_operator` is different: `tools/askOperator.ts`'s own `execute` blocks
|
|
12
|
+
* on an injected wait bridge (Pi's in-process mechanism), which is exactly
|
|
13
|
+
* what Phase 3's "never let it go dangling" design avoids. This file reuses
|
|
14
|
+
* only the pure parsing/validation helpers from that module
|
|
15
|
+
* (`parseAskOperatorParams`, `normalizePromptIds`) and writes its own
|
|
16
|
+
* non-blocking handler: record the prompt on `capture`, return an immediate
|
|
17
|
+
* placeholder result, and rely on the adapter's `interrupt()` backstop
|
|
18
|
+
* (called right after this result lands) to end the turn — see
|
|
19
|
+
* claudeAdapter.ts.
|
|
20
|
+
*
|
|
21
|
+
* Registered under one in-process MCP server named "stageflow", so the model
|
|
22
|
+
* sees these as `mcp__stageflow__emit_stage_envelope`,
|
|
23
|
+
* `mcp__stageflow__write_stage_artifact`, and `mcp__stageflow__ask_operator`
|
|
24
|
+
* — those exact strings are what `composeStageUserPrompt`'s hints and the
|
|
25
|
+
* adapter's resume prompts must reference.
|
|
26
|
+
*/
|
|
27
|
+
import { type McpServerConfig } from "@anthropic-ai/claude-agent-sdk";
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
import { type EmitCapture } from "../tools/emitStageEnvelope.js";
|
|
30
|
+
import { type WriteStageArtifactOptions } from "../tools/writeStageArtifact.js";
|
|
31
|
+
import { type AskOperatorKind, type AskOperatorPrompt } from "../tools/askOperator.js";
|
|
32
|
+
import type { CloneEmitContext, ForkEmitContext } from "../types/forkChoice.js";
|
|
33
|
+
import type { PreEmitCheckOptions } from "../envelope/preEmitChecks.js";
|
|
34
|
+
export declare const STAGEFLOW_MCP_SERVER_NAME = "stageflow";
|
|
35
|
+
export declare const EMIT_STAGE_ENVELOPE_TOOL_NAME = "mcp__stageflow__emit_stage_envelope";
|
|
36
|
+
export declare const WRITE_STAGE_ARTIFACT_TOOL_NAME = "mcp__stageflow__write_stage_artifact";
|
|
37
|
+
export declare const ASK_OPERATOR_TOOL_NAME = "mcp__stageflow__ask_operator";
|
|
38
|
+
export type AskOperatorCapture = {
|
|
39
|
+
prompt?: AskOperatorPrompt;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Exported so a parity test can assert this stays field-for-field aligned
|
|
43
|
+
* with `tools/emitStageEnvelope.ts`'s typebox schema — the two are hand
|
|
44
|
+
* written against the same envelope shape with no shared source, so
|
|
45
|
+
* nothing else catches one drifting from the other.
|
|
46
|
+
*
|
|
47
|
+
* Field presence mirrors the typebox schema's own conditionality:
|
|
48
|
+
* `clone_forks` only exists when a stage can clone-fork at all, and
|
|
49
|
+
* `fork_choice` is required (not just present) when the stage has a fork
|
|
50
|
+
* to route. Building this statically would silently offer the model a
|
|
51
|
+
* `clone_forks` field on stages that have nowhere to route one.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildEmitStageEnvelopeShape(options: {
|
|
54
|
+
forkEmitContext?: ForkEmitContext;
|
|
55
|
+
cloneEmitContext?: CloneEmitContext;
|
|
56
|
+
}): {
|
|
57
|
+
status: z.ZodEnum<{
|
|
58
|
+
failure: "failure";
|
|
59
|
+
success: "success";
|
|
60
|
+
}>;
|
|
61
|
+
summary: z.ZodString;
|
|
62
|
+
artifacts: z.ZodArray<z.ZodString>;
|
|
63
|
+
payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
64
|
+
fork_choice: z.ZodArray<z.ZodString> | z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
65
|
+
clone_forks?: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>> | undefined;
|
|
66
|
+
checklist_attestations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
67
|
+
check_id: z.ZodString;
|
|
68
|
+
items: z.ZodArray<z.ZodString>;
|
|
69
|
+
}, z.core.$strip>>>;
|
|
70
|
+
stage_id: z.ZodOptional<z.ZodString>;
|
|
71
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
72
|
+
};
|
|
73
|
+
/** Exported for the same parity-test reason as `emitStageEnvelopeShape` above. */
|
|
74
|
+
export declare const writeStageArtifactShape: {
|
|
75
|
+
path: z.ZodString;
|
|
76
|
+
content: z.ZodString;
|
|
77
|
+
};
|
|
78
|
+
export declare function buildStageflowMcpServer(options: {
|
|
79
|
+
capture: EmitCapture;
|
|
80
|
+
payloadSchema?: unknown;
|
|
81
|
+
forkEmitContext?: ForkEmitContext;
|
|
82
|
+
cloneEmitContext?: CloneEmitContext;
|
|
83
|
+
preEmitCheckOptions?: PreEmitCheckOptions;
|
|
84
|
+
writeStageArtifact: WriteStageArtifactOptions;
|
|
85
|
+
askOperator?: {
|
|
86
|
+
capture: AskOperatorCapture;
|
|
87
|
+
allowedKinds?: readonly AskOperatorKind[];
|
|
88
|
+
};
|
|
89
|
+
}): McpServerConfig;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK tool wrappers for the stage tool set.
|
|
3
|
+
*
|
|
4
|
+
* The actual envelope/artifact business logic lives in
|
|
5
|
+
* `tools/emitStageEnvelope.ts` / `tools/writeStageArtifact.ts` and is fully
|
|
6
|
+
* SDK-agnostic (typed `params: unknown`, no Pi imports) — this file reuses
|
|
7
|
+
* those `execute` functions as-is and only translates the *declaration*
|
|
8
|
+
* layer (typebox → Zod) and the *result* layer (drops Pi-only `terminate`/
|
|
9
|
+
* `details` fields, keeping just the MCP `CallToolResult` shape).
|
|
10
|
+
*
|
|
11
|
+
* `ask_operator` is different: `tools/askOperator.ts`'s own `execute` blocks
|
|
12
|
+
* on an injected wait bridge (Pi's in-process mechanism), which is exactly
|
|
13
|
+
* what Phase 3's "never let it go dangling" design avoids. This file reuses
|
|
14
|
+
* only the pure parsing/validation helpers from that module
|
|
15
|
+
* (`parseAskOperatorParams`, `normalizePromptIds`) and writes its own
|
|
16
|
+
* non-blocking handler: record the prompt on `capture`, return an immediate
|
|
17
|
+
* placeholder result, and rely on the adapter's `interrupt()` backstop
|
|
18
|
+
* (called right after this result lands) to end the turn — see
|
|
19
|
+
* claudeAdapter.ts.
|
|
20
|
+
*
|
|
21
|
+
* Registered under one in-process MCP server named "stageflow", so the model
|
|
22
|
+
* sees these as `mcp__stageflow__emit_stage_envelope`,
|
|
23
|
+
* `mcp__stageflow__write_stage_artifact`, and `mcp__stageflow__ask_operator`
|
|
24
|
+
* — those exact strings are what `composeStageUserPrompt`'s hints and the
|
|
25
|
+
* adapter's resume prompts must reference.
|
|
26
|
+
*/
|
|
27
|
+
import { createSdkMcpServer, tool, } from "@anthropic-ai/claude-agent-sdk";
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
import { createEmitStageEnvelopeTool, } from "../tools/emitStageEnvelope.js";
|
|
30
|
+
import { createWriteStageArtifactTool, } from "../tools/writeStageArtifact.js";
|
|
31
|
+
import { ASK_OPERATOR_KINDS, AskOperatorError, normalizePromptIds, parseAskOperatorParams, } from "../tools/askOperator.js";
|
|
32
|
+
export const STAGEFLOW_MCP_SERVER_NAME = "stageflow";
|
|
33
|
+
export const EMIT_STAGE_ENVELOPE_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__emit_stage_envelope`;
|
|
34
|
+
export const WRITE_STAGE_ARTIFACT_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__write_stage_artifact`;
|
|
35
|
+
export const ASK_OPERATOR_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__ask_operator`;
|
|
36
|
+
/**
|
|
37
|
+
* Exported so a parity test can assert this stays field-for-field aligned
|
|
38
|
+
* with `tools/emitStageEnvelope.ts`'s typebox schema — the two are hand
|
|
39
|
+
* written against the same envelope shape with no shared source, so
|
|
40
|
+
* nothing else catches one drifting from the other.
|
|
41
|
+
*
|
|
42
|
+
* Field presence mirrors the typebox schema's own conditionality:
|
|
43
|
+
* `clone_forks` only exists when a stage can clone-fork at all, and
|
|
44
|
+
* `fork_choice` is required (not just present) when the stage has a fork
|
|
45
|
+
* to route. Building this statically would silently offer the model a
|
|
46
|
+
* `clone_forks` field on stages that have nowhere to route one.
|
|
47
|
+
*/
|
|
48
|
+
export function buildEmitStageEnvelopeShape(options) {
|
|
49
|
+
return {
|
|
50
|
+
status: z.enum(["success", "failure"]),
|
|
51
|
+
summary: z.string().min(1),
|
|
52
|
+
artifacts: z.array(z.string()),
|
|
53
|
+
payload: z.record(z.string(), z.unknown()).optional(),
|
|
54
|
+
fork_choice: options.forkEmitContext !== undefined
|
|
55
|
+
? z.array(z.string())
|
|
56
|
+
: z.array(z.string()).optional(),
|
|
57
|
+
...(options.cloneEmitContext !== undefined
|
|
58
|
+
? { clone_forks: z.array(z.record(z.string(), z.unknown())) }
|
|
59
|
+
: {}),
|
|
60
|
+
checklist_attestations: z
|
|
61
|
+
.array(z.object({
|
|
62
|
+
check_id: z.string().min(1),
|
|
63
|
+
items: z.array(z.string()),
|
|
64
|
+
}))
|
|
65
|
+
.optional(),
|
|
66
|
+
stage_id: z.string().optional(),
|
|
67
|
+
notes: z.string().optional(),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Exported for the same parity-test reason as `emitStageEnvelopeShape` above. */
|
|
71
|
+
export const writeStageArtifactShape = {
|
|
72
|
+
path: z.string().min(1),
|
|
73
|
+
content: z.string(),
|
|
74
|
+
};
|
|
75
|
+
const askOperatorShape = {
|
|
76
|
+
kind: z.string(),
|
|
77
|
+
message: z.string().optional(),
|
|
78
|
+
id: z.string().optional(),
|
|
79
|
+
questions: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
80
|
+
artifacts: z.array(z.string()).optional(),
|
|
81
|
+
};
|
|
82
|
+
/** Adapts a `{content, details?, isError?, terminate?}` tool result to plain MCP CallToolResult. */
|
|
83
|
+
function toCallToolResult(result) {
|
|
84
|
+
return {
|
|
85
|
+
content: result.content,
|
|
86
|
+
...(result.isError ? { isError: true } : {}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function buildAskOperatorTool(capture, allowedKinds) {
|
|
90
|
+
const allowed = allowedKinds && allowedKinds.length > 0
|
|
91
|
+
? new Set(allowedKinds)
|
|
92
|
+
: null;
|
|
93
|
+
const kindList = allowed ? [...allowed].join(", ") : ASK_OPERATOR_KINDS.join(", ");
|
|
94
|
+
return tool("ask_operator", `Ask the operator a question and stop. Supports ${kindList} prompts. Calling this tool immediately ends your turn — after it returns, do not call any more tools and do not keep talking; you will be resumed in a new turn once the operator has answered. Does not complete the stage — call emit_stage_envelope when finished.`, askOperatorShape, async (args) => {
|
|
95
|
+
try {
|
|
96
|
+
const parsed = parseAskOperatorParams(args);
|
|
97
|
+
if (allowed && !allowed.has(parsed.kind)) {
|
|
98
|
+
throw new AskOperatorError(`unsupported prompt kind "${parsed.kind}" (allowed: ${kindList})`);
|
|
99
|
+
}
|
|
100
|
+
capture.prompt = normalizePromptIds(parsed);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text", text: `Invalid ask_operator params: ${message}` }],
|
|
106
|
+
isError: true,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: "Question recorded for the operator. Stop here — do not call any more tools or continue this turn; you will be resumed with the operator's answer in a new turn.",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
export function buildStageflowMcpServer(options) {
|
|
120
|
+
const emitDef = createEmitStageEnvelopeTool(options.capture, options.payloadSchema, options.forkEmitContext, options.cloneEmitContext, options.preEmitCheckOptions);
|
|
121
|
+
const artifactDef = createWriteStageArtifactTool(options.writeStageArtifact);
|
|
122
|
+
const emitTool = tool(emitDef.name, emitDef.description, buildEmitStageEnvelopeShape({
|
|
123
|
+
forkEmitContext: options.forkEmitContext,
|
|
124
|
+
cloneEmitContext: options.cloneEmitContext,
|
|
125
|
+
}), async (args) => toCallToolResult(await emitDef.execute("claude-tool-call", args)));
|
|
126
|
+
const artifactTool = tool(artifactDef.name, artifactDef.description, writeStageArtifactShape, async (args) => toCallToolResult(await artifactDef.execute("claude-tool-call", args)));
|
|
127
|
+
const tools = [emitTool, artifactTool];
|
|
128
|
+
if (options.askOperator) {
|
|
129
|
+
tools.push(buildAskOperatorTool(options.askOperator.capture, options.askOperator.allowedKinds));
|
|
130
|
+
}
|
|
131
|
+
return createSdkMcpServer({
|
|
132
|
+
name: STAGEFLOW_MCP_SERVER_NAME,
|
|
133
|
+
tools,
|
|
134
|
+
});
|
|
135
|
+
}
|
package/dist/agent/fakeAgent.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { assertRequiredEnvelope, isAdvancingEnvelope, } from "../envelope/check.js";
|
|
4
|
+
import { assertFeedbackLoopAction } from "../envelope/feedbackLoop.js";
|
|
4
5
|
import { assertEnvelopePayload } from "../envelope/payloadSchema.js";
|
|
5
6
|
import { assertCloneForks } from "../envelope/cloneForks.js";
|
|
6
7
|
import { assertForkEnvelope } from "../envelope/forkChoice.js";
|
|
7
8
|
import { assertAnswerMatchesPrompt, parseAskOperatorAnswer, } from "../tools/askOperator.js";
|
|
8
9
|
import { tryParsePendingPrompt } from "../runtime/stageHitl.js";
|
|
9
|
-
import { runtimeStageId, runStageViaOpen } from "./port.js";
|
|
10
|
+
import { runtimeStageId, runStageViaOpen, createCompletedOnlyStageHandle, } from "./port.js";
|
|
10
11
|
function opaqueEqual(a, b) {
|
|
11
12
|
if (Object.is(a, b))
|
|
12
13
|
return true;
|
|
@@ -68,6 +69,17 @@ export class FakeAgent {
|
|
|
68
69
|
let answerPromise;
|
|
69
70
|
let started = false;
|
|
70
71
|
let resumeCorrupt = false;
|
|
72
|
+
const sessionMode = input.sessionMode ?? "fresh";
|
|
73
|
+
const skipHitlPark = sessionMode === "feedback_resume" || sessionMode === "new_session";
|
|
74
|
+
if (sessionMode === "feedback_resume" && input.feedbackLoopContext === undefined) {
|
|
75
|
+
return createCompletedOnlyStageHandle({
|
|
76
|
+
stageId: runtimeStageId(input),
|
|
77
|
+
run: async () => ({
|
|
78
|
+
ok: false,
|
|
79
|
+
reason: "feedback_resume requires feedbackLoopContext",
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
71
83
|
const armWait = () => {
|
|
72
84
|
waiting = true;
|
|
73
85
|
answerPromise = new Promise((resolve) => {
|
|
@@ -83,7 +95,7 @@ export class FakeAgent {
|
|
|
83
95
|
answerPromise = undefined;
|
|
84
96
|
return answer;
|
|
85
97
|
};
|
|
86
|
-
if (behavior.type === "wait_then_emit") {
|
|
98
|
+
if (behavior.type === "wait_then_emit" && !skipHitlPark) {
|
|
87
99
|
const loaded = loadFakeHitlResume(input.roots, runtimeStageId(input));
|
|
88
100
|
if (loaded === "corrupt") {
|
|
89
101
|
resumeCorrupt = true;
|
|
@@ -99,6 +111,12 @@ export class FakeAgent {
|
|
|
99
111
|
armWait();
|
|
100
112
|
}
|
|
101
113
|
}
|
|
114
|
+
if (sessionMode === "feedback_resume" && behavior.type === "wait_then_emit") {
|
|
115
|
+
behavior = {
|
|
116
|
+
type: "emit",
|
|
117
|
+
envelope: behavior.envelope,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
102
120
|
const emitStartActivity = () => {
|
|
103
121
|
if (started)
|
|
104
122
|
return;
|
|
@@ -137,10 +155,12 @@ export class FakeAgent {
|
|
|
137
155
|
: undefined;
|
|
138
156
|
try {
|
|
139
157
|
const envelope = assertRequiredEnvelope(envelopeValue);
|
|
140
|
-
|
|
158
|
+
assertFeedbackLoopAction(envelope, input.feedbackLoopEmitContext);
|
|
159
|
+
const isFeedbackSendBack = envelope.feedback_loop?.action === "send_back";
|
|
160
|
+
if (!isFeedbackSendBack && input.forkEmitContext !== undefined) {
|
|
141
161
|
assertForkEnvelope(envelope, input.forkEmitContext);
|
|
142
162
|
}
|
|
143
|
-
if (input.cloneEmitContext !== undefined) {
|
|
163
|
+
if (!isFeedbackSendBack && input.cloneEmitContext !== undefined) {
|
|
144
164
|
assertCloneForks(envelope, input.cloneEmitContext);
|
|
145
165
|
}
|
|
146
166
|
assertEnvelopePayload(envelope, input.stage.payload_schema);
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { type AgentSession, DefaultResourceLoader, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import { type AgentSession, type EventBus, type InlineExtension, DefaultResourceLoader, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { StageRoots } from "../runtime/stageRoots.js";
|
|
3
3
|
import { type AskOperatorPrompt, type AskOperatorWaitBridge } from "../tools/askOperator.js";
|
|
4
4
|
import "./cursorProvider.js";
|
|
5
5
|
import { type StageActivityObserver } from "./activityObserver.js";
|
|
6
6
|
import type { AgentPort, OpaqueAnswer, StageHandle, StageHandleCloseOptions, StageRunInput, StageRunResult } from "./port.js";
|
|
7
|
+
import type { StageGateKind } from "../types/stage.js";
|
|
7
8
|
/**
|
|
8
9
|
* Stage tool allowlist for sealed Pi sessions.
|
|
9
|
-
*
|
|
10
|
-
* (always, for bound and unbound —
|
|
10
|
+
* Includes `ask_operator` unless `gateKinds` is an empty list (R6).
|
|
11
|
+
* `write_stage_artifact` when registered (always, for bound and unbound —
|
|
12
|
+
* bound/unbound only gates cwd / env bind).
|
|
11
13
|
*/
|
|
12
|
-
export declare function resolveStageToolNames(emitToolName: string, artifactToolName?: string, askOperatorToolName?: string): string[];
|
|
14
|
+
export declare function resolveStageToolNames(emitToolName: string, artifactToolName?: string, askOperatorToolName?: string, gateKinds?: StageGateKind[]): string[];
|
|
13
15
|
/**
|
|
14
16
|
* In-process wait channel for `ask_operator` → StageHandle yield/deliver.
|
|
15
17
|
* HITL `openStage` connects this via `setWaitHandler` + handle `deliverAnswer`.
|
|
@@ -62,6 +64,7 @@ export type RouteSessionEventToProgressOptions = {
|
|
|
62
64
|
*/
|
|
63
65
|
export declare function routeSessionEventToProgress(event: Record<string, unknown>, options: RouteSessionEventToProgressOptions): void;
|
|
64
66
|
export declare function composeStageUserPrompt(input: StageRunInput, emitToolName: string, emitHintOverride?: string, artifactToolName?: string): string;
|
|
67
|
+
export declare function composeFeedbackResumePrompt(input: StageRunInput): string;
|
|
65
68
|
/**
|
|
66
69
|
* DefaultResourceLoader with host/global discovery turned off.
|
|
67
70
|
*
|
|
@@ -69,9 +72,9 @@ export declare function composeStageUserPrompt(input: StageRunInput, emitToolNam
|
|
|
69
72
|
* up the consumer project's AGENTS.md, `.agents/skills/`, `.pi/extensions`,
|
|
70
73
|
* and APPEND_SYSTEM.md. Stages must not inherit that context.
|
|
71
74
|
*
|
|
72
|
-
* `additionalExtensionPaths` is the
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
+
* `additionalExtensionPaths` is the Cursor/provider seam. `extensionFactories`
|
|
76
|
+
* is the isolated MCP seam. With `noExtensions: true`, discovered
|
|
77
|
+
* global/project packages stay out; only those allowlists load.
|
|
75
78
|
* `additionalSkillPaths` is the matching allowlist for one named skill.
|
|
76
79
|
*/
|
|
77
80
|
export declare function createSealedResourceLoader(options: {
|
|
@@ -81,6 +84,8 @@ export declare function createSealedResourceLoader(options: {
|
|
|
81
84
|
systemPrompt: string;
|
|
82
85
|
additionalExtensionPaths?: string[];
|
|
83
86
|
additionalSkillPaths?: string[];
|
|
87
|
+
extensionFactories?: InlineExtension[];
|
|
88
|
+
eventBus?: EventBus;
|
|
84
89
|
}): DefaultResourceLoader;
|
|
85
90
|
/** Stable Pi session JSONL path under the run workspace stage dir. */
|
|
86
91
|
export declare function stageSessionFilePath(roots: Pick<StageRoots, "runWorkspaceDir" | "attempt">, stageId: string): string;
|