stageflow 0.35.0 → 0.39.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 (320) hide show
  1. package/README.md +18 -8
  2. package/dist/agent/claudeAdapter.js +12 -0
  3. package/dist/agent/piAdapter.d.ts +9 -1
  4. package/dist/agent/piAdapter.js +30 -2
  5. package/dist/agent/piIsolatedMcp.js +14 -2
  6. package/dist/agent/piIsolatedMcpProbe.d.ts +5 -1
  7. package/dist/agent/piIsolatedMcpProbe.js +16 -30
  8. package/dist/agent/port.d.ts +7 -0
  9. package/dist/agents/agentId.d.ts +8 -0
  10. package/dist/agents/agentId.js +27 -0
  11. package/dist/agents/approvalBroker.d.ts +16 -0
  12. package/dist/agents/approvalBroker.js +45 -0
  13. package/dist/agents/authorChat.d.ts +19 -0
  14. package/dist/agents/authorChat.js +63 -0
  15. package/dist/agents/chatTurn.d.ts +23 -0
  16. package/dist/agents/chatTurn.js +77 -0
  17. package/dist/agents/communications/agentmail.d.ts +19 -0
  18. package/dist/agents/communications/agentmail.js +46 -0
  19. package/dist/agents/communications/agentmailListener.d.ts +59 -0
  20. package/dist/agents/communications/agentmailListener.js +183 -0
  21. package/dist/agents/communications/allowlist.d.ts +10 -0
  22. package/dist/agents/communications/allowlist.js +36 -0
  23. package/dist/agents/communications/channelConnections.d.ts +3 -0
  24. package/dist/agents/communications/channelConnections.js +51 -0
  25. package/dist/agents/communications/delivery.d.ts +53 -0
  26. package/dist/agents/communications/delivery.js +142 -0
  27. package/dist/agents/communications/email.d.ts +26 -0
  28. package/dist/agents/communications/email.js +20 -0
  29. package/dist/agents/communications/identity.d.ts +13 -0
  30. package/dist/agents/communications/identity.js +18 -0
  31. package/dist/agents/communications/imessageListener.d.ts +41 -0
  32. package/dist/agents/communications/imessageListener.js +92 -0
  33. package/dist/agents/communications/inbound.d.ts +63 -0
  34. package/dist/agents/communications/inbound.js +143 -0
  35. package/dist/agents/communications/listener.d.ts +36 -0
  36. package/dist/agents/communications/listener.js +38 -0
  37. package/dist/agents/communications/photon.d.ts +19 -0
  38. package/dist/agents/communications/photon.js +38 -0
  39. package/dist/agents/communications/port.d.ts +27 -0
  40. package/dist/agents/communications/port.js +6 -0
  41. package/dist/agents/communications/slack.d.ts +28 -0
  42. package/dist/agents/communications/slack.js +65 -0
  43. package/dist/agents/communications/supervisor.d.ts +36 -0
  44. package/dist/agents/communications/supervisor.js +96 -0
  45. package/dist/agents/communications/telegram.d.ts +19 -0
  46. package/dist/agents/communications/telegram.js +52 -0
  47. package/dist/agents/definitionSummary.d.ts +13 -0
  48. package/dist/agents/definitionSummary.js +91 -0
  49. package/dist/agents/delegate.d.ts +3 -0
  50. package/dist/agents/delegate.js +8 -0
  51. package/dist/agents/errors.d.ts +10 -0
  52. package/dist/agents/errors.js +32 -0
  53. package/dist/agents/guardrails.d.ts +24 -0
  54. package/dist/agents/guardrails.js +39 -0
  55. package/dist/agents/historyFit.d.ts +10 -0
  56. package/dist/agents/historyFit.js +26 -0
  57. package/dist/agents/hostContext.d.ts +54 -0
  58. package/dist/agents/hostContext.js +156 -0
  59. package/dist/agents/ledgers.d.ts +29 -0
  60. package/dist/agents/ledgers.js +31 -0
  61. package/dist/agents/lineDiff.d.ts +12 -0
  62. package/dist/agents/lineDiff.js +46 -0
  63. package/dist/agents/ndjson.d.ts +9 -0
  64. package/dist/agents/ndjson.js +47 -0
  65. package/dist/agents/otlpExport.d.ts +9 -0
  66. package/dist/agents/otlpExport.js +58 -0
  67. package/dist/agents/otlpSink.d.ts +7 -0
  68. package/dist/agents/otlpSink.js +23 -0
  69. package/dist/agents/policy.d.ts +31 -0
  70. package/dist/agents/policy.js +84 -0
  71. package/dist/agents/projectRoot.d.ts +5 -0
  72. package/dist/agents/projectRoot.js +15 -0
  73. package/dist/agents/runtime/piRuntime.d.ts +52 -0
  74. package/dist/agents/runtime/piRuntime.js +474 -0
  75. package/dist/agents/runtime/types.d.ts +77 -0
  76. package/dist/agents/runtime/types.js +26 -0
  77. package/dist/agents/sandbox.d.ts +17 -0
  78. package/dist/agents/sandbox.js +10 -0
  79. package/dist/agents/scheduleSource.d.ts +32 -0
  80. package/dist/agents/scheduleSource.js +100 -0
  81. package/dist/agents/service.d.ts +298 -0
  82. package/dist/agents/service.js +1105 -0
  83. package/dist/agents/sessions.d.ts +38 -0
  84. package/dist/agents/sessions.js +321 -0
  85. package/dist/agents/store.d.ts +277 -0
  86. package/dist/agents/store.js +643 -0
  87. package/dist/agents/trace.d.ts +27 -0
  88. package/dist/agents/trace.js +29 -0
  89. package/dist/agents/turnLifecycle.d.ts +72 -0
  90. package/dist/agents/turnLifecycle.js +157 -0
  91. package/dist/agents/webChat/publicChat.d.ts +36 -0
  92. package/dist/agents/webChat/publicChat.js +290 -0
  93. package/dist/agents/webChat/rateLimit.d.ts +15 -0
  94. package/dist/agents/webChat/rateLimit.js +33 -0
  95. package/dist/agents/webChat/slug.d.ts +2 -0
  96. package/dist/agents/webChat/slug.js +9 -0
  97. package/dist/channels/config.d.ts +11 -0
  98. package/dist/channels/config.js +95 -0
  99. package/dist/channels/configWriter.d.ts +40 -0
  100. package/dist/channels/configWriter.js +148 -0
  101. package/dist/channels/connectionEdits.d.ts +34 -0
  102. package/dist/channels/connectionEdits.js +53 -0
  103. package/dist/channels/errorInfo.d.ts +18 -0
  104. package/dist/channels/errorInfo.js +71 -0
  105. package/dist/channels/errors.d.ts +12 -0
  106. package/dist/channels/errors.js +13 -0
  107. package/dist/channels/health.d.ts +41 -0
  108. package/dist/channels/health.js +99 -0
  109. package/dist/channels/host.d.ts +55 -0
  110. package/dist/channels/host.js +185 -0
  111. package/dist/channels/http.d.ts +31 -0
  112. package/dist/channels/http.js +79 -0
  113. package/dist/channels/index.d.ts +10 -0
  114. package/dist/channels/index.js +9 -0
  115. package/dist/channels/ledger.d.ts +37 -0
  116. package/dist/channels/ledger.js +95 -0
  117. package/dist/channels/providers/agentmail.d.ts +26 -0
  118. package/dist/channels/providers/agentmail.js +94 -0
  119. package/dist/channels/providers/kit.d.ts +40 -0
  120. package/dist/channels/providers/kit.js +78 -0
  121. package/dist/channels/providers/photonImessage.d.ts +45 -0
  122. package/dist/channels/providers/photonImessage.js +188 -0
  123. package/dist/channels/providers/photonWhatsapp.d.ts +49 -0
  124. package/dist/channels/providers/photonWhatsapp.js +146 -0
  125. package/dist/channels/providers/slack.d.ts +25 -0
  126. package/dist/channels/providers/slack.js +102 -0
  127. package/dist/channels/providers/telegram.d.ts +21 -0
  128. package/dist/channels/providers/telegram.js +101 -0
  129. package/dist/channels/providers.d.ts +5 -0
  130. package/dist/channels/providers.js +22 -0
  131. package/dist/channels/registry.d.ts +54 -0
  132. package/dist/channels/registry.js +186 -0
  133. package/dist/channels/secretStore.d.ts +21 -0
  134. package/dist/channels/secretStore.js +75 -0
  135. package/dist/channels/secrets.d.ts +15 -0
  136. package/dist/channels/secrets.js +50 -0
  137. package/dist/channels/service.d.ts +54 -0
  138. package/dist/channels/service.js +307 -0
  139. package/dist/channels/stageChannels.d.ts +45 -0
  140. package/dist/channels/stageChannels.js +110 -0
  141. package/dist/channels/types.d.ts +169 -0
  142. package/dist/channels/types.js +1 -0
  143. package/dist/cli/agentsCommand.d.ts +14 -0
  144. package/dist/cli/agentsCommand.js +291 -0
  145. package/dist/cli/doctorCommand.js +3 -1
  146. package/dist/cli.js +9 -0
  147. package/dist/config/browseCatalog.js +4 -11
  148. package/dist/config/draftPackage.d.ts +2 -0
  149. package/dist/config/draftPackage.js +8 -0
  150. package/dist/config/hostConfig.js +8 -0
  151. package/dist/config/loadAgent.d.ts +266 -0
  152. package/dist/config/loadAgent.js +374 -0
  153. package/dist/config/loadPipeline.js +31 -4
  154. package/dist/config/loadStage.js +15 -0
  155. package/dist/config/mcpHostCatalog.d.ts +50 -0
  156. package/dist/config/mcpHostCatalog.js +405 -0
  157. package/dist/config/mcpOAuth.d.ts +37 -0
  158. package/dist/config/mcpOAuth.js +263 -0
  159. package/dist/config/mcpRedaction.d.ts +42 -0
  160. package/dist/config/mcpRedaction.js +314 -0
  161. package/dist/config/mcpToolCache.d.ts +20 -0
  162. package/dist/config/mcpToolCache.js +43 -0
  163. package/dist/config/mergePipelineIncludes.d.ts +5 -0
  164. package/dist/config/mergePipelineIncludes.js +52 -0
  165. package/dist/config/pipelineStageKeys.js +1 -0
  166. package/dist/config/resolveStageMcpServers.d.ts +35 -3
  167. package/dist/config/resolveStageMcpServers.js +124 -9
  168. package/dist/config/validateCatalog.d.ts +4 -1
  169. package/dist/config/validateCatalog.js +45 -1
  170. package/dist/errors/codes.d.ts +1 -1
  171. package/dist/errors/codes.js +1 -0
  172. package/dist/mcp/agentTools.d.ts +8 -0
  173. package/dist/mcp/agentTools.js +132 -0
  174. package/dist/mcp/catalogTools.js +7 -9
  175. package/dist/mcp/deps.d.ts +6 -0
  176. package/dist/mcp/pipelineAuthorSupport.d.ts +19 -0
  177. package/dist/mcp/pipelineAuthorSupport.js +10 -0
  178. package/dist/mcp/pipelineAuthorTools.d.ts +8 -0
  179. package/dist/mcp/pipelineAuthorTools.js +109 -0
  180. package/dist/mcp/server.js +5 -0
  181. package/dist/mcp/tools.js +4 -0
  182. package/dist/mcp/triggerTools.js +32 -0
  183. package/dist/memory/access.d.ts +36 -0
  184. package/dist/memory/access.js +116 -0
  185. package/dist/memory/agentIssues.d.ts +3 -0
  186. package/dist/memory/agentIssues.js +23 -0
  187. package/dist/memory/bashDeny.d.ts +6 -0
  188. package/dist/memory/bashDeny.js +35 -0
  189. package/dist/memory/host.d.ts +51 -0
  190. package/dist/memory/host.js +394 -0
  191. package/dist/memory/ids.d.ts +15 -0
  192. package/dist/memory/ids.js +31 -0
  193. package/dist/memory/worker.d.ts +1 -0
  194. package/dist/memory/worker.js +84 -0
  195. package/dist/models/catalog.d.ts +113 -0
  196. package/dist/models/catalog.js +238 -0
  197. package/dist/models/response.d.ts +29 -0
  198. package/dist/models/response.js +25 -0
  199. package/dist/operatorAgent/agentAuthorContext.d.ts +17 -0
  200. package/dist/operatorAgent/agentAuthorContext.js +32 -0
  201. package/dist/operatorAgent/fakeHost.js +2 -0
  202. package/dist/operatorAgent/host.js +6 -3
  203. package/dist/operatorAgent/index.d.ts +4 -0
  204. package/dist/operatorAgent/index.js +4 -0
  205. package/dist/operatorAgent/piModel.js +2 -0
  206. package/dist/operatorAgent/profiles/agentAuthor.d.ts +11 -0
  207. package/dist/operatorAgent/profiles/agentAuthor.js +192 -0
  208. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -0
  209. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +29 -0
  210. package/dist/operatorAgent/profiles/workshopAuthor.js +2 -2
  211. package/dist/operatorAgent/profiles/workshopAuthorTools.js +3 -3
  212. package/dist/operatorAgent/proposals.d.ts +1 -0
  213. package/dist/operatorAgent/proposals.js +23 -2
  214. package/dist/package-meta.d.ts +1 -1
  215. package/dist/package-meta.js +1 -1
  216. package/dist/preflight/channelChecks.d.ts +17 -0
  217. package/dist/preflight/channelChecks.js +106 -0
  218. package/dist/preflight/pipelinePreflight.d.ts +9 -2
  219. package/dist/preflight/pipelinePreflight.js +10 -1
  220. package/dist/runstore/createStore.d.ts +2 -0
  221. package/dist/runstore/createStore.js +8 -0
  222. package/dist/runstore/overviewSummary.d.ts +56 -0
  223. package/dist/runstore/overviewSummary.js +134 -0
  224. package/dist/runstore/port.d.ts +3 -0
  225. package/dist/runstore/sqlite/SqliteRunStore.d.ts +2 -0
  226. package/dist/runstore/sqlite/SqliteRunStore.js +18 -0
  227. package/dist/runstore/sqlite/migrations/013-agents.d.ts +12 -0
  228. package/dist/runstore/sqlite/migrations/013-agents.js +156 -0
  229. package/dist/runstore/sqlite/migrations/014-agent-deliveries.d.ts +11 -0
  230. package/dist/runstore/sqlite/migrations/014-agent-deliveries.js +32 -0
  231. package/dist/runstore/sqlite/migrations/index.js +4 -0
  232. package/dist/runtime/pipelineRunner.js +3 -1
  233. package/dist/runtime/repositoryMaterialize.d.ts +1 -1
  234. package/dist/runtime/runManager.js +4 -1
  235. package/dist/runtime/stageAttemptBootstrap.js +31 -1
  236. package/dist/runtime/stageEnvironment.d.ts +2 -0
  237. package/dist/runtime/stageEnvironment.js +6 -1
  238. package/dist/runtime/stageProcessLauncher.js +54 -1
  239. package/dist/runtime/stageSecrets.d.ts +6 -1
  240. package/dist/runtime/stageSecrets.js +34 -1
  241. package/dist/server/agentAuthorRoutes.d.ts +5 -0
  242. package/dist/server/agentAuthorRoutes.js +64 -0
  243. package/dist/server/agentRoutes.d.ts +5 -0
  244. package/dist/server/agentRoutes.js +323 -0
  245. package/dist/server/bootstrap.d.ts +11 -0
  246. package/dist/server/bootstrap.js +40 -10
  247. package/dist/server/channelRoutes.d.ts +9 -0
  248. package/dist/server/channelRoutes.js +88 -0
  249. package/dist/server/controlToken.js +8 -0
  250. package/dist/server/http.d.ts +8 -1
  251. package/dist/server/http.js +183 -20
  252. package/dist/server/mcpHost.js +1 -0
  253. package/dist/server/memoryRoutes.d.ts +5 -0
  254. package/dist/server/memoryRoutes.js +146 -0
  255. package/dist/server/projectMcpRoutes.d.ts +1 -0
  256. package/dist/server/projectMcpRoutes.js +241 -3
  257. package/dist/toolkit/guard.d.ts +27 -0
  258. package/dist/toolkit/guard.js +98 -0
  259. package/dist/toolkit/index.d.ts +48 -0
  260. package/dist/toolkit/index.js +82 -0
  261. package/dist/toolkit/meta.d.ts +36 -0
  262. package/dist/toolkit/meta.js +117 -0
  263. package/dist/toolkit/profiles.d.ts +13 -0
  264. package/dist/toolkit/profiles.js +25 -0
  265. package/dist/toolkit/record.d.ts +15 -0
  266. package/dist/toolkit/record.js +17 -0
  267. package/dist/tools/memoryTools.d.ts +95 -0
  268. package/dist/tools/memoryTools.js +179 -0
  269. package/dist/tools/sendMessage.d.ts +21 -0
  270. package/dist/tools/sendMessage.js +25 -0
  271. package/dist/types/pipeline.d.ts +7 -1
  272. package/dist/types/stage.d.ts +15 -0
  273. package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +9 -0
  274. package/dist/ui/assets/MarkdownText-D7tYaIFO.js +29 -0
  275. package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +6 -0
  276. package/dist/ui/assets/Tooltip-DrpoRRNi.js +1 -0
  277. package/dist/ui/assets/chat-BbLukq9W.js +5 -0
  278. package/dist/ui/assets/chat-zIZU_bP1.css +2 -0
  279. package/dist/ui/assets/jsx-runtime-iVoFAvQ_.js +1 -0
  280. package/dist/ui/assets/{index-CIPbbBUi.css → main-BFo79krC.css} +1 -1
  281. package/dist/ui/assets/main-CijYMeM0.js +259 -0
  282. package/dist/ui/chat.html +25 -0
  283. package/dist/ui/index.html +6 -2
  284. package/dist/workshop/modelSettings.d.ts +2 -2
  285. package/dist/workshop/modelSettings.js +5 -11
  286. package/examples/README.md +1 -0
  287. package/examples/channels-smoke/README.md +23 -0
  288. package/examples/channels-smoke/channels-smoke.pipeline.yaml +11 -0
  289. package/examples/channels-smoke/channels-smoke.task.yaml +3 -0
  290. package/examples/channels-smoke/send-email.yaml +32 -0
  291. package/examples/channels-smoke/send-text.yaml +31 -0
  292. package/examples/github-release/README.md +1 -1
  293. package/examples/memory/README.md +99 -0
  294. package/examples/memory/desk-outage.task.yaml +15 -0
  295. package/examples/memory/desk-refund.task.yaml +15 -0
  296. package/examples/memory/desk-revisit-outage.task.yaml +11 -0
  297. package/examples/memory/desk-revisit.pipeline.yaml +123 -0
  298. package/examples/memory/desk.pipeline.yaml +192 -0
  299. package/examples/memory/file-card.pipeline.yaml +39 -0
  300. package/examples/memory/file-ledger.task.yaml +22 -0
  301. package/examples/memory/file-release.task.yaml +24 -0
  302. package/examples/memory/file-rich.pipeline.yaml +40 -0
  303. package/examples/memory/file-ticket.task.yaml +7 -0
  304. package/examples/memory/file-timeline.task.yaml +28 -0
  305. package/examples/memory/find-card.pipeline.yaml +46 -0
  306. package/examples/memory/find-ticket.task.yaml +7 -0
  307. package/examples/memory/missing.pipeline.yaml +16 -0
  308. package/examples/memory/recall.pipeline.yaml +24 -0
  309. package/examples/memory/recall.task.yaml +5 -0
  310. package/examples/memory/remember-ada.task.yaml +6 -0
  311. package/examples/memory/remember-grace.task.yaml +6 -0
  312. package/examples/memory/remember.pipeline.yaml +58 -0
  313. package/examples/memory/stageflow.yaml +12 -0
  314. package/examples/memory/update-card.pipeline.yaml +43 -0
  315. package/examples/memory/update-ledger.task.yaml +23 -0
  316. package/examples/memory/update-rich.pipeline.yaml +44 -0
  317. package/examples/memory/update-ticket.task.yaml +7 -0
  318. package/examples/stage-mcp/README.md +1 -1
  319. package/package.json +9 -3
  320. package/dist/ui/assets/index-BoFPkKFg.js +0 -256
