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.
Files changed (167) hide show
  1. package/README.md +6 -1
  2. package/dist/agent/activity.d.ts +9 -0
  3. package/dist/agent/agentBackend.d.ts +32 -0
  4. package/dist/agent/agentBackend.js +38 -0
  5. package/dist/agent/claudeActivity.d.ts +15 -0
  6. package/dist/agent/claudeActivity.js +58 -0
  7. package/dist/agent/claudeAdapter.d.ts +5 -0
  8. package/dist/agent/claudeAdapter.js +331 -0
  9. package/dist/agent/claudeSession.d.ts +18 -0
  10. package/dist/agent/claudeSession.js +54 -0
  11. package/dist/agent/claudeTools.d.ts +89 -0
  12. package/dist/agent/claudeTools.js +135 -0
  13. package/dist/agent/fakeAgent.js +24 -4
  14. package/dist/agent/piAdapter.d.ts +12 -7
  15. package/dist/agent/piAdapter.js +194 -44
  16. package/dist/agent/piIsolatedMcp.d.ts +23 -0
  17. package/dist/agent/piIsolatedMcp.js +223 -0
  18. package/dist/agent/piIsolatedMcpProbe.d.ts +15 -0
  19. package/dist/agent/piIsolatedMcpProbe.js +292 -0
  20. package/dist/agent/port.d.ts +52 -1
  21. package/dist/agent/port.js +1 -1
  22. package/dist/agent/resolveAgentPort.d.ts +26 -0
  23. package/dist/agent/resolveAgentPort.js +39 -0
  24. package/dist/cli/runCommand.js +2 -2
  25. package/dist/cli/runsCommand.d.ts +1 -1
  26. package/dist/cli/runsCommand.js +184 -2
  27. package/dist/cli.d.ts +1 -1
  28. package/dist/cli.js +11 -5
  29. package/dist/config/browseCatalog.js +1 -1
  30. package/dist/config/createPipeline.d.ts +2 -1
  31. package/dist/config/createPipeline.js +34 -9
  32. package/dist/config/createStage.js +14 -9
  33. package/dist/config/loadPipeline.js +11 -1
  34. package/dist/config/loadStage.d.ts +1 -0
  35. package/dist/config/loadStage.js +207 -2
  36. package/dist/config/loadStageflowManifest.js +14 -1
  37. package/dist/config/mergePipelineIncludes.d.ts +1 -0
  38. package/dist/config/mergePipelineIncludes.js +14 -1
  39. package/dist/config/normalizePipelineStageEntry.d.ts +7 -2
  40. package/dist/config/normalizePipelineStageEntry.js +56 -7
  41. package/dist/config/parseCompletionContract.d.ts +8 -0
  42. package/dist/config/parseCompletionContract.js +218 -0
  43. package/dist/config/parsePreEmitChecks.d.ts +3 -0
  44. package/dist/config/parsePreEmitChecks.js +65 -0
  45. package/dist/config/pipelineNeeds.d.ts +19 -0
  46. package/dist/config/pipelineNeeds.js +151 -0
  47. package/dist/config/pipelineStageKeys.js +17 -1
  48. package/dist/config/resolveForkEmitContext.d.ts +6 -2
  49. package/dist/config/resolveForkEmitContext.js +9 -2
  50. package/dist/config/resolvePipelineDag.d.ts +2 -1
  51. package/dist/config/resolvePipelineDag.js +182 -39
  52. package/dist/config/resolveStageMcpServers.d.ts +35 -0
  53. package/dist/config/resolveStageMcpServers.js +207 -0
  54. package/dist/config/validateCatalog.d.ts +1 -1
  55. package/dist/config/validateCatalog.js +51 -0
  56. package/dist/config/validateCompletionContract.d.ts +4 -0
  57. package/dist/config/validateCompletionContract.js +52 -0
  58. package/dist/envelope/check.d.ts +1 -1
  59. package/dist/envelope/check.js +86 -13
  60. package/dist/envelope/cloneForks.d.ts +1 -1
  61. package/dist/envelope/cloneForks.js +24 -9
  62. package/dist/envelope/feedbackLoop.d.ts +4 -0
  63. package/dist/envelope/feedbackLoop.js +31 -0
  64. package/dist/envelope/payloadSchema.d.ts +7 -1
  65. package/dist/envelope/payloadSchema.js +170 -13
  66. package/dist/envelope/preEmitChecks.d.ts +29 -0
  67. package/dist/envelope/preEmitChecks.js +57 -0
  68. package/dist/hitl/qaTrail.d.ts +25 -0
  69. package/dist/hitl/qaTrail.js +51 -0
  70. package/dist/mcp/catalogTools.js +4 -2
  71. package/dist/mcp/controlTools.js +72 -0
  72. package/dist/mcp/readArtifact.d.ts +2 -0
  73. package/dist/mcp/readArtifact.js +25 -1
  74. package/dist/mcp/waitingGates.d.ts +2 -1
  75. package/dist/mcp/waitingGates.js +17 -1
  76. package/dist/package-meta.d.ts +1 -1
  77. package/dist/package-meta.js +1 -1
  78. package/dist/projection/projectRun.d.ts +4 -1
  79. package/dist/projection/projectRun.js +7 -0
  80. package/dist/prompt/feedbackLoopContext.d.ts +2 -0
  81. package/dist/prompt/feedbackLoopContext.js +17 -0
  82. package/dist/prompt/priorEnvelope.d.ts +2 -2
  83. package/dist/prompt/priorEnvelope.js +4 -1
  84. package/dist/runstore/pipelineDagSnapshot.d.ts +1 -0
  85. package/dist/runstore/pipelineDagSnapshot.js +51 -11
  86. package/dist/runstore/port.d.ts +185 -3
  87. package/dist/runstore/port.js +42 -3
  88. package/dist/runstore/runProjection.d.ts +7 -3
  89. package/dist/runstore/runProjection.js +30 -15
  90. package/dist/runstore/sqlite/SqliteRunStore.d.ts +25 -1
  91. package/dist/runstore/sqlite/SqliteRunStore.js +626 -8
  92. package/dist/runstore/sqlite/schema.d.ts +1 -1
  93. package/dist/runstore/sqlite/schema.js +99 -0
  94. package/dist/runstore/stageInstanceId.d.ts +1 -1
  95. package/dist/runstore/stageInstanceId.js +3 -2
  96. package/dist/runstore/trackProjection.js +59 -27
  97. package/dist/runstore/verificationHistory.d.ts +19 -0
  98. package/dist/runstore/verificationHistory.js +30 -0
  99. package/dist/runtime/cloneSchedule.d.ts +40 -4
  100. package/dist/runtime/cloneSchedule.js +212 -28
  101. package/dist/runtime/completionCheckRunner.d.ts +137 -0
  102. package/dist/runtime/completionCheckRunner.js +365 -0
  103. package/dist/runtime/completionCheckoutBaseline.d.ts +3 -0
  104. package/dist/runtime/completionCheckoutBaseline.js +44 -0
  105. package/dist/runtime/envelopeRouting.d.ts +10 -3
  106. package/dist/runtime/envelopeRouting.js +146 -5
  107. package/dist/runtime/feedbackLoopCoordinator.d.ts +74 -0
  108. package/dist/runtime/feedbackLoopCoordinator.js +446 -0
  109. package/dist/runtime/feedbackLoopDecision.d.ts +44 -0
  110. package/dist/runtime/feedbackLoopDecision.js +207 -0
  111. package/dist/runtime/feedbackLoopRoute.d.ts +5 -0
  112. package/dist/runtime/feedbackLoopRoute.js +13 -0
  113. package/dist/runtime/feedbackLoopSchedule.d.ts +38 -0
  114. package/dist/runtime/feedbackLoopSchedule.js +72 -0
  115. package/dist/runtime/forkGeneration.d.ts +72 -0
  116. package/dist/runtime/forkGeneration.js +295 -0
  117. package/dist/runtime/gitCheckoutCapability.d.ts +7 -0
  118. package/dist/runtime/gitCheckoutCapability.js +97 -0
  119. package/dist/runtime/manualRecoveryState.d.ts +40 -0
  120. package/dist/runtime/manualRecoveryState.js +73 -0
  121. package/dist/runtime/pipelineScheduler.d.ts +13 -1
  122. package/dist/runtime/pipelineScheduler.js +328 -45
  123. package/dist/runtime/replayLifecycle.d.ts +107 -0
  124. package/dist/runtime/replayLifecycle.js +168 -0
  125. package/dist/runtime/runManager.d.ts +54 -1
  126. package/dist/runtime/runManager.js +471 -6
  127. package/dist/runtime/runRetryCoordinator.d.ts +6 -1
  128. package/dist/runtime/runRetryCoordinator.js +43 -9
  129. package/dist/runtime/stageAttemptBootstrap.d.ts +3 -1
  130. package/dist/runtime/stageAttemptBootstrap.js +117 -3
  131. package/dist/runtime/stageProcessLauncher.d.ts +1 -1
  132. package/dist/runtime/stageRecovery.js +1 -1
  133. package/dist/runtime/stageRunner.d.ts +4 -1
  134. package/dist/runtime/stageRunner.js +29 -3
  135. package/dist/runtime/stageWorker.js +69 -2
  136. package/dist/runtime/stageWorkerProtocol.d.ts +1 -1
  137. package/dist/runtime/verifiedStageExecution.d.ts +31 -0
  138. package/dist/runtime/verifiedStageExecution.js +127 -0
  139. package/dist/server/bootstrap.js +1 -0
  140. package/dist/server/http.js +137 -4
  141. package/dist/server/operatorResults.js +2 -0
  142. package/dist/server/projectMcpRoutes.d.ts +6 -0
  143. package/dist/server/projectMcpRoutes.js +38 -0
  144. package/dist/tools/askOperator.d.ts +161 -5
  145. package/dist/tools/askOperator.js +58 -25
  146. package/dist/tools/emitStageEnvelope.d.ts +12 -12
  147. package/dist/tools/emitStageEnvelope.js +112 -24
  148. package/dist/types/completion.d.ts +43 -0
  149. package/dist/types/completion.js +1 -0
  150. package/dist/types/envelope.d.ts +18 -0
  151. package/dist/types/forkChoice.d.ts +9 -4
  152. package/dist/types/forkChoice.js +1 -1
  153. package/dist/types/pipeline.d.ts +41 -2
  154. package/dist/types/preEmitCheck.d.ts +18 -0
  155. package/dist/types/preEmitCheck.js +1 -0
  156. package/dist/types/stage.d.ts +14 -0
  157. package/dist/types/stageflowManifest.d.ts +2 -0
  158. package/dist/ui/assets/index-Yzh42vhf.js +118 -0
  159. package/dist/ui/assets/{index-C3N7MXAC.css → index-uVHiae8s.css} +1 -1
  160. package/dist/ui/index.html +2 -2
  161. package/package.json +5 -1
  162. package/skills/stageflow-author/SKILL.md +20 -2
  163. package/skills/stageflow-author/references/catalog-mapping.md +88 -3
  164. package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
  165. package/skills/stageflow-author/references/stage-prompt-template.md +63 -4
  166. package/skills/stageflow-session-capture/references/catalog-authoring.md +4 -1
  167. 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
+ }
@@ -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
- if (input.forkEmitContext !== undefined) {
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
- * Always includes `ask_operator` (R7); `write_stage_artifact` when registered
10
- * (always, for bound and unbound — bound/unbound only gates cwd / env bind).
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 only way extensions enter a sealed stage
73
- * (used by StageProviderSupport implementations). With `noExtensions: true`,
74
- * discovered global/project packages stay out; only allowlisted paths load.
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;