stageflow 0.42.0 → 0.46.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 (315) hide show
  1. package/README.md +12 -3
  2. package/dist/agent/activity.d.ts +18 -0
  3. package/dist/agent/claudeAdapter.js +7 -0
  4. package/dist/agent/claudeTools.d.ts +27 -18
  5. package/dist/agent/claudeTools.js +54 -15
  6. package/dist/agent/compactionEvents.d.ts +34 -0
  7. package/dist/agent/compactionEvents.js +37 -0
  8. package/dist/agent/openRouterRouting.d.ts +7 -0
  9. package/dist/agent/openRouterRouting.js +23 -0
  10. package/dist/agent/piAdapter.d.ts +6 -0
  11. package/dist/agent/piAdapter.js +128 -27
  12. package/dist/agent/piSettings.d.ts +7 -0
  13. package/dist/agent/piSettings.js +9 -0
  14. package/dist/agent/sandboxPiTools.d.ts +3 -1
  15. package/dist/agent/sandboxPiTools.js +8 -2
  16. package/dist/agent/stageTurnDriver.d.ts +11 -2
  17. package/dist/agent/stageTurnDriver.js +45 -4
  18. package/dist/agent/toolOutputBudget.d.ts +15 -1
  19. package/dist/agent/toolOutputBudget.js +18 -0
  20. package/dist/agent/toolOutputBudgetExtension.d.ts +2 -0
  21. package/dist/agent/toolOutputBudgetExtension.js +40 -3
  22. package/dist/agents/authorChat.d.ts +1 -0
  23. package/dist/agents/authorChat.js +7 -0
  24. package/dist/agents/authorSessionStore.d.ts +3 -0
  25. package/dist/agents/authorSessionStore.js +17 -2
  26. package/dist/agents/evalGate.js +1 -0
  27. package/dist/agents/hostContext.d.ts +1 -1
  28. package/dist/agents/runtime/piRuntime.d.ts +5 -1
  29. package/dist/agents/runtime/piRuntime.js +46 -12
  30. package/dist/agents/service.js +3 -2
  31. package/dist/agents/sessions.js +3 -1
  32. package/dist/cli/evalCommand.d.ts +1 -1
  33. package/dist/cli/evalCommand.js +39 -2
  34. package/dist/cli/hostClient.d.ts +6 -0
  35. package/dist/cli/hostClient.js +4 -0
  36. package/dist/compaction/directives.d.ts +18 -0
  37. package/dist/compaction/directives.js +119 -0
  38. package/dist/compaction/extension.d.ts +20 -0
  39. package/dist/compaction/extension.js +78 -0
  40. package/dist/compaction/mask.d.ts +18 -0
  41. package/dist/compaction/mask.js +155 -0
  42. package/dist/compaction/pin.d.ts +21 -0
  43. package/dist/compaction/pin.js +270 -0
  44. package/dist/compaction/scrub.d.ts +7 -0
  45. package/dist/compaction/scrub.js +26 -0
  46. package/dist/compaction/sessionWiring.d.ts +22 -0
  47. package/dist/compaction/sessionWiring.js +26 -0
  48. package/dist/compaction/summarize.d.ts +79 -0
  49. package/dist/compaction/summarize.js +171 -0
  50. package/dist/compaction/toolPolicy.d.ts +3 -0
  51. package/dist/compaction/toolPolicy.js +33 -0
  52. package/dist/compaction/types.d.ts +45 -0
  53. package/dist/compaction/types.js +7 -0
  54. package/dist/compaction/workingState.d.ts +56 -0
  55. package/dist/compaction/workingState.js +198 -0
  56. package/dist/config/createPipeline.js +2 -0
  57. package/dist/config/createTrigger.js +7 -1
  58. package/dist/config/ensureCatalogScanRoot.d.ts +2 -0
  59. package/dist/config/ensureCatalogScanRoot.js +6 -0
  60. package/dist/config/evalCatalog.js +30 -2
  61. package/dist/config/githubTriggerAction.d.ts +8 -0
  62. package/dist/config/githubTriggerAction.js +29 -0
  63. package/dist/config/hostConfig.js +2 -0
  64. package/dist/config/loadEval.d.ts +3 -1
  65. package/dist/config/loadEval.js +96 -10
  66. package/dist/config/pipelineNeeds.d.ts +2 -0
  67. package/dist/config/pipelineNeeds.js +16 -3
  68. package/dist/config/pipelineRoute.js +5 -4
  69. package/dist/config/validateCatalog.d.ts +1 -1
  70. package/dist/config/validateCatalog.js +12 -0
  71. package/dist/config/validateEval.js +60 -3
  72. package/dist/config/writeEval.js +2 -2
  73. package/dist/envelope/assertEmittedArtifacts.d.ts +12 -0
  74. package/dist/envelope/assertEmittedArtifacts.js +43 -0
  75. package/dist/envelope/preEmitChecks.d.ts +3 -0
  76. package/dist/evals/calibration.d.ts +21 -0
  77. package/dist/evals/calibration.js +69 -0
  78. package/dist/evals/calibrationSuggest.d.ts +29 -0
  79. package/dist/evals/calibrationSuggest.js +72 -0
  80. package/dist/evals/calibrator.d.ts +46 -0
  81. package/dist/evals/calibrator.js +148 -0
  82. package/dist/evals/compare.d.ts +1 -1
  83. package/dist/evals/compare.js +3 -0
  84. package/dist/evals/constants.d.ts +7 -3
  85. package/dist/evals/constants.js +7 -1
  86. package/dist/evals/engine/FakeEngine.d.ts +2 -1
  87. package/dist/evals/engine/FakeEngine.js +9 -3
  88. package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +1 -1
  89. package/dist/evals/engine/promptfoo/PromptfooEngine.js +4 -2
  90. package/dist/evals/engine/promptfoo/generate.d.ts +3 -1
  91. package/dist/evals/engine/promptfoo/generate.js +43 -23
  92. package/dist/evals/engine/promptfoo/judgeProviders.js +2 -1
  93. package/dist/evals/engine/promptfoo/normalize.d.ts +4 -1
  94. package/dist/evals/engine/promptfoo/normalize.js +16 -3
  95. package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +3 -1
  96. package/dist/evals/engine/promptfoo/rubricPrompt.js +15 -5
  97. package/dist/evals/errors.d.ts +7 -0
  98. package/dist/evals/errors.js +10 -0
  99. package/dist/evals/judgeProviders.d.ts +6 -0
  100. package/dist/evals/judgeProviders.js +14 -0
  101. package/dist/evals/judged.d.ts +7 -0
  102. package/dist/evals/judged.js +7 -0
  103. package/dist/evals/manager.d.ts +27 -8
  104. package/dist/evals/manager.js +73 -12
  105. package/dist/evals/plan.d.ts +10 -1
  106. package/dist/evals/plan.js +33 -13
  107. package/dist/evals/planInternalAgent.d.ts +19 -0
  108. package/dist/evals/planInternalAgent.js +201 -0
  109. package/dist/evals/read/api.d.ts +18 -0
  110. package/dist/evals/read/api.js +10 -1
  111. package/dist/evals/read/labelQueue.d.ts +45 -0
  112. package/dist/evals/read/labelQueue.js +83 -0
  113. package/dist/evals/read/list.js +10 -1
  114. package/dist/evals/read/runs.d.ts +5 -0
  115. package/dist/evals/read/runs.js +7 -1
  116. package/dist/evals/runner/caseRunner.d.ts +2 -1
  117. package/dist/evals/runner/caseRunner.js +16 -5
  118. package/dist/evals/runner/internalAgentCaseRunner.d.ts +6 -0
  119. package/dist/evals/runner/internalAgentCaseRunner.js +250 -0
  120. package/dist/evals/snapshot.d.ts +10 -2
  121. package/dist/evals/snapshot.js +8 -3
  122. package/dist/evals/stats.d.ts +40 -0
  123. package/dist/evals/stats.js +69 -0
  124. package/dist/evals/storeTypes.d.ts +42 -0
  125. package/dist/evals/types.d.ts +25 -2
  126. package/dist/evals/types.js +4 -1
  127. package/dist/evals/votes.d.ts +10 -0
  128. package/dist/evals/votes.js +60 -0
  129. package/dist/guide/cardContract.d.ts +133 -0
  130. package/dist/guide/cardContract.js +156 -0
  131. package/dist/guide/chat.d.ts +45 -0
  132. package/dist/guide/chat.js +378 -0
  133. package/dist/guide/knowledge/index.d.ts +14 -0
  134. package/dist/guide/knowledge/index.js +109 -0
  135. package/dist/guide/knowledge/topics/agents.d.ts +2 -0
  136. package/dist/guide/knowledge/topics/agents.js +58 -0
  137. package/dist/guide/knowledge/topics/bestPractices.d.ts +2 -0
  138. package/dist/guide/knowledge/topics/bestPractices.js +67 -0
  139. package/dist/guide/knowledge/topics/connections.d.ts +2 -0
  140. package/dist/guide/knowledge/topics/connections.js +66 -0
  141. package/dist/guide/knowledge/topics/consoleMap.d.ts +2 -0
  142. package/dist/guide/knowledge/topics/consoleMap.js +55 -0
  143. package/dist/guide/knowledge/topics/evals.d.ts +2 -0
  144. package/dist/guide/knowledge/topics/evals.js +72 -0
  145. package/dist/guide/knowledge/topics/limitations.d.ts +2 -0
  146. package/dist/guide/knowledge/topics/limitations.js +84 -0
  147. package/dist/guide/knowledge/topics/mcpSkillsBrowser.d.ts +2 -0
  148. package/dist/guide/knowledge/topics/mcpSkillsBrowser.js +57 -0
  149. package/dist/guide/knowledge/topics/memories.d.ts +2 -0
  150. package/dist/guide/knowledge/topics/memories.js +63 -0
  151. package/dist/guide/knowledge/topics/modelsAndProviders.d.ts +2 -0
  152. package/dist/guide/knowledge/topics/modelsAndProviders.js +56 -0
  153. package/dist/guide/knowledge/topics/overview.d.ts +2 -0
  154. package/dist/guide/knowledge/topics/overview.js +56 -0
  155. package/dist/guide/knowledge/topics/pipelines.d.ts +2 -0
  156. package/dist/guide/knowledge/topics/pipelines.js +93 -0
  157. package/dist/guide/knowledge/topics/stages.d.ts +2 -0
  158. package/dist/guide/knowledge/topics/stages.js +54 -0
  159. package/dist/guide/knowledge/topics/tasks.d.ts +2 -0
  160. package/dist/guide/knowledge/topics/tasks.js +52 -0
  161. package/dist/guide/knowledge/topics/triggers.d.ts +2 -0
  162. package/dist/guide/knowledge/topics/triggers.js +68 -0
  163. package/dist/guide/knowledge/topics/troubleshooting.d.ts +2 -0
  164. package/dist/guide/knowledge/topics/troubleshooting.js +85 -0
  165. package/dist/guide/knowledgeTools.d.ts +2 -0
  166. package/dist/guide/knowledgeTools.js +56 -0
  167. package/dist/guide/showUi.d.ts +6 -0
  168. package/dist/guide/showUi.js +546 -0
  169. package/dist/guide/threadStore.d.ts +48 -0
  170. package/dist/guide/threadStore.js +189 -0
  171. package/dist/guide/writeRoot.d.ts +11 -0
  172. package/dist/guide/writeRoot.js +70 -0
  173. package/dist/hosted/translate.js +2 -0
  174. package/dist/mcp/agentTools.js +12 -0
  175. package/dist/mcp/catalogTools.js +18 -20
  176. package/dist/mcp/catalogWriteTools.d.ts +8 -0
  177. package/dist/mcp/catalogWriteTools.js +225 -0
  178. package/dist/mcp/connectionTools.d.ts +3 -0
  179. package/dist/mcp/connectionTools.js +85 -0
  180. package/dist/mcp/controlTools.js +34 -1
  181. package/dist/mcp/deps.d.ts +9 -0
  182. package/dist/mcp/evalTools.js +723 -21
  183. package/dist/mcp/memoryHostTools.d.ts +3 -0
  184. package/dist/mcp/memoryHostTools.js +72 -0
  185. package/dist/mcp/pipelineAuthorTools.js +14 -1
  186. package/dist/mcp/server.js +5 -0
  187. package/dist/mcp/tools.js +6 -0
  188. package/dist/mcp/triggerTools.js +77 -31
  189. package/dist/operatorAgent/evalGate.d.ts +22 -0
  190. package/dist/operatorAgent/evalGate.js +77 -0
  191. package/dist/operatorAgent/fakeHost.js +10 -2
  192. package/dist/operatorAgent/host.js +18 -2
  193. package/dist/operatorAgent/index.d.ts +1 -0
  194. package/dist/operatorAgent/index.js +1 -0
  195. package/dist/operatorAgent/internalAgents.d.ts +11 -0
  196. package/dist/operatorAgent/internalAgents.js +13 -0
  197. package/dist/operatorAgent/piModel.d.ts +5 -1
  198. package/dist/operatorAgent/piModel.js +15 -10
  199. package/dist/operatorAgent/profiles/agentAuthor.js +66 -22
  200. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
  201. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +1 -1
  202. package/dist/operatorAgent/profiles/guideConfirm.d.ts +21 -0
  203. package/dist/operatorAgent/profiles/guideConfirm.js +100 -0
  204. package/dist/operatorAgent/profiles/guideTools.d.ts +7 -0
  205. package/dist/operatorAgent/profiles/guideTools.js +123 -0
  206. package/dist/operatorAgent/profiles/stageflowGuide.d.ts +7 -0
  207. package/dist/operatorAgent/profiles/stageflowGuide.js +133 -0
  208. package/dist/operatorAgent/profiles/workshopAuthor.js +23 -0
  209. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  210. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +9 -1
  211. package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +2 -0
  212. package/dist/operatorAgent/profiles/workshopAuthorTools.js +197 -9
  213. package/dist/operatorAgent/types.d.ts +23 -0
  214. package/dist/package-meta.d.ts +1 -1
  215. package/dist/package-meta.js +1 -1
  216. package/dist/runstore/port.d.ts +13 -1
  217. package/dist/runstore/retention.d.ts +2 -1
  218. package/dist/runstore/retention.js +3 -7
  219. package/dist/runstore/runStatusClock.d.ts +17 -0
  220. package/dist/runstore/runStatusClock.js +19 -0
  221. package/dist/runstore/sqlite/SqliteRunStore.d.ts +11 -1
  222. package/dist/runstore/sqlite/SqliteRunStore.js +70 -45
  223. package/dist/runstore/sqlite/calibrationStore.d.ts +10 -0
  224. package/dist/runstore/sqlite/calibrationStore.js +106 -0
  225. package/dist/runstore/sqlite/evalStore.d.ts +5 -0
  226. package/dist/runstore/sqlite/evalStore.js +7 -0
  227. package/dist/runstore/sqlite/migrations/018-eval-grader-labels.d.ts +11 -0
  228. package/dist/runstore/sqlite/migrations/018-eval-grader-labels.js +43 -0
  229. package/dist/runstore/sqlite/migrations/index.js +2 -0
  230. package/dist/runstore/workspaceLayout.js +6 -1
  231. package/dist/runtime/artifactDiskInspect.d.ts +14 -0
  232. package/dist/runtime/artifactDiskInspect.js +52 -0
  233. package/dist/runtime/artifactPath.d.ts +24 -0
  234. package/dist/runtime/artifactPath.js +84 -0
  235. package/dist/runtime/completionCheckRunner.js +15 -43
  236. package/dist/runtime/githubPollSource.d.ts +22 -4
  237. package/dist/runtime/githubPollSource.js +93 -8
  238. package/dist/runtime/priorArtifactGrants.d.ts +8 -0
  239. package/dist/runtime/priorArtifactGrants.js +105 -0
  240. package/dist/runtime/runActionEligibility.d.ts +46 -0
  241. package/dist/runtime/runActionEligibility.js +123 -0
  242. package/dist/runtime/runManager.d.ts +4 -1
  243. package/dist/runtime/runManager.js +52 -10
  244. package/dist/runtime/runRetryCoordinator.d.ts +2 -14
  245. package/dist/runtime/runRetryCoordinator.js +3 -67
  246. package/dist/runtime/sandboxContainer.d.ts +7 -0
  247. package/dist/runtime/sandboxContainer.js +48 -6
  248. package/dist/runtime/sandboxDesk.d.ts +2 -1
  249. package/dist/runtime/sandboxDesk.js +11 -12
  250. package/dist/runtime/stageEnvironment.js +1 -0
  251. package/dist/runtime/stageRoots.d.ts +12 -0
  252. package/dist/runtime/stageSandbox.d.ts +2 -1
  253. package/dist/runtime/stageSandbox.js +18 -2
  254. package/dist/runtime/stageWorker.js +20 -0
  255. package/dist/server/agentAuthorRoutes.js +12 -3
  256. package/dist/server/bootstrap.d.ts +5 -0
  257. package/dist/server/bootstrap.js +14 -1
  258. package/dist/server/evalRoutes.js +251 -12
  259. package/dist/server/guideRoutes.d.ts +5 -0
  260. package/dist/server/guideRoutes.js +166 -0
  261. package/dist/server/http.d.ts +10 -0
  262. package/dist/server/http.js +35 -1
  263. package/dist/server/internalAgentRoutes.d.ts +6 -0
  264. package/dist/server/internalAgentRoutes.js +12 -0
  265. package/dist/server/mcpHost.js +2 -0
  266. package/dist/toolkit/meta.d.ts +1 -1
  267. package/dist/toolkit/meta.js +41 -1
  268. package/dist/toolkit/profiles.d.ts +5 -0
  269. package/dist/toolkit/profiles.js +12 -0
  270. package/dist/tools/askOperator.js +2 -1
  271. package/dist/tools/emitStageEnvelope.d.ts +17 -16
  272. package/dist/tools/emitStageEnvelope.js +46 -32
  273. package/dist/tools/stageToolNames.d.ts +4 -0
  274. package/dist/tools/stageToolNames.js +4 -0
  275. package/dist/tools/workingState.d.ts +40 -0
  276. package/dist/tools/workingState.js +108 -0
  277. package/dist/tools/writeStageArtifact.d.ts +0 -9
  278. package/dist/tools/writeStageArtifact.js +2 -1
  279. package/dist/ui/assets/{MarkdownText-BTrOqu5J.js → MarkdownText-BS2BBbCx.js} +2 -2
  280. package/dist/ui/assets/adapters-DPD-cAsy.js +17 -0
  281. package/dist/ui/assets/chat-BD1-VTT6.js +2 -0
  282. package/dist/ui/assets/chat-DXnQv9st.css +2 -0
  283. package/dist/ui/assets/main-AX5XNLtX.js +278 -0
  284. package/dist/ui/assets/{main-CHlmTNJe.css → main-CFX0_n8J.css} +1 -1
  285. package/dist/ui/chat.html +3 -4
  286. package/dist/ui/index.html +3 -4
  287. package/dist/workshop/chatTurn.js +1 -1
  288. package/dist/workshop/workshopPin.d.ts +3 -3
  289. package/dist/workshop/workshopPin.js +16 -15
  290. package/examples/compaction-stress/README.md +30 -0
  291. package/examples/compaction-stress/check-run.mjs +53 -0
  292. package/examples/compaction-stress/collect-evidence.yaml +60 -0
  293. package/examples/compaction-stress/compaction-stress.pipeline.yaml +141 -0
  294. package/examples/compaction-stress/compaction-stress.task.yaml +15 -0
  295. package/examples/compaction-stress/generate-evidence.mjs +110 -0
  296. package/examples/compaction-stress/root-cause.yaml +26 -0
  297. package/examples/compaction-stress/stageflow.yaml +9 -0
  298. package/examples/compaction-stress/write-postmortem.yaml +40 -0
  299. package/examples/evals/README.md +18 -0
  300. package/examples/evals/guide-behavior.dataset.jsonl +15 -0
  301. package/examples/evals/guide-behavior.eval.yaml +35 -0
  302. package/examples/evals/guide-depth.dataset.jsonl +35 -0
  303. package/examples/evals/guide-depth.eval.yaml +169 -0
  304. package/examples/memory/evals/guide-behavior.dataset.jsonl +15 -0
  305. package/examples/memory/evals/guide-behavior.eval.yaml +35 -0
  306. package/examples/memory/evals/guide-depth.dataset.jsonl +35 -0
  307. package/examples/memory/evals/guide-depth.eval.yaml +169 -0
  308. package/package.json +1 -1
  309. package/skills/stageflow/references/control-surface.md +1 -1
  310. package/skills/stageflow/references/evals.md +1 -0
  311. package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +0 -9
  312. package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +0 -6
  313. package/dist/ui/assets/chat-Df_wCpak.js +0 -5
  314. package/dist/ui/assets/chat-P7KQznHm.css +0 -2
  315. package/dist/ui/assets/main-CvVfxmMd.js +0 -271