@@ -13,6 +13,10 @@ import { buildStageEnvironment, isAmbientBlockedEnv, isForeverDeniedSecret, } fr
13
13
  import { ensureStageCacheDirs } from "./stageCacheEnv.js";
14
14
  import { SF_STAGE_WORKER, STAGE_WORKER_EXIT, } from "./stageWorkerProtocol.js";
15
15
  import { emailHostFor, emailWorkerEnvironment, stageEmail, validateStageEmailAccounts } from "../email/host.js";
16
+ import { ChannelError } from "../channels/errors.js";
17
+ import { channelFault, channelHostFor, channelsWorkerEnvironment, stageChannels, validateStageChannelConnections } from "../channels/host.js";
18
+ import { MemoryError, memoryHostFor } from "../memory/host.js";
19
+ import { isMemoryVerb, runMemoryRequest } from "../memory/access.js";
16
20
  import { EmailError } from "../email/port.js";
17
21
  import { overlayStageBindingEnv, } from "./stageRoots.js";
18
22
  function defaultStageCliEntry() {
@@ -189,6 +193,15 @@ export class StageProcessLauncher {
189
193
  return { type: "failed", reason: error.code };
190
194
  throw error;
191
195
  }
196
+ try {
197
+ if (input.stage?.channels?.length)
198
+ validateStageChannelConnections(channelHostFor(input.factoryCwd ?? input.operatorCatalog?.cwd ?? input.rootDir), input.stage);
199
+ }
200
+ catch (error) {
201
+ if (error instanceof ChannelError)
202
+ return { type: "failed", reason: error.code };
203
+ throw error;
204
+ }
192
205
  input = { ...input, stage: input.stage ? structuredClone(input.stage) : undefined };
193
206
  const acquired = await this.waitForCapacity(input.runId);
194
207
  if (!acquired) {
@@ -363,7 +376,7 @@ export class StageProcessLauncher {
363
376
  }
364
377
  const child = this.forkFn(this.cliEntry, args, {
365
378
  cwd: input.rootDir,
366
- env: emailWorkerEnvironment({ ...childEnv, [SF_STAGE_WORKER]: "1" }, emailHostFor(factoryCwd).accounts),
379
+ env: channelsWorkerEnvironment(emailWorkerEnvironment({ ...childEnv, [SF_STAGE_WORKER]: "1" }, emailHostFor(factoryCwd).accounts), factoryCwd),
367
380
  stdio: ["pipe", "pipe", "pipe", "ipc"],
368
381
  detached: true,
369
382
  // Explicit: do not inherit Host execArgv; set heap from cgroup budget.
@@ -432,6 +445,46 @@ export class StageProcessLauncher {
432
445
  })();
433
446
  return;
434
447
  }
448
+ const memoryVerb = request?.type;
449
+ if (!settled && isMemoryVerb(memoryVerb)) {
450
+ void (async () => {
451
+ if (typeof request.requestId !== "string" || request.requestId.length > 100)
452
+ return;
453
+ let response;
454
+ try {
455
+ const stage = input.stage;
456
+ if (!stage || settled)
457
+ throw new MemoryError("MEMORY_NOT_ATTACHED");
458
+ response = await runMemoryRequest(memoryHostFor(), stage.memories ?? [], memoryVerb, request.input);
459
+ }
460
+ catch (error) {
461
+ const code = error instanceof MemoryError ? error.code : "MEMORY_UNREADABLE";
462
+ response = { error: { code } };
463
+ }
464
+ if (child.connected && !settled)
465
+ child.send({ type: "memory.response", requestId: request.requestId, ...response });
466
+ })();
467
+ return;
468
+ }
469
+ if (!settled && request?.type === "channel.send") {
470
+ void (async () => {
471
+ if (typeof request.requestId !== "string" || request.requestId.length > 100)
472
+ return;
473
+ let response;
474
+ try {
475
+ const stage = input.stage;
476
+ if (!stage || settled)
477
+ throw new ChannelError("CHANNEL_UNAUTHORIZED");
478
+ response = { receipt: await stageChannels(channelHostFor(factoryCwd), stage, input.runId).send(request.input) };
479
+ }
480
+ catch (error) {
481
+ response = { error: channelFault(error) };
482
+ }
483
+ if (child.connected && !settled)
484
+ child.send({ type: "channel.response", requestId: request.requestId, ...response });
485
+ })();
486
+ return;
487
+ }
435
488
  if (!isStageWorkerResult(message))
436
489
  return;
437
490
  finish(resultFromWorkerMessage(message));
@@ -9,6 +9,11 @@ export type SecretRegistryEntry = {
9
9
  name: string;
10
10
  source: string;
11
11
  pointerVar: string;
12
+ }
13
+ /** A secret saved from the console (Settings → Project MCP). It only fills `${NAME}` in MCP servers; never the stage environment. */
14
+ | {
15
+ kind: "console";
16
+ name: string;
12
17
  };
13
18
  export type SecretRegistry = Map<string, SecretRegistryEntry>;
14
19
  export declare class SecretUnavailableError extends Error {
@@ -35,6 +40,6 @@ export type ResolveStageSecretsResult = {
35
40
  credentialDirs: string[];
36
41
  };
37
42
  /** Presence-only check (no materialisation) for preflight / start gates. */
38
- export declare function assertSecretPresent(name: string, registry: SecretRegistry, hostEnv: NodeJS.ProcessEnv): void;
43
+ export declare function assertSecretPresent(name: string, registry: SecretRegistry, hostEnv: NodeJS.ProcessEnv, home?: string): void;
39
44
  export declare function resolveStageSecrets(input: ResolveStageSecretsInput): ResolveStageSecretsResult;
40
45
  export { cleanupCredentialsDir };
@@ -1,5 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { shouldRegisterValue } from "../logging/redact.js";
3
+ import { readMcpSecrets } from "../config/mcpHostCatalog.js";
3
4
  import { globalStageflowHome } from "../project/globalHome.js";
4
5
  import { attemptCredentialsDir, cleanupCredentialsDir, ensureStageAskpassHelper, materialiseCredentialFile, materialiseSecretBytes, readFileCredentialRegistry, } from "./credentialMaterialisation.js";
5
6
  import { defaultSecretDelivery, } from "./stageSecretDecl.js";
@@ -66,10 +67,20 @@ export function loadSecretRegistry(env = process.env, home = globalStageflowHome
66
67
  continue;
67
68
  registry.set(name, { kind: "env", name });
68
69
  }
70
+ // Console-stored MCP secrets: a stage may declare them so its `mcp:` servers can be filled.
71
+ try {
72
+ for (const name of Object.keys(readMcpSecrets(home))) {
73
+ if (!isForeverDeniedSecret(name) && !registry.has(name))
74
+ registry.set(name, { kind: "console", name });
75
+ }
76
+ }
77
+ catch {
78
+ // A corrupt host-state file is reported by the MCP routes; it must not stop other stages.
79
+ }
69
80
  return registry;
70
81
  }
71
82
  /** Presence-only check (no materialisation) for preflight / start gates. */
72
- export function assertSecretPresent(name, registry, hostEnv) {
83
+ export function assertSecretPresent(name, registry, hostEnv, home) {
73
84
  if (isForeverDeniedSecret(name)) {
74
85
  throw new Error(`secret "${name}" is permanently denied`);
75
86
  }
@@ -86,11 +97,24 @@ export function assertSecretPresent(name, registry, hostEnv) {
86
97
  }
87
98
  return;
88
99
  }
100
+ if (entry.kind === "console") {
101
+ if (!readMcpSecretValue(name, home))
102
+ throw new SecretUnavailableError(name);
103
+ return;
104
+ }
89
105
  const value = readEnvSecretValue(name, hostEnv);
90
106
  if (value === undefined || value === "") {
91
107
  throw new SecretUnavailableError(name);
92
108
  }
93
109
  }
110
+ function readMcpSecretValue(name, home) {
111
+ try {
112
+ return readMcpSecrets(home)[name];
113
+ }
114
+ catch {
115
+ return undefined;
116
+ }
117
+ }
94
118
  function materialiseGitAskpassGrant(grantEnv, credentialDirs, opts) {
95
119
  const destDir = attemptCredentialsDir(opts.attemptDir, opts.secretName);
96
120
  const tokenFile = materialiseSecretBytes({
@@ -119,6 +143,15 @@ export function resolveStageSecrets(input) {
119
143
  if (entry === undefined) {
120
144
  throw new Error(`stage.unknown_secret: "${decl.name}"`);
121
145
  }
146
+ if (entry.kind === "console") {
147
+ const stored = readMcpSecretValue(decl.name, input.home);
148
+ if (!stored)
149
+ throw new SecretUnavailableError(decl.name);
150
+ if (shouldRegisterValue(stored))
151
+ knownValues.push({ name: decl.name, value: stored });
152
+ warnings.push(`secrets: ${decl.name} is a console secret; it fills MCP servers only and is not set in the stage environment`);
153
+ continue;
154
+ }
122
155
  if (entry.kind === "file") {
123
156
  const destDir = attemptCredentialsDir(input.attemptDir, decl.name);
124
157
  const homeRoot = input.home ?? globalStageflowHome();
@@ -0,0 +1,5 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { OperatorAgentHost } from "../operatorAgent/index.js";
3
+ type Json = (res: ServerResponse, status: number, body: unknown) => void;
4
+ export declare function handleAgentAuthorRoutes(req: IncomingMessage, method: string, pathname: string, projectRoot: string, host: OperatorAgentHost, res: ServerResponse, json: Json): Promise<boolean>;
5
+ export {};
@@ -0,0 +1,64 @@
1
+ import { parseAgentAuthorDraft, runAgentAuthorTurn, undoAgentAuthorTurn } from "../agents/authorChat.js";
2
+ async function readBody(req) {
3
+ const chunks = [];
4
+ let size = 0;
5
+ for await (const chunk of req) {
6
+ const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
7
+ size += buffer.length;
8
+ if (size > 65536)
9
+ return { __tooLarge: true };
10
+ chunks.push(buffer);
11
+ }
12
+ if (chunks.length === 0)
13
+ return {};
14
+ try {
15
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
16
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
17
+ ? parsed
18
+ : { __invalid: true };
19
+ }
20
+ catch {
21
+ return { __invalid: true };
22
+ }
23
+ }
24
+ export async function handleAgentAuthorRoutes(req, method, pathname, projectRoot, host, res, json) {
25
+ const chat = pathname === "/api/agent-author/chat";
26
+ const undo = pathname === "/api/agent-author/undo";
27
+ if (!chat && !undo)
28
+ return false;
29
+ if (method !== "POST") {
30
+ json(res, 405, { code: "agent.invalid" });
31
+ return true;
32
+ }
33
+ const body = await readBody(req);
34
+ if (body.__tooLarge || body.__invalid) {
35
+ json(res, 400, { code: "agent.invalid" });
36
+ return true;
37
+ }
38
+ try {
39
+ if (undo) {
40
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId.trim() : "";
41
+ if (!sessionId) {
42
+ json(res, 400, { code: "agent.invalid" });
43
+ return true;
44
+ }
45
+ json(res, 200, { draft: undoAgentAuthorTurn(sessionId) });
46
+ return true;
47
+ }
48
+ const message = typeof body.message === "string" ? body.message : "";
49
+ const result = await runAgentAuthorTurn({
50
+ host,
51
+ projectRoot,
52
+ message,
53
+ ...(typeof body.sessionId === "string" ? { sessionId: body.sessionId } : {}),
54
+ draft: parseAgentAuthorDraft(body.draft),
55
+ });
56
+ json(res, 200, result);
57
+ }
58
+ catch (err) {
59
+ const code = err instanceof Error ? err.message : "agent.internal";
60
+ const status = code === "agent.session_not_found" ? 404 : code === "agent.conflict" ? 409 : 400;
61
+ json(res, status, { code: code.startsWith("agent.") ? code : "agent.internal" });
62
+ }
63
+ return true;
64
+ }
@@ -0,0 +1,5 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { AgentHostContext } from "../agents/hostContext.js";
3
+ type Json = (res: ServerResponse, status: number, body: unknown) => void;
4
+ export declare function handleAgentRoutes(req: IncomingMessage, method: string, pathname: string, host: AgentHostContext, projectRoot: string, res: ServerResponse, json: Json): Promise<boolean>;
5
+ export {};
@@ -0,0 +1,323 @@
1
+ import { AgentError, isAgentError } from "../agents/errors.js";
2
+ import { describeToolkit } from "../toolkit/index.js";
3
+ import { isSessionStatus } from "../agents/store.js";
4
+ import { BodyError, readJsonBody, writeFrame } from "../agents/ndjson.js";
5
+ const MAX_BODY_BYTES = 256 * 1024;
6
+ const CONSOLE_ACTOR = { source: "console" };
7
+ async function readBody(req) {
8
+ try {
9
+ return await readJsonBody(req, MAX_BODY_BYTES);
10
+ }
11
+ catch (error) {
12
+ if (!(error instanceof BodyError))
13
+ throw error;
14
+ throw new AgentError("agent.invalid", error.reason === "too_large" ? "Request body is too large" : error.reason === "not_json" ? "Request body is not JSON" : "Request body must be a JSON object");
15
+ }
16
+ }
17
+ function requireInt(value, name) {
18
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
19
+ throw new AgentError("agent.invalid", `${name} must be a non-negative integer`);
20
+ }
21
+ return value;
22
+ }
23
+ function requireText(value, name) {
24
+ if (typeof value !== "string" || value.trim().length === 0)
25
+ throw new AgentError("agent.invalid", `${name} is required`);
26
+ return value.trim();
27
+ }
28
+ function optionalText(value) {
29
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
30
+ }
31
+ function sessionQuery(url) {
32
+ const status = url.searchParams.get("status") ?? undefined;
33
+ if (status !== undefined && !isSessionStatus(status)) {
34
+ throw new AgentError("agent.invalid", "Unknown session status");
35
+ }
36
+ const limit = Number(url.searchParams.get("limit") ?? 25);
37
+ return {
38
+ limit: Number.isInteger(limit) && limit > 0 && limit <= 100 ? limit : 25,
39
+ ...(status && isSessionStatus(status) ? { status } : {}),
40
+ ...(url.searchParams.get("source") ? { source: url.searchParams.get("source") } : {}),
41
+ ...(url.searchParams.get("q")?.trim() ? { search: url.searchParams.get("q").trim() } : {}),
42
+ ...(url.searchParams.get("cursor") ? { cursor: url.searchParams.get("cursor") } : {}),
43
+ };
44
+ }
45
+ /** A session must belong to the agent named in the path, or the request is a 404. */
46
+ async function sessionFor(ctx, agentId, sessionId) {
47
+ const detail = await ctx.host.service.getSession(ctx.projectRoot, sessionId);
48
+ if (detail.session.agentId !== agentId)
49
+ throw new AgentError("agent.session_not_found");
50
+ return detail;
51
+ }
52
+ async function chat(ctx, agentId) {
53
+ const body = await readBody(ctx.req);
54
+ const message = requireText(body.message, "message");
55
+ const sessionId = optionalText(body.sessionId);
56
+ const wantsStream = body.stream === true || String(ctx.req.headers.accept ?? "").includes("application/x-ndjson");
57
+ const send = (onDelta, onSessionOpened) => ctx.host.chat.send({
58
+ projectRoot: ctx.projectRoot,
59
+ agentId,
60
+ text: message,
61
+ source: "console",
62
+ ...(sessionId ? { sessionId } : {}),
63
+ ...(onDelta ? { onDelta } : {}),
64
+ ...(onSessionOpened ? { onSessionOpened } : {}),
65
+ });
66
+ if (!wantsStream) {
67
+ ctx.json(ctx.res, 200, await send());
68
+ return;
69
+ }
70
+ ctx.res.writeHead(200, { "Content-Type": "application/x-ndjson; charset=utf-8", "Cache-Control": "no-store" });
71
+ let writes = Promise.resolve();
72
+ let finished = false;
73
+ let openedId = sessionId;
74
+ ctx.res.once("close", () => {
75
+ if (finished)
76
+ return;
77
+ if (openedId)
78
+ ctx.host.chat.stop(openedId);
79
+ else
80
+ ctx.host.chat.stopOpening(agentId);
81
+ });
82
+ try {
83
+ const detail = await send((text) => {
84
+ writes = writes.then(() => writeFrame(ctx.res, { type: "delta", text }));
85
+ }, (id) => {
86
+ openedId = id;
87
+ writes = writes.then(() => writeFrame(ctx.res, { type: "session", sessionId: id }));
88
+ });
89
+ finished = true;
90
+ await writes;
91
+ await writeFrame(ctx.res, { type: "done", session: detail });
92
+ }
93
+ catch (error) {
94
+ finished = true;
95
+ await writes.catch(() => undefined);
96
+ await writeFrame(ctx.res, {
97
+ type: "error",
98
+ code: isAgentError(error) ? error.code : "agent.internal",
99
+ ...(isAgentError(error) && error.message !== error.code ? { message: error.message } : {}),
100
+ });
101
+ }
102
+ finally {
103
+ if (!ctx.res.writableEnded && !ctx.res.destroyed)
104
+ ctx.res.end();
105
+ }
106
+ }
107
+ const routes = [
108
+ {
109
+ method: "GET",
110
+ pattern: /^\/api\/agents$/,
111
+ handle: async (ctx) => ctx.json(ctx.res, 200, { agents: await ctx.host.service.list(ctx.projectRoot) }),
112
+ },
113
+ {
114
+ method: "POST",
115
+ pattern: /^\/api\/agents$/,
116
+ handle: async (ctx) => {
117
+ const body = await readBody(ctx.req);
118
+ ctx.json(ctx.res, 201, await ctx.host.service.create(ctx.projectRoot, body.definition, CONSOLE_ACTOR));
119
+ },
120
+ },
121
+ {
122
+ method: "POST",
123
+ pattern: /^\/api\/agents\/validate$/,
124
+ handle: async (ctx) => {
125
+ const body = await readBody(ctx.req);
126
+ ctx.json(ctx.res, 200, await ctx.host.service.validate(ctx.projectRoot, body.definition));
127
+ },
128
+ },
129
+ {
130
+ method: "GET",
131
+ pattern: /^\/api\/agents\/stageflow-tools$/,
132
+ handle: async ({ res, json }) => json(res, 200, describeToolkit()),
133
+ },
134
+ {
135
+ method: "GET",
136
+ pattern: /^\/api\/agents\/([^/]+)\/channel$/,
137
+ handle: async (ctx) => {
138
+ const detail = await ctx.host.service.get(ctx.projectRoot, ctx.params[0]);
139
+ const connectionId = detail.definition?.channel?.connection_id ?? null;
140
+ // `listener` is absent for webhook providers and for agents that are disabled.
141
+ const listener = connectionId ? ctx.host.channelListeners?.status(connectionId) : undefined;
142
+ const failed = ctx.host.service.latestUndelivered(ctx.projectRoot, ctx.params[0]);
143
+ ctx.json(ctx.res, 200, {
144
+ connectionId,
145
+ ...(listener ? { listener } : {}),
146
+ ...(failed ? { lastUndelivered: { turnId: failed.turnId, sessionId: failed.sessionId, state: failed.state, code: failed.errorCode, message: failed.errorMessage, at: failed.updatedAt } } : {}),
147
+ });
148
+ },
149
+ },
150
+ {
151
+ method: "POST",
152
+ pattern: /^\/api\/agents\/([^/]+)\/sessions\/([^/]+)\/turns\/([^/]+)\/delivery\/(resend|delivered)$/,
153
+ handle: async (ctx) => {
154
+ const [agentId, sessionId, turnId, action] = ctx.params;
155
+ const session = await ctx.host.service.getSession(ctx.projectRoot, sessionId);
156
+ if (session.session.agentId !== agentId || !session.turns.some((turn) => turn.id === turnId))
157
+ throw new AgentError("agent.session_not_found");
158
+ if (action === "resend")
159
+ await ctx.host.delivery.resend(turnId);
160
+ else
161
+ ctx.host.delivery.markDelivered(turnId);
162
+ ctx.json(ctx.res, 200, await ctx.host.service.getSession(ctx.projectRoot, sessionId));
163
+ },
164
+ },
165
+ {
166
+ method: "GET",
167
+ pattern: /^\/api\/agents\/([^/]+)\/channel\/contact$/,
168
+ handle: async (ctx) => {
169
+ const detail = await ctx.host.service.get(ctx.projectRoot, ctx.params[0]);
170
+ const connectionId = detail.definition?.channel?.connection_id;
171
+ const found = connectionId ? await ctx.host.channelContact(connectionId) : undefined;
172
+ ctx.json(ctx.res, 200, { connectionId: connectionId ?? null, ...(found ?? {}) });
173
+ },
174
+ },
175
+ {
176
+ method: "GET",
177
+ pattern: /^\/api\/agents\/sessions$/,
178
+ handle: async (ctx) => ctx.json(ctx.res, 200, await ctx.host.service.listAllSessions(ctx.projectRoot, sessionQuery(ctx.url), ctx.url.searchParams.get("agent") || undefined)),
179
+ },
180
+ {
181
+ method: "GET",
182
+ pattern: /^\/api\/agents\/approvals$/,
183
+ handle: async (ctx) => ctx.json(ctx.res, 200, { approvals: await ctx.host.service.pendingApprovals(ctx.projectRoot) }),
184
+ },
185
+ {
186
+ method: "GET",
187
+ pattern: /^\/api\/agents\/([^/]+)$/,
188
+ handle: async (ctx) => ctx.json(ctx.res, 200, await ctx.host.service.get(ctx.projectRoot, ctx.params[0])),
189
+ },
190
+ {
191
+ method: "POST",
192
+ pattern: /^\/api\/agents\/([^/]+)\/save$/,
193
+ handle: async (ctx) => {
194
+ const body = await readBody(ctx.req);
195
+ ctx.json(ctx.res, 200, await ctx.host.service.save(ctx.projectRoot, ctx.params[0], {
196
+ baseRevision: requireInt(body.baseRevision, "baseRevision"),
197
+ definition: body.definition,
198
+ summary: optionalText(body.summary) ?? null,
199
+ }, CONSOLE_ACTOR));
200
+ },
201
+ },
202
+ {
203
+ method: "POST",
204
+ pattern: /^\/api\/agents\/([^/]+)\/(enable|disable)$/,
205
+ handle: async (ctx) => ctx.json(ctx.res, 200, await ctx.host.service.setEnabled(ctx.projectRoot, ctx.params[0], ctx.params[1] === "enable", CONSOLE_ACTOR)),
206
+ },
207
+ {
208
+ method: "POST",
209
+ pattern: /^\/api\/agents\/([^/]+)\/archive$/,
210
+ handle: async (ctx) => {
211
+ await ctx.host.service.archive(ctx.projectRoot, ctx.params[0]);
212
+ ctx.json(ctx.res, 200, { archived: true });
213
+ },
214
+ },
215
+ {
216
+ method: "GET",
217
+ pattern: /^\/api\/agents\/([^/]+)\/revisions$/,
218
+ handle: async (ctx) => ctx.json(ctx.res, 200, { revisions: await ctx.host.service.listRevisions(ctx.projectRoot, ctx.params[0]) }),
219
+ },
220
+ {
221
+ method: "GET",
222
+ pattern: /^\/api\/agents\/([^/]+)\/revisions\/(\d+)$/,
223
+ handle: async (ctx) => ctx.json(ctx.res, 200, await ctx.host.service.getRevision(ctx.projectRoot, ctx.params[0], Number(ctx.params[1]))),
224
+ },
225
+ {
226
+ method: "GET",
227
+ pattern: /^\/api\/agents\/([^/]+)\/revisions\/(\d+)\/diff\/(\d+)$/,
228
+ handle: async (ctx) => ctx.json(ctx.res, 200, {
229
+ lines: await ctx.host.service.diffRevisions(ctx.projectRoot, ctx.params[0], Number(ctx.params[1]), Number(ctx.params[2])),
230
+ }),
231
+ },
232
+ {
233
+ method: "POST",
234
+ pattern: /^\/api\/agents\/([^/]+)\/revisions\/(\d+)\/restore$/,
235
+ handle: async (ctx) => {
236
+ const body = await readBody(ctx.req);
237
+ ctx.json(ctx.res, 200, await ctx.host.service.restore(ctx.projectRoot, ctx.params[0], { revision: Number(ctx.params[1]), baseRevision: requireInt(body.baseRevision, "baseRevision") }, CONSOLE_ACTOR));
238
+ },
239
+ },
240
+ {
241
+ method: "GET",
242
+ pattern: /^\/api\/agents\/([^/]+)\/sessions$/,
243
+ handle: async (ctx) => ctx.json(ctx.res, 200, await ctx.host.service.listSessions(ctx.projectRoot, ctx.params[0], sessionQuery(ctx.url))),
244
+ },
245
+ {
246
+ method: "GET",
247
+ pattern: /^\/api\/agents\/([^/]+)\/sessions\/([^/]+)$/,
248
+ handle: async (ctx) => ctx.json(ctx.res, 200, await sessionFor(ctx, ctx.params[0], ctx.params[1])),
249
+ },
250
+ {
251
+ method: "GET",
252
+ pattern: /^\/api\/agents\/([^/]+)\/sessions\/([^/]+)\/otlp$/,
253
+ handle: async (ctx) => {
254
+ await sessionFor(ctx, ctx.params[0], ctx.params[1]);
255
+ ctx.res.setHeader("Content-Disposition", `attachment; filename="${ctx.params[1]}.otlp.json"`);
256
+ ctx.json(ctx.res, 200, ctx.host.service.exportSessionOtlp(ctx.projectRoot, ctx.params[1]));
257
+ },
258
+ },
259
+ {
260
+ method: "POST",
261
+ pattern: /^\/api\/agents\/([^/]+)\/sessions\/([^/]+)\/approval\/(approve|deny)$/,
262
+ handle: async (ctx) => {
263
+ await sessionFor(ctx, ctx.params[0], ctx.params[1]);
264
+ const approval = await ctx.host.service.decideApproval(ctx.projectRoot, ctx.params[1], ctx.params[2] === "approve" ? "approve" : "deny", { kind: "operator" });
265
+ ctx.json(ctx.res, 200, { approval, session: await sessionFor(ctx, ctx.params[0], ctx.params[1]) });
266
+ },
267
+ },
268
+ {
269
+ method: "POST",
270
+ pattern: /^\/api\/agents\/([^/]+)\/chat$/,
271
+ handle: async (ctx) => chat(ctx, ctx.params[0]),
272
+ },
273
+ {
274
+ method: "POST",
275
+ pattern: /^\/api\/agents\/([^/]+)\/chat\/stop$/,
276
+ handle: async (ctx) => {
277
+ const body = await readBody(ctx.req);
278
+ const sessionId = requireText(body.sessionId, "sessionId");
279
+ await sessionFor(ctx, ctx.params[0], sessionId);
280
+ ctx.json(ctx.res, 200, { stopped: ctx.host.chat.stop(sessionId) });
281
+ },
282
+ },
283
+ ];
284
+ export async function handleAgentRoutes(req, method, pathname, host, projectRoot, res, json) {
285
+ for (const route of routes) {
286
+ const match = route.pattern.exec(pathname);
287
+ if (!match)
288
+ continue;
289
+ if (route.method !== method)
290
+ continue;
291
+ try {
292
+ const params = match.slice(1).map((segment) => decodeURIComponent(segment));
293
+ await route.handle({ req, res, json, host, projectRoot, params, url: new URL(req.url ?? pathname, "http://agents") });
294
+ }
295
+ catch (error) {
296
+ if (res.headersSent) {
297
+ if (!res.writableEnded)
298
+ res.end();
299
+ return true;
300
+ }
301
+ if (error instanceof URIError) {
302
+ json(res, 400, { code: "agent.invalid", message: "Malformed path" });
303
+ }
304
+ else if (isAgentError(error)) {
305
+ json(res, error.httpStatus, {
306
+ code: error.code,
307
+ ...(error.message !== error.code ? { message: error.message } : {}),
308
+ ...(error.issues.length > 0 ? { issues: error.issues } : {}),
309
+ });
310
+ }
311
+ else {
312
+ throw error;
313
+ }
314
+ }
315
+ return true;
316
+ }
317
+ const known = routes.some((route) => route.pattern.test(pathname));
318
+ if (known) {
319
+ json(res, 405, { code: "agent.method_not_allowed" });
320
+ return true;
321
+ }
322
+ return false;
323
+ }
@@ -9,10 +9,14 @@ import type { StageBrowserSupport } from "../browser/browserHost.js";
9
9
  import { RunManager } from "../runtime/runManager.js";
10
10
  import { emailHostFor } from "../email/host.js";
11
11
  import { EmailTriggers } from "../email/triggers.js";
12
+ import { type ChannelService } from "../channels/service.js";
13
+ import { type MemoryHost } from "../memory/host.js";
12
14
  import { type RunChangeBus } from "../runtime/runChangeBus.js";
13
15
  import { type McpHttpHandler } from "../mcp/server.js";
14
16
  import { type HostConfig } from "../config/hostConfig.js";
15
17
  import { bootProviderConfig } from "../agent/bootProviderConfig.js";
18
+ import { type AgentHostContext } from "../agents/hostContext.js";
19
+ import type { ConversationBackend } from "../agents/sessions.js";
16
20
  export declare const DEFAULT_GC_INTERVAL_MS: number;
17
21
  export type StageflowHostOptions = {
18
22
  agent: StagePort;
@@ -32,6 +36,7 @@ export type StageflowHostOptions = {
32
36
  /** Browser support for the runs this Host executes; defaults to the local host with the live view relay. */
33
37
  browser?: StageBrowserSupport;
34
38
  emailTriggerQueue?: ConstructorParameters<typeof EmailTriggers>[0]["queue"];
39
+ agentConversationBackend?: ConversationBackend;
35
40
  };
36
41
  export type StageflowHostBootstrap = {
37
42
  a2a?: A2aHost;
@@ -59,6 +64,12 @@ export type StageflowHostBootstrap = {
59
64
  stopGithubPollSource: () => void;
60
65
  /** Stops the email-trigger IMAP/IDLE listeners started at boot. */
61
66
  stopEmailSource: () => void;
67
+ /** Connections page read model and background health checks. */
68
+ channels?: ChannelService;
69
+ /** Named markdown pages under `$STAGEFLOW_HOME/memories`. */
70
+ memories?: MemoryHost;
71
+ /** Shared agent store, chat controller, and conversation backend for HTTP/MCP. */
72
+ agentHost?: AgentHostContext;
62
73
  email?: ReturnType<typeof emailHostFor> & {
63
74
  triggers: EmailTriggers;
64
75
  stop: () => Promise<void>;