@@ -285,6 +285,7 @@ export declare class RunManager {
285
285
  private notifyGateClosed;
286
286
  abandonStage(runId: string, stageId: string): Promise<AbandonStageResult>;
287
287
  cancelRun(runId: string, reason: string): Promise<CancelRunResult>;
288
+ private interruptLiveStages;
288
289
  setA2aStore(store: A2aStore): void;
289
290
  deleteRun(runId: string, options?: {
290
291
  force?: boolean;
@@ -321,7 +322,9 @@ export declare class RunManager {
321
322
  pinned?: boolean;
322
323
  callerId?: string | null;
323
324
  }): Promise<StartRunResult>;
324
- retryStage(runId: string, stageId: string): Promise<RetryStageResult>;
325
+ retryStage(runId: string, stageId: string, options?: {
326
+ awaitRoot?: boolean;
327
+ }): Promise<RetryStageResult>;
325
328
  resumeTimedOutStage(runId: string, stageId: string, options?: {
326
329
  source?: "explicit" | "auto";
327
330
  }): Promise<RetryStageResult>;
@@ -988,7 +988,16 @@ export class RunManager {
988
988
  catch {
989
989
  return { ok: false, reason: `Run not found: ${runId}`, status: 404 };
990
990
  }
991
+ const trimmedReason = reason.trim();
992
+ if (trimmedReason.length === 0) {
993
+ return {
994
+ ok: false,
995
+ reason: "Cancel reason is required",
996
+ status: 400,
997
+ };
998
+ }
991
999
  if (meta.status === "cancelled") {
1000
+ await this.interruptLiveStages(runId);
992
1001
  return { ok: true, runId };
993
1002
  }
994
1003
  if (meta.status === "succeeded" || meta.status === "failed") {
@@ -998,14 +1007,6 @@ export class RunManager {
998
1007
  status: 409,
999
1008
  };
1000
1009
  }
1001
- const trimmedReason = reason.trim();
1002
- if (trimmedReason.length === 0) {
1003
- return {
1004
- ok: false,
1005
- reason: "Cancel reason is required",
1006
- status: 400,
1007
- };
1008
- }
1009
1010
  await this.options.store.updateRunStatus(runId, "cancelled");
1010
1011
  this.notifyGateClosed(runId);
1011
1012
  await this.options.store.setCancelReason(runId, trimmedReason);
@@ -1056,6 +1057,39 @@ export class RunManager {
1056
1057
  }
1057
1058
  return { ok: true, runId };
1058
1059
  }
1060
+ async interruptLiveStages(runId) {
1061
+ const halt = this.schedulingHalts.get(runId);
1062
+ if (halt !== undefined) {
1063
+ halt.halted = true;
1064
+ }
1065
+ if (this.stageProcessLauncher !== undefined) {
1066
+ await this.stageProcessLauncher.cancelRun(runId);
1067
+ }
1068
+ const detail = await this.options.store.readRun(runId);
1069
+ for (const stage of detail.stages) {
1070
+ if (stage.status !== "running" && stage.status !== "waiting_for_input") {
1071
+ continue;
1072
+ }
1073
+ const wasWaiting = stage.status === "waiting_for_input";
1074
+ await markStageInterrupted({
1075
+ store: this.options.store,
1076
+ runId,
1077
+ stageId: stage.stage_id,
1078
+ reason: OPERATOR_CANCEL_REASON,
1079
+ status: "failed",
1080
+ });
1081
+ if (this.retryCoordinator.isActive(runId) &&
1082
+ this.retryCoordinator.getActiveRoots(runId)?.has(stage.stage_id)) {
1083
+ this.retryCoordinator.signalExternalRetryRootTerminal(runId, stage.stage_id);
1084
+ }
1085
+ if (wasWaiting) {
1086
+ this.hitl.clearLiveWait(runId, stage.stage_id);
1087
+ }
1088
+ }
1089
+ if (this.active.has(runId)) {
1090
+ this.removeActiveEntry(runId, false);
1091
+ }
1092
+ }
1059
1093
  setA2aStore(store) {
1060
1094
  this.a2aStore = store;
1061
1095
  }
@@ -1357,8 +1391,8 @@ export class RunManager {
1357
1391
  }
1358
1392
  return this.reserveAndStartPipeline(taskYaml, pipeline, `run ${runId} task`, rerunCwd, undefined, skipGates, ciIdentity, rerunProjectRoot, undefined, undefined, pinned, options?.callerId);
1359
1393
  }
1360
- async retryStage(runId, stageId) {
1361
- return this.retryStageInternal(runId, stageId, true);
1394
+ async retryStage(runId, stageId, options) {
1395
+ return this.retryStageInternal(runId, stageId, options?.awaitRoot !== false);
1362
1396
  }
1363
1397
  async resumeTimedOutStage(runId, stageId, options) {
1364
1398
  if (!this.acceptingWork) {
@@ -1622,6 +1656,14 @@ export class RunManager {
1622
1656
  if (!result.ok || awaitRoot !== false) {
1623
1657
  this.retryInFlight.delete(retryKey);
1624
1658
  }
1659
+ else if (result.done !== undefined) {
1660
+ void result.done.finally(() => {
1661
+ this.retryInFlight.delete(retryKey);
1662
+ });
1663
+ }
1664
+ else {
1665
+ this.retryInFlight.delete(retryKey);
1666
+ }
1625
1667
  return result;
1626
1668
  }
1627
1669
  catch (err) {
@@ -1,5 +1,5 @@
1
1
  import type { StagePort } from "../agent/port.js";
2
- import { type RunDetail, type RunPipelineDagSnapshot, type RunStatus, type RunStore, type StageSnapshot } from "../runstore/port.js";
2
+ import type { RunStatus, RunStore } from "../runstore/port.js";
3
3
  import type { PipelineRunResult } from "./pipelineRunner.js";
4
4
  import { type SchedulerPreparedPipeline } from "./pipelineScheduler.js";
5
5
  import type { StageExecutionMode } from "./stageConcurrency.js";
@@ -7,6 +7,7 @@ import type { StageBrowserSupport } from "../browser/browserHost.js";
7
7
  import type { StageProcessLauncher } from "./stageProcessLauncher.js";
8
8
  import type { StageHitlController } from "./stageHitl.js";
9
9
  import type { OperatorCatalog } from "./stageAttemptBootstrap.js";
10
+ export { assertStageRetryEligible, resumesRunOnStageRetry, succeededStageRetryBlocker, } from "./runActionEligibility.js";
10
11
  export declare class DuplicateRetryRootError extends Error {
11
12
  constructor(runId: string, stageId: string);
12
13
  }
@@ -69,19 +70,6 @@ export type RetryStageRequest = {
69
70
  awaitRoot?: boolean;
70
71
  beforeAttemptStart?: (attempt: number) => Promise<void>;
71
72
  };
72
- export declare function succeededStageRetryBlocker(stageSnap: StageSnapshot): string | undefined;
73
- export declare function assertStageRetryEligible(detail: RunDetail, stageId: string, opts?: {
74
- recoveryActive?: boolean;
75
- persistedStatus?: RunStatus;
76
- dag?: Pick<RunPipelineDagSnapshot, "nodes"> | null;
77
- }): {
78
- ok: true;
79
- } | {
80
- ok: false;
81
- reason: string;
82
- status: number;
83
- code: string;
84
- };
85
73
  export declare function readRetryRootWaitTimeoutMs(): number;
86
74
  export declare class RunRetryCoordinator {
87
75
  private readonly sessions;
@@ -1,7 +1,8 @@
1
- import { findUnhandledFailedStage, } from "../runstore/port.js";
2
1
  import { retryRun, } from "./pipelineScheduler.js";
3
2
  import { loadRunContext } from "./resumeReconstruct.js";
4
3
  import { syncRunStatusFromStages } from "./stageRecovery.js";
4
+ import { assertStageRetryEligible, resumesRunOnStageRetry, } from "./runActionEligibility.js";
5
+ export { assertStageRetryEligible, resumesRunOnStageRetry, succeededStageRetryBlocker, } from "./runActionEligibility.js";
5
6
  export class DuplicateRetryRootError extends Error {
6
7
  constructor(runId, stageId) {
7
8
  super(`Retry already in progress for run ${runId} stage ${stageId}`);
@@ -20,71 +21,6 @@ export class RetryRootWaitTimeoutError extends Error {
20
21
  this.name = "RetryRootWaitTimeoutError";
21
22
  }
22
23
  }
23
- export function succeededStageRetryBlocker(stageSnap) {
24
- if (stageSnap.definition_id !== undefined &&
25
- stageSnap.definition_id !== stageSnap.stage_id) {
26
- return `Cannot re-run a succeeded clone instance stage (stage=${stageSnap.stage_id}, definition=${stageSnap.definition_id}): clone instances are created by upstream fan-out and cannot be rebuilt in place. Use rerun to start a new run.`;
27
- }
28
- if (stageSnap.envelope?.fork_choice !== undefined) {
29
- return `Cannot re-run a succeeded fork stage (stage=${stageSnap.stage_id}): its envelope recorded a fork_choice and skipped branches cannot be restored. Use rerun to start a new run.`;
30
- }
31
- return undefined;
32
- }
33
- export function assertStageRetryEligible(detail, stageId, opts) {
34
- const stageSnap = detail.stages.find((s) => s.stage_id === stageId);
35
- if (!stageSnap) {
36
- return {
37
- ok: false,
38
- reason: `Stage not found: ${stageId}`,
39
- status: 404,
40
- code: "stage_not_found",
41
- };
42
- }
43
- if (stageSnap.status === "waiting_for_input") {
44
- return {
45
- ok: false,
46
- reason: `Stage is waiting for input and cannot be retried`,
47
- status: 409,
48
- code: "hitl_not_retriable",
49
- };
50
- }
51
- const runStatus = opts?.persistedStatus ?? detail.status;
52
- const recoveryActive = opts?.recoveryActive === true;
53
- if (runStatus !== "failed" && runStatus !== "succeeded") {
54
- const acceptedPendingRetry = runStatus === "running" &&
55
- stageSnap.status === "failed" &&
56
- findUnhandledFailedStage(detail.stages, opts?.dag) === undefined &&
57
- detail.stages.some((stage) => stage.status === "pending");
58
- if (!(runStatus === "running" && recoveryActive) && !acceptedPendingRetry) {
59
- return {
60
- ok: false,
61
- reason: `Run is not failed or succeeded (status=${runStatus})`,
62
- status: 409,
63
- code: "run_not_retryable",
64
- };
65
- }
66
- }
67
- if (stageSnap.status !== "failed" && stageSnap.status !== "succeeded") {
68
- return {
69
- ok: false,
70
- reason: `Stage is not failed or succeeded (status=${stageSnap.status})`,
71
- status: 409,
72
- code: "stage_not_failed",
73
- };
74
- }
75
- if (stageSnap.status === "succeeded") {
76
- const blocker = succeededStageRetryBlocker(stageSnap);
77
- if (blocker !== undefined) {
78
- return {
79
- ok: false,
80
- reason: blocker,
81
- status: 409,
82
- code: "run_not_retryable",
83
- };
84
- }
85
- }
86
- return { ok: true };
87
- }
88
24
  function mutationKey(stageId, attempt) {
89
25
  return `${stageId}\0${attempt}`;
90
26
  }
@@ -318,7 +254,7 @@ export class RunRetryCoordinator {
318
254
  };
319
255
  }
320
256
  insertedForResume = tracked.insertedForResume === true;
321
- if (priorRunStatus === "failed" || priorRunStatus === "succeeded") {
257
+ if (resumesRunOnStageRetry(priorRunStatus)) {
322
258
  await store.updateRunStatus(runId, "running");
323
259
  bumpedRunning = true;
324
260
  }
@@ -1,5 +1,7 @@
1
1
  export declare const SANDBOX_CONTAINER_IMAGE_ENV = "STAGEFLOW_STAGE_CONTAINER_IMAGE";
2
2
  export declare const SANDBOX_CONTAINER_DOCKER_BIN_ENV = "STAGEFLOW_STAGE_CONTAINER_DOCKER_BIN";
3
+ /** One host directory bind-mounted read-only into every stage sandbox, at the same path. */
4
+ export declare const STAGE_READONLY_DIR_ENV = "STAGEFLOW_STAGE_READONLY_DIR";
3
5
  /** Docker container names allow only `[a-zA-Z0-9_.-]`. */
4
6
  export declare function sanitizeContainerNameSegment(value: string): string;
5
7
  export declare function buildContainerName(input: {
@@ -18,9 +20,13 @@ export declare function resolveSandboxContainerOptions(env: Record<string, strin
18
20
  /** Fixed path inside the sandbox. The daemon owns the volume; we never pass it a path from this process. */
19
21
  export declare const SANDBOX_MOUNT_PATH = "/workspace";
20
22
  export declare function sandboxVolumeName(containerName: string): string;
23
+ export declare function stageReadOnlyDir(env: Record<string, string | undefined>): string | undefined;
24
+ export declare function assertStageReadOnlyDir(raw: string): string;
25
+ export declare function assertStageReadOnlyDirExists(dir: string): Promise<void>;
21
26
  export declare function buildStartContainerArgs(params: {
22
27
  image: string;
23
28
  containerName: string;
29
+ readOnlyDir?: string;
24
30
  }): string[];
25
31
  export declare function startSandboxContainer(params: {
26
32
  image: string;
@@ -28,6 +34,7 @@ export declare function startSandboxContainer(params: {
28
34
  stageId: string;
29
35
  attempt?: number;
30
36
  dockerBin?: string;
37
+ readOnlyDir?: string;
31
38
  }): Promise<{
32
39
  containerName: string;
33
40
  }>;
@@ -1,8 +1,12 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
+ import { stat } from "node:fs/promises";
4
+ import path from "node:path";
3
5
  import { promisify } from "node:util";
4
6
  export const SANDBOX_CONTAINER_IMAGE_ENV = "STAGEFLOW_STAGE_CONTAINER_IMAGE";
5
7
  export const SANDBOX_CONTAINER_DOCKER_BIN_ENV = "STAGEFLOW_STAGE_CONTAINER_DOCKER_BIN";
8
+ /** One host directory bind-mounted read-only into every stage sandbox, at the same path. */
9
+ export const STAGE_READONLY_DIR_ENV = "STAGEFLOW_STAGE_READONLY_DIR";
6
10
  const execFileAsync = promisify(execFile);
7
11
  /** Docker container names allow only `[a-zA-Z0-9_.-]`. */
8
12
  export function sanitizeContainerNameSegment(value) {
@@ -35,21 +39,55 @@ export function sandboxVolumeName(containerName) {
35
39
  const safe = containerName.replace(/[^A-Za-z0-9_.-]/g, "_");
36
40
  return `sf-bash-${safe}`;
37
41
  }
42
+ export function stageReadOnlyDir(env) {
43
+ const raw = env[STAGE_READONLY_DIR_ENV];
44
+ if (raw === undefined || raw.trim() === "")
45
+ return undefined;
46
+ return assertStageReadOnlyDir(raw);
47
+ }
48
+ export function assertStageReadOnlyDir(raw) {
49
+ const trimmed = raw.trim();
50
+ if (!path.isAbsolute(trimmed)) {
51
+ throw new Error(`${STAGE_READONLY_DIR_ENV} must be an absolute path`);
52
+ }
53
+ if (trimmed.includes("\0") || trimmed.includes(":")) {
54
+ throw new Error(`${STAGE_READONLY_DIR_ENV} contains a character docker cannot mount`);
55
+ }
56
+ const resolved = path.resolve(trimmed);
57
+ const parts = resolved.split(path.sep).filter((part) => part.length > 0);
58
+ if (parts.length < 3) {
59
+ throw new Error(`${STAGE_READONLY_DIR_ENV} is too broad`);
60
+ }
61
+ return resolved;
62
+ }
63
+ export async function assertStageReadOnlyDirExists(dir) {
64
+ let info;
65
+ try {
66
+ info = await stat(dir);
67
+ }
68
+ catch {
69
+ throw new Error(`read-only folder is not available: ${dir}`);
70
+ }
71
+ if (!info.isDirectory()) {
72
+ throw new Error(`read-only folder is not a directory: ${dir}`);
73
+ }
74
+ }
38
75
  export function buildStartContainerArgs(params) {
39
76
  const { image, containerName } = params;
40
- return [
77
+ const args = [
41
78
  "run",
42
79
  "-d",
43
80
  "--name",
44
81
  containerName,
45
82
  "-v",
46
83
  `${sandboxVolumeName(containerName)}:${SANDBOX_MOUNT_PATH}`,
47
- "-w",
48
- SANDBOX_MOUNT_PATH,
49
- image,
50
- "sleep",
51
- "infinity",
52
84
  ];
85
+ if (params.readOnlyDir !== undefined) {
86
+ const dir = assertStageReadOnlyDir(params.readOnlyDir);
87
+ args.push("-v", `${dir}:${dir}:ro`);
88
+ }
89
+ args.push("-w", SANDBOX_MOUNT_PATH, image, "sleep", "infinity");
90
+ return args;
53
91
  }
54
92
  function errorMessage(err) {
55
93
  if (err && typeof err === "object" && "stderr" in err) {
@@ -61,9 +99,13 @@ function errorMessage(err) {
61
99
  }
62
100
  export async function startSandboxContainer(params) {
63
101
  const containerName = buildContainerName({ runId: params.runId, stageId: params.stageId, attempt: params.attempt }, randomUUID().slice(0, 8));
102
+ if (params.readOnlyDir !== undefined) {
103
+ await assertStageReadOnlyDirExists(params.readOnlyDir);
104
+ }
64
105
  const args = buildStartContainerArgs({
65
106
  image: params.image,
66
107
  containerName,
108
+ ...(params.readOnlyDir !== undefined ? { readOnlyDir: params.readOnlyDir } : {}),
67
109
  });
68
110
  try {
69
111
  await execFileAsync(params.dockerBin ?? "docker", args);
@@ -1,3 +1,4 @@
1
+ import type { StageArtifactGrant } from "./stageRoots.js";
1
2
  export type SandboxDeskRole = "artifacts" | "envelope" | "checkout" | "grant" | "work";
2
3
  export type SandboxDeskBinding = {
3
4
  role: SandboxDeskRole;
@@ -14,7 +15,7 @@ export type SandboxDesk = {
14
15
  export declare function buildSandboxDesk(input: {
15
16
  attemptDir: string;
16
17
  checkoutRoot?: string;
17
- grantedPaths?: readonly string[];
18
+ grants?: readonly StageArtifactGrant[];
18
19
  }): SandboxDesk;
19
20
  export declare function populateSandboxDesk(params: {
20
21
  containerName: string;
@@ -37,16 +37,14 @@ export function buildSandboxDesk(input) {
37
37
  optional: false,
38
38
  });
39
39
  }
40
- const grants = input.grantedPaths ?? [];
41
- for (let index = 0; index < grants.length; index += 1) {
42
- const hostPath = grants[index];
40
+ for (const grant of input.grants ?? []) {
43
41
  bindings.push({
44
42
  role: "grant",
45
- hostPath,
46
- containerPath: `${SANDBOX_MOUNT_PATH}/grants/${grantSegment(index, hostPath)}`,
47
- shape: "mirror",
43
+ hostPath: grant.hostPath,
44
+ containerPath: grant.containerPath,
45
+ shape: grant.kind === "directory" ? "directory" : "file",
48
46
  copyIn: true,
49
- copyOut: true,
47
+ copyOut: false,
50
48
  optional: false,
51
49
  });
52
50
  }
@@ -61,11 +59,6 @@ export function buildSandboxDesk(input) {
61
59
  });
62
60
  return { bindings };
63
61
  }
64
- function grantSegment(index, hostPath) {
65
- const base = path.basename(hostPath).replace(/[^A-Za-z0-9_.-]/g, "_");
66
- const safe = base.length > 0 && base !== "." && base !== ".." ? base : "path";
67
- return `${index}-${safe}`;
68
- }
69
62
  async function pathExists(target) {
70
63
  try {
71
64
  await access(target);
@@ -85,6 +78,12 @@ export async function populateSandboxDesk(params) {
85
78
  if (binding.role === "checkout" || binding.shape === "mirror") {
86
79
  dirs.add(binding.containerPath);
87
80
  }
81
+ if (binding.role === "grant" && binding.shape === "file") {
82
+ dirs.add(path.posix.dirname(binding.containerPath));
83
+ }
84
+ if (binding.role === "grant" && binding.shape === "directory") {
85
+ dirs.add(binding.containerPath);
86
+ }
88
87
  }
89
88
  const made = await execInSandboxContainer({
90
89
  containerName: params.containerName,
@@ -28,6 +28,7 @@ export const STAGE_ENV_ALLOWLIST = [
28
28
  "STAGEFLOW_CURSOR_EXTENSION",
29
29
  "STAGEFLOW_STAGE_CONTAINER_IMAGE",
30
30
  "STAGEFLOW_STAGE_CONTAINER_DOCKER_BIN",
31
+ "STAGEFLOW_STAGE_READONLY_DIR",
31
32
  ];
32
33
  export const PROVIDER_AND_CLOUD_NAMES = [
33
34
  "ANTHROPIC_API_KEY",
@@ -2,6 +2,14 @@ import type { RunMeta } from "../runstore/port.js";
2
2
  import type { StageAttemptContext } from "./stageAttemptContext.js";
3
3
  import type { TaskFile, TaskGitIdentity } from "../types/task.js";
4
4
  export type DerivedBindingKind = "repository" | "checkout" | "unbound";
5
+ export type StageArtifactGrant = {
6
+ /** Run-relative path from the prior envelope. */
7
+ listedPath: string;
8
+ hostPath: string;
9
+ /** File or directory inside the sandbox, under /workspace/grants. */
10
+ containerPath: string;
11
+ kind: "file" | "directory";
12
+ };
5
13
  export type StageRoots = {
6
14
  mode: "bound" | "unbound";
7
15
  cwd: string;
@@ -19,6 +27,10 @@ export type StageRoots = {
19
27
  containerName?: string;
20
28
  /** The docker binary the container above was started with (see STAGEFLOW_STAGE_CONTAINER_DOCKER_BIN). */
21
29
  containerDockerBin?: string;
30
+ /** Host folder bind-mounted read-only into the sandbox at this same path. */
31
+ readonlyDir?: string;
32
+ /** Prior artifacts mounted into the sandbox. Empty or omitted when none are granted. */
33
+ artifactGrants?: readonly StageArtifactGrant[];
22
34
  };
23
35
  export declare const STAGEFLOW_CHECKOUT = "STAGEFLOW_CHECKOUT";
24
36
  export declare const STAGEFLOW_RUN_WORKSPACE = "STAGEFLOW_RUN_WORKSPACE";
@@ -1,4 +1,4 @@
1
- import { type StageRoots } from "./stageRoots.js";
1
+ import { type StageArtifactGrant, type StageRoots } from "./stageRoots.js";
2
2
  export type StageSandbox = {
3
3
  roots: StageRoots;
4
4
  beforeVerify?: () => Promise<void>;
@@ -13,4 +13,5 @@ export declare function openStageSandbox(input: {
13
13
  attempt: number;
14
14
  attemptDir: string;
15
15
  checkoutRoot?: string;
16
+ grants?: readonly StageArtifactGrant[];
16
17
  }): Promise<StageSandbox>;
@@ -1,4 +1,4 @@
1
- import { resolveSandboxContainerOptions, startSandboxContainer, stopSandboxContainer, } from "./sandboxContainer.js";
1
+ import { assertStageReadOnlyDirExists, resolveSandboxContainerOptions, stageReadOnlyDir, startSandboxContainer, stopSandboxContainer, } from "./sandboxContainer.js";
2
2
  import { buildSandboxDesk, populateSandboxDesk, syncSandboxDesk, } from "./sandboxDesk.js";
3
3
  import { withContainerName } from "./stageRoots.js";
4
4
  export async function openStageSandbox(input) {
@@ -6,18 +6,34 @@ export async function openStageSandbox(input) {
6
6
  if (!options) {
7
7
  return { roots: input.roots, close: async () => { } };
8
8
  }
9
+ let readOnlyDir;
10
+ try {
11
+ readOnlyDir = stageReadOnlyDir(input.env);
12
+ if (readOnlyDir !== undefined)
13
+ await assertStageReadOnlyDirExists(readOnlyDir);
14
+ }
15
+ catch (err) {
16
+ return {
17
+ roots: input.roots,
18
+ close: async () => { },
19
+ failure: err instanceof Error ? err.message : String(err),
20
+ };
21
+ }
9
22
  const started = await startSandboxContainer({
10
23
  image: options.image,
11
24
  dockerBin: options.dockerBin,
12
25
  runId: input.runId,
13
26
  stageId: input.stageId,
14
27
  attempt: input.attempt,
28
+ ...(readOnlyDir !== undefined ? { readOnlyDir } : {}),
15
29
  });
16
- const roots = withContainerName(input.roots, started.containerName, options.dockerBin);
30
+ const roots = withContainerName(readOnlyDir !== undefined ? { ...input.roots, readonlyDir: readOnlyDir } : input.roots, started.containerName, options.dockerBin);
17
31
  const checkout = input.checkoutRoot?.trim();
32
+ const grants = input.grants ?? [];
18
33
  const desk = buildSandboxDesk({
19
34
  attemptDir: input.attemptDir,
20
35
  ...(checkout ? { checkoutRoot: checkout } : {}),
36
+ ...(grants.length > 0 ? { grants } : {}),
21
37
  });
22
38
  const session = {
23
39
  roots,
@@ -16,6 +16,9 @@ import { runStage, isRunStageWaiting, } from "./stageRunner.js";
16
16
  import { openStageAttempt } from "./stageAttemptBootstrap.js";
17
17
  import { attemptContext } from "./stageAttemptContext.js";
18
18
  import { loadActiveFeedbackLoopContext } from "./feedbackLoopCoordinator.js";
19
+ import { buildCompletedEnvelopesFromRun, resolvePriorEnvelope, } from "./envelopeRouting.js";
20
+ import { assignment } from "./cloneSchedule.js";
21
+ import { artifactGrantsFromPriors } from "./priorArtifactGrants.js";
19
22
  import { outcomeToWorkerResult, SF_STAGE_WORKER, STAGE_WORKER_EXIT, } from "./stageWorkerProtocol.js";
20
23
  export async function runStageWorker(input) {
21
24
  const store = createRunStore({ rootDir: globalStageflowHome() });
@@ -67,6 +70,19 @@ export async function runStageWorker(input) {
67
70
  const eventOptions = { attempt };
68
71
  let roots = withResolvedAuthPath(rootsForStageWorker(workspaceDir, input.stageId, stage.model, checkoutRoot, attemptCtx), input.rootDir);
69
72
  const unbindAgentDir = bindPiAgentDirEnv(roots.agentDir);
73
+ const completedEnvelopes = await buildCompletedEnvelopesFromRun(store, input.runId, undefined, dag);
74
+ const assigned = assignment(dag, input.stageId, definitionId, completedEnvelopes);
75
+ const priorResult = assigned ??
76
+ (await resolvePriorEnvelope({
77
+ dag,
78
+ stageId: input.stageId,
79
+ completedEnvelopes,
80
+ store,
81
+ runId: input.runId,
82
+ }));
83
+ const artifactGrants = priorResult.ok
84
+ ? await artifactGrantsFromPriors(workspaceDir, priorResult)
85
+ : [];
70
86
  const sandbox = await openStageSandbox({
71
87
  env: process.env,
72
88
  roots,
@@ -75,8 +91,12 @@ export async function runStageWorker(input) {
75
91
  attempt,
76
92
  attemptDir,
77
93
  ...(checkoutRoot !== undefined ? { checkoutRoot } : {}),
94
+ ...(artifactGrants.length > 0 ? { grants: artifactGrants } : {}),
78
95
  });
79
96
  roots = sandbox.roots;
97
+ if (artifactGrants.length > 0) {
98
+ roots = { ...roots, artifactGrants };
99
+ }
80
100
  const beforeVerify = sandbox.beforeVerify;
81
101
  let outcome;
82
102
  let thrown;
@@ -1,5 +1,5 @@
1
- import { authorSessionForApi, parseAgentAuthorDraft, runAgentAuthorTurn, undoAgentAuthorTurn, } from "../agents/authorChat.js";
2
- import { createAuthorSession, listAuthorDrafts, readAuthorSession, saveAuthorSession } from "../agents/authorSessionStore.js";
1
+ import { authorSessionForApi, forgetAgentAuthorSession, parseAgentAuthorDraft, runAgentAuthorTurn, undoAgentAuthorTurn, } from "../agents/authorChat.js";
2
+ import { createAuthorSession, deleteAuthorSession, listAuthorDrafts, readAuthorSession, saveAuthorSession, } from "../agents/authorSessionStore.js";
3
3
  import { parseWorkshopModel } from "../workshop/modelSettings.js";
4
4
  async function readBody(req) {
5
5
  const chunks = [];
@@ -46,13 +46,22 @@ export async function handleAgentAuthorRoutes(req, method, pathname, projectRoot
46
46
  json(res, 200, { drafts: listAuthorDrafts() });
47
47
  return true;
48
48
  }
49
- if (sessionMatch && (method === "GET" || method === "PUT")) {
49
+ if (sessionMatch && (method === "GET" || method === "PUT" || method === "DELETE")) {
50
50
  const sessionId = decodeURIComponent(sessionMatch[1] ?? "");
51
51
  try {
52
52
  if (method === "GET") {
53
53
  json(res, 200, authorSessionForApi(sessionId));
54
54
  return true;
55
55
  }
56
+ if (method === "DELETE") {
57
+ forgetAgentAuthorSession(sessionId);
58
+ if (!deleteAuthorSession(sessionId)) {
59
+ json(res, 404, { code: "agent.session_not_found" });
60
+ return true;
61
+ }
62
+ json(res, 200, { removed: true });
63
+ return true;
64
+ }
56
65
  const body = await readBody(req);
57
66
  if (body.__tooLarge || body.__invalid) {
58
67
  json(res, 400, { code: "agent.invalid" });
@@ -17,6 +17,7 @@ import { type RunChangeBus } from "../runtime/runChangeBus.js";
17
17
  import { type McpHttpHandler } from "../mcp/server.js";
18
18
  import { type HostConfig } from "../config/hostConfig.js";
19
19
  import { bootProviderConfig } from "../agent/bootProviderConfig.js";
20
+ import type { OperatorAgentHost } from "../operatorAgent/types.js";
20
21
  import { type AgentHostContext } from "../agents/hostContext.js";
21
22
  import type { ConversationBackend } from "../agents/sessions.js";
22
23
  export declare const DEFAULT_GC_INTERVAL_MS: number;
@@ -78,6 +79,10 @@ export type StageflowHostBootstrap = {
78
79
  memories?: MemoryHost;
79
80
  /** Shared agent store, chat controller, and conversation backend for HTTP/MCP. */
80
81
  agentHost?: AgentHostContext;
82
+ /** Filled when the operator console host is created, so internal-agent evals can open a gated session. */
83
+ operatorHostSlot?: {
84
+ current?: OperatorAgentHost;
85
+ };
81
86
  email?: ReturnType<typeof emailHostFor> & {
82
87
  triggers: EmailTriggers;
83
88
  stop: () => Promise<void>;
@@ -40,6 +40,8 @@ import { logger as rootLogger } from "../logging/logger.js";
40
40
  import { getModelCatalog } from "../models/catalog.js";
41
41
  import { createAgentCaseRunner } from "../evals/runner/agentCaseRunner.js";
42
42
  import { createEvalCaseRunner } from "../evals/runner/caseRunner.js";
43
+ import { createInternalAgentCaseRunner } from "../evals/runner/internalAgentCaseRunner.js";
44
+ import { describeInternalAgents, internalAgentsVisible } from "../operatorAgent/internalAgents.js";
43
45
  import { defaultLoadJudgePrices } from "../evals/engine/promptfoo/judgeServices.js";
44
46
  import { createAgentHostContext } from "../agents/hostContext.js";
45
47
  export const DEFAULT_GC_INTERVAL_MS = 60 * 60 * 1000;
@@ -268,17 +270,22 @@ export async function bootstrapStageflowHost(options) {
268
270
  await manager.reenqueuePersistedQueuedRuns();
269
271
  const evalLog = rootLogger.child({ component: "evals" });
270
272
  const evalStageRunner = createStageCaseRunner({ manager, store });
273
+ const operatorHostSlot = {};
271
274
  const evalAgentRunner = createAgentCaseRunner({
272
275
  host: () => agentHost,
273
276
  loadPrices: defaultLoadJudgePrices,
274
277
  });
275
- const evalRunner = createEvalCaseRunner({ stage: evalStageRunner, agent: evalAgentRunner });
278
+ const evalInternalRunner = createInternalAgentCaseRunner({ host: () => operatorHostSlot.current });
279
+ const evalRunner = createEvalCaseRunner({ stage: evalStageRunner, agent: evalAgentRunner, internalAgent: evalInternalRunner });
276
280
  const evalTokens = new RunTokenRegistry();
277
281
  let evalCallbackBase = "";
278
282
  const evalRunManager = new EvalRunManager({
279
283
  store,
280
284
  stageRunner: evalRunner,
281
285
  agents: () => agentHost?.service,
286
+ internalAgents: () => operatorHostSlot.current && internalAgentsVisible()
287
+ ? describeInternalAgents(operatorHostSlot.current).map(({ id, title, playbook, tools }) => ({ id, title, playbook, tools }))
288
+ : [],
282
289
  engines: (name, engineOptions) => getEngine(name, name === EVAL_DEFAULT_ENGINE
283
290
  ? {
284
291
  tokens: evalTokens,
@@ -400,6 +407,9 @@ export async function bootstrapStageflowHost(options) {
400
407
  store,
401
408
  agentDir,
402
409
  projectRoot: rootDir,
410
+ channels,
411
+ memories: memoryHostFor(),
412
+ evalRunManager,
403
413
  ...(options.providerAuthContext ? { providerAuthContext: options.providerAuthContext } : {}),
404
414
  ...(providerBoot !== undefined ? { providerBoot } : {}),
405
415
  },
@@ -419,6 +429,8 @@ export async function bootstrapStageflowHost(options) {
419
429
  providerAuthContext: options.providerAuthContext,
420
430
  projectRoot: rootDir,
421
431
  evalRunManager,
432
+ channels,
433
+ memories: memoryHostFor(),
422
434
  ...(agentHost
423
435
  ? {
424
436
  agentService: agentHost.service,
@@ -462,5 +474,6 @@ export async function bootstrapStageflowHost(options) {
462
474
  storeFilesystem,
463
475
  workspaceCatalog,
464
476
  ...(agentHost ? { agentHost } : {}),
477
+ operatorHostSlot,
465
478
  };
466
479
  }