stageflow 0.36.0 → 0.40.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 (288) hide show
  1. package/README.md +61 -26
  2. package/dist/agent/claudeAdapter.js +21 -1
  3. package/dist/agent/claudeTools.d.ts +13 -0
  4. package/dist/agent/claudeTools.js +98 -0
  5. package/dist/agent/piAdapter.d.ts +9 -1
  6. package/dist/agent/piAdapter.js +59 -18
  7. package/dist/agent/piIsolatedMcp.js +14 -2
  8. package/dist/agent/piIsolatedMcpProbe.d.ts +5 -1
  9. package/dist/agent/piIsolatedMcpProbe.js +16 -30
  10. package/dist/agent/port.d.ts +5 -0
  11. package/dist/agent/sandboxPiTools.d.ts +99 -0
  12. package/dist/agent/sandboxPiTools.js +109 -0
  13. package/dist/agents/agentId.d.ts +8 -0
  14. package/dist/agents/agentId.js +27 -0
  15. package/dist/agents/approvalBroker.d.ts +16 -0
  16. package/dist/agents/approvalBroker.js +45 -0
  17. package/dist/agents/authorChat.d.ts +19 -0
  18. package/dist/agents/authorChat.js +63 -0
  19. package/dist/agents/chatTurn.d.ts +23 -0
  20. package/dist/agents/chatTurn.js +77 -0
  21. package/dist/agents/communications/agentmail.d.ts +19 -0
  22. package/dist/agents/communications/agentmail.js +46 -0
  23. package/dist/agents/communications/agentmailListener.d.ts +59 -0
  24. package/dist/agents/communications/agentmailListener.js +183 -0
  25. package/dist/agents/communications/allowlist.d.ts +10 -0
  26. package/dist/agents/communications/allowlist.js +36 -0
  27. package/dist/agents/communications/channelConnections.d.ts +3 -0
  28. package/dist/agents/communications/channelConnections.js +51 -0
  29. package/dist/agents/communications/delivery.d.ts +53 -0
  30. package/dist/agents/communications/delivery.js +142 -0
  31. package/dist/agents/communications/email.d.ts +26 -0
  32. package/dist/agents/communications/email.js +20 -0
  33. package/dist/agents/communications/identity.d.ts +13 -0
  34. package/dist/agents/communications/identity.js +18 -0
  35. package/dist/agents/communications/imessageListener.d.ts +41 -0
  36. package/dist/agents/communications/imessageListener.js +92 -0
  37. package/dist/agents/communications/inbound.d.ts +63 -0
  38. package/dist/agents/communications/inbound.js +143 -0
  39. package/dist/agents/communications/listener.d.ts +36 -0
  40. package/dist/agents/communications/listener.js +38 -0
  41. package/dist/agents/communications/photon.d.ts +19 -0
  42. package/dist/agents/communications/photon.js +38 -0
  43. package/dist/agents/communications/port.d.ts +27 -0
  44. package/dist/agents/communications/port.js +6 -0
  45. package/dist/agents/communications/slack.d.ts +28 -0
  46. package/dist/agents/communications/slack.js +65 -0
  47. package/dist/agents/communications/supervisor.d.ts +36 -0
  48. package/dist/agents/communications/supervisor.js +96 -0
  49. package/dist/agents/communications/telegram.d.ts +19 -0
  50. package/dist/agents/communications/telegram.js +52 -0
  51. package/dist/agents/definitionSummary.d.ts +13 -0
  52. package/dist/agents/definitionSummary.js +91 -0
  53. package/dist/agents/delegate.d.ts +3 -0
  54. package/dist/agents/delegate.js +8 -0
  55. package/dist/agents/errors.d.ts +10 -0
  56. package/dist/agents/errors.js +32 -0
  57. package/dist/agents/guardrails.d.ts +24 -0
  58. package/dist/agents/guardrails.js +39 -0
  59. package/dist/agents/historyFit.d.ts +10 -0
  60. package/dist/agents/historyFit.js +26 -0
  61. package/dist/agents/hostContext.d.ts +54 -0
  62. package/dist/agents/hostContext.js +156 -0
  63. package/dist/agents/ledgers.d.ts +29 -0
  64. package/dist/agents/ledgers.js +31 -0
  65. package/dist/agents/lineDiff.d.ts +12 -0
  66. package/dist/agents/lineDiff.js +46 -0
  67. package/dist/agents/ndjson.d.ts +9 -0
  68. package/dist/agents/ndjson.js +47 -0
  69. package/dist/agents/otlpExport.d.ts +9 -0
  70. package/dist/agents/otlpExport.js +58 -0
  71. package/dist/agents/otlpSink.d.ts +7 -0
  72. package/dist/agents/otlpSink.js +23 -0
  73. package/dist/agents/policy.d.ts +31 -0
  74. package/dist/agents/policy.js +84 -0
  75. package/dist/agents/projectRoot.d.ts +5 -0
  76. package/dist/agents/projectRoot.js +15 -0
  77. package/dist/agents/runtime/piRuntime.d.ts +52 -0
  78. package/dist/agents/runtime/piRuntime.js +474 -0
  79. package/dist/agents/runtime/types.d.ts +77 -0
  80. package/dist/agents/runtime/types.js +26 -0
  81. package/dist/agents/sandbox.d.ts +17 -0
  82. package/dist/agents/sandbox.js +10 -0
  83. package/dist/agents/scheduleSource.d.ts +32 -0
  84. package/dist/agents/scheduleSource.js +100 -0
  85. package/dist/agents/service.d.ts +298 -0
  86. package/dist/agents/service.js +1105 -0
  87. package/dist/agents/sessions.d.ts +38 -0
  88. package/dist/agents/sessions.js +321 -0
  89. package/dist/agents/store.d.ts +277 -0
  90. package/dist/agents/store.js +643 -0
  91. package/dist/agents/trace.d.ts +27 -0
  92. package/dist/agents/trace.js +29 -0
  93. package/dist/agents/turnLifecycle.d.ts +72 -0
  94. package/dist/agents/turnLifecycle.js +157 -0
  95. package/dist/agents/webChat/publicChat.d.ts +36 -0
  96. package/dist/agents/webChat/publicChat.js +290 -0
  97. package/dist/agents/webChat/rateLimit.d.ts +15 -0
  98. package/dist/agents/webChat/rateLimit.js +33 -0
  99. package/dist/agents/webChat/slug.d.ts +2 -0
  100. package/dist/agents/webChat/slug.js +9 -0
  101. package/dist/channels/providers/agentmail.js +14 -1
  102. package/dist/channels/providers/photonImessage.js +36 -0
  103. package/dist/channels/providers/slack.js +2 -1
  104. package/dist/channels/providers/telegram.js +2 -1
  105. package/dist/channels/types.d.ts +27 -0
  106. package/dist/cli/agentsCommand.d.ts +14 -0
  107. package/dist/cli/agentsCommand.js +291 -0
  108. package/dist/cli.js +9 -0
  109. package/dist/config/browseCatalog.js +4 -11
  110. package/dist/config/draftPackage.d.ts +1 -0
  111. package/dist/config/draftPackage.js +4 -0
  112. package/dist/config/hostConfig.js +9 -0
  113. package/dist/config/loadAgent.d.ts +266 -0
  114. package/dist/config/loadAgent.js +374 -0
  115. package/dist/config/loadPipeline.js +5 -2
  116. package/dist/config/loadTask.js +34 -1
  117. package/dist/config/mcpHostCatalog.d.ts +50 -0
  118. package/dist/config/mcpHostCatalog.js +405 -0
  119. package/dist/config/mcpOAuth.d.ts +37 -0
  120. package/dist/config/mcpOAuth.js +263 -0
  121. package/dist/config/mcpRedaction.d.ts +42 -0
  122. package/dist/config/mcpRedaction.js +314 -0
  123. package/dist/config/mcpToolCache.d.ts +20 -0
  124. package/dist/config/mcpToolCache.js +43 -0
  125. package/dist/config/mergePipelineIncludes.d.ts +2 -0
  126. package/dist/config/mergePipelineIncludes.js +25 -0
  127. package/dist/config/resolveStageMcpServers.d.ts +35 -3
  128. package/dist/config/resolveStageMcpServers.js +124 -9
  129. package/dist/config/validateCatalog.d.ts +1 -1
  130. package/dist/config/validateCatalog.js +22 -1
  131. package/dist/mcp/agentTools.d.ts +8 -0
  132. package/dist/mcp/agentTools.js +132 -0
  133. package/dist/mcp/catalogTools.js +17 -10
  134. package/dist/mcp/deps.d.ts +6 -0
  135. package/dist/mcp/pipelineAuthorSupport.d.ts +19 -0
  136. package/dist/mcp/pipelineAuthorSupport.js +10 -0
  137. package/dist/mcp/pipelineAuthorTools.d.ts +8 -0
  138. package/dist/mcp/pipelineAuthorTools.js +109 -0
  139. package/dist/mcp/server.js +5 -0
  140. package/dist/mcp/tools.js +4 -0
  141. package/dist/mcp/triggerTools.js +32 -0
  142. package/dist/memory/access.d.ts +36 -0
  143. package/dist/memory/access.js +116 -0
  144. package/dist/memory/agentIssues.d.ts +3 -0
  145. package/dist/memory/agentIssues.js +23 -0
  146. package/dist/memory/bashDeny.d.ts +6 -0
  147. package/dist/memory/bashDeny.js +35 -0
  148. package/dist/memory/host.d.ts +51 -0
  149. package/dist/memory/host.js +394 -0
  150. package/dist/memory/ids.d.ts +15 -0
  151. package/dist/memory/ids.js +31 -0
  152. package/dist/memory/worker.d.ts +1 -0
  153. package/dist/memory/worker.js +84 -0
  154. package/dist/models/catalog.d.ts +113 -0
  155. package/dist/models/catalog.js +238 -0
  156. package/dist/models/response.d.ts +29 -0
  157. package/dist/models/response.js +25 -0
  158. package/dist/operatorAgent/agentAuthorContext.d.ts +17 -0
  159. package/dist/operatorAgent/agentAuthorContext.js +32 -0
  160. package/dist/operatorAgent/fakeHost.js +2 -0
  161. package/dist/operatorAgent/host.js +6 -3
  162. package/dist/operatorAgent/index.d.ts +4 -0
  163. package/dist/operatorAgent/index.js +4 -0
  164. package/dist/operatorAgent/piModel.js +2 -0
  165. package/dist/operatorAgent/profiles/agentAuthor.d.ts +11 -0
  166. package/dist/operatorAgent/profiles/agentAuthor.js +192 -0
  167. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -0
  168. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +29 -0
  169. package/dist/operatorAgent/profiles/workshopAuthor.js +2 -2
  170. package/dist/operatorAgent/profiles/workshopAuthorTools.js +3 -3
  171. package/dist/operatorAgent/proposals.d.ts +1 -0
  172. package/dist/operatorAgent/proposals.js +23 -2
  173. package/dist/package-meta.d.ts +1 -1
  174. package/dist/package-meta.js +1 -1
  175. package/dist/preflight/pipelinePreflight.js +1 -0
  176. package/dist/runstore/createStore.d.ts +2 -0
  177. package/dist/runstore/createStore.js +8 -0
  178. package/dist/runstore/overviewSummary.d.ts +56 -0
  179. package/dist/runstore/overviewSummary.js +134 -0
  180. package/dist/runstore/port.d.ts +3 -0
  181. package/dist/runstore/sqlite/SqliteRunStore.d.ts +2 -0
  182. package/dist/runstore/sqlite/SqliteRunStore.js +18 -0
  183. package/dist/runstore/sqlite/migrations/013-agents.d.ts +12 -0
  184. package/dist/runstore/sqlite/migrations/013-agents.js +156 -0
  185. package/dist/runstore/sqlite/migrations/014-agent-deliveries.d.ts +11 -0
  186. package/dist/runstore/sqlite/migrations/014-agent-deliveries.js +32 -0
  187. package/dist/runstore/sqlite/migrations/index.js +4 -0
  188. package/dist/runtime/gitCheckoutCapability.d.ts +1 -0
  189. package/dist/runtime/gitCheckoutCapability.js +1 -1
  190. package/dist/runtime/gitWorktreeCheckout.d.ts +22 -0
  191. package/dist/runtime/gitWorktreeCheckout.js +120 -0
  192. package/dist/runtime/sandboxContainer.d.ts +62 -0
  193. package/dist/runtime/sandboxContainer.js +151 -0
  194. package/dist/runtime/sandboxDesk.d.ts +28 -0
  195. package/dist/runtime/sandboxDesk.js +242 -0
  196. package/dist/runtime/sandboxFiles.d.ts +20 -0
  197. package/dist/runtime/sandboxFiles.js +150 -0
  198. package/dist/runtime/stageAttemptBootstrap.js +15 -1
  199. package/dist/runtime/stageEnvironment.d.ts +2 -0
  200. package/dist/runtime/stageEnvironment.js +8 -1
  201. package/dist/runtime/stageProcessLauncher.js +23 -0
  202. package/dist/runtime/stageRoots.d.ts +9 -0
  203. package/dist/runtime/stageRoots.js +14 -0
  204. package/dist/runtime/stageRunner.d.ts +5 -0
  205. package/dist/runtime/stageRunner.js +38 -4
  206. package/dist/runtime/stageSandbox.d.ts +16 -0
  207. package/dist/runtime/stageSandbox.js +54 -0
  208. package/dist/runtime/stageSecrets.d.ts +6 -1
  209. package/dist/runtime/stageSecrets.js +34 -1
  210. package/dist/runtime/stageWorker.js +138 -105
  211. package/dist/runtime/workspaceBinding.d.ts +2 -1
  212. package/dist/runtime/workspaceBinding.js +20 -4
  213. package/dist/server/agentAuthorRoutes.d.ts +5 -0
  214. package/dist/server/agentAuthorRoutes.js +64 -0
  215. package/dist/server/agentRoutes.d.ts +5 -0
  216. package/dist/server/agentRoutes.js +323 -0
  217. package/dist/server/bootstrap.d.ts +8 -0
  218. package/dist/server/bootstrap.js +36 -10
  219. package/dist/server/controlToken.js +8 -0
  220. package/dist/server/http.d.ts +8 -1
  221. package/dist/server/http.js +172 -20
  222. package/dist/server/mcpHost.js +1 -0
  223. package/dist/server/memoryRoutes.d.ts +5 -0
  224. package/dist/server/memoryRoutes.js +146 -0
  225. package/dist/server/projectMcpRoutes.d.ts +1 -0
  226. package/dist/server/projectMcpRoutes.js +241 -3
  227. package/dist/toolkit/guard.d.ts +27 -0
  228. package/dist/toolkit/guard.js +98 -0
  229. package/dist/toolkit/index.d.ts +48 -0
  230. package/dist/toolkit/index.js +82 -0
  231. package/dist/toolkit/meta.d.ts +36 -0
  232. package/dist/toolkit/meta.js +117 -0
  233. package/dist/toolkit/profiles.d.ts +13 -0
  234. package/dist/toolkit/profiles.js +25 -0
  235. package/dist/toolkit/record.d.ts +15 -0
  236. package/dist/toolkit/record.js +17 -0
  237. package/dist/tools/memoryTools.d.ts +95 -0
  238. package/dist/tools/memoryTools.js +179 -0
  239. package/dist/types/pipeline.d.ts +3 -0
  240. package/dist/types/stage.d.ts +2 -0
  241. package/dist/types/task.d.ts +11 -1
  242. package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +9 -0
  243. package/dist/ui/assets/MarkdownText-D7tYaIFO.js +29 -0
  244. package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +6 -0
  245. package/dist/ui/assets/Tooltip-DrpoRRNi.js +1 -0
  246. package/dist/ui/assets/chat-BbLukq9W.js +5 -0
  247. package/dist/ui/assets/chat-zIZU_bP1.css +2 -0
  248. package/dist/ui/assets/jsx-runtime-iVoFAvQ_.js +1 -0
  249. package/dist/ui/assets/{index-Clv4sJpU.css → main-BFo79krC.css} +1 -1
  250. package/dist/ui/assets/main-CijYMeM0.js +259 -0
  251. package/dist/ui/chat.html +25 -0
  252. package/dist/ui/index.html +6 -2
  253. package/dist/workshop/modelSettings.d.ts +2 -2
  254. package/dist/workshop/modelSettings.js +5 -11
  255. package/examples/README.md +1 -0
  256. package/examples/channels-smoke/README.md +12 -1
  257. package/examples/github-release/README.md +1 -1
  258. package/examples/memory/README.md +101 -0
  259. package/examples/memory/desk-outage.task.yaml +15 -0
  260. package/examples/memory/desk-refund.task.yaml +15 -0
  261. package/examples/memory/desk-revisit-outage.task.yaml +11 -0
  262. package/examples/memory/desk-revisit.pipeline.yaml +123 -0
  263. package/examples/memory/desk.pipeline.yaml +192 -0
  264. package/examples/memory/file-card.pipeline.yaml +39 -0
  265. package/examples/memory/file-ledger.task.yaml +22 -0
  266. package/examples/memory/file-release.task.yaml +24 -0
  267. package/examples/memory/file-rich.pipeline.yaml +40 -0
  268. package/examples/memory/file-ticket.task.yaml +7 -0
  269. package/examples/memory/file-timeline.task.yaml +28 -0
  270. package/examples/memory/find-card.pipeline.yaml +46 -0
  271. package/examples/memory/find-ticket.task.yaml +7 -0
  272. package/examples/memory/missing.pipeline.yaml +16 -0
  273. package/examples/memory/probe-bash.task.yaml +3 -0
  274. package/examples/memory/recall.pipeline.yaml +24 -0
  275. package/examples/memory/recall.task.yaml +5 -0
  276. package/examples/memory/remember-ada.task.yaml +6 -0
  277. package/examples/memory/remember-grace.task.yaml +6 -0
  278. package/examples/memory/remember.pipeline.yaml +58 -0
  279. package/examples/memory/sandbox-memory-bash.pipeline.yaml +28 -0
  280. package/examples/memory/stageflow.yaml +12 -0
  281. package/examples/memory/update-card.pipeline.yaml +43 -0
  282. package/examples/memory/update-ledger.task.yaml +23 -0
  283. package/examples/memory/update-rich.pipeline.yaml +44 -0
  284. package/examples/memory/update-ticket.task.yaml +7 -0
  285. package/examples/ship-feature/README.md +15 -0
  286. package/examples/stage-mcp/README.md +1 -1
  287. package/package.json +2 -1
  288. package/dist/ui/assets/index-BOhqnPNg.js +0 -260
@@ -1,3 +1,4 @@
1
+ import { OVERVIEW_RANGES, buildOverview, isOverviewRange, overviewWindowMs, } from "../runstore/overviewSummary.js";
1
2
  import { readFile, access } from "node:fs/promises";
2
3
  import { createReadStream, statSync } from "node:fs";
3
4
  import path from "node:path";
@@ -25,7 +26,7 @@ import { createWorkshopBuild, focusUnboundWorkshopBuild, getWorkshopBuild, listW
25
26
  import { resolveAgentPort } from "../operatorAgent/index.js";
26
27
  import { listPipelinesForContext } from "../config/browseCatalog.js";
27
28
  import { catalogContextFromStageflow } from "../config/resolveCatalogContext.js";
28
- import { listModelsMultiProject, listPipelinesMultiProject, listTasksMultiProject, } from "../config/multiProjectCatalog.js";
29
+ import { listPipelinesMultiProject, listTasksMultiProject, } from "../config/multiProjectCatalog.js";
29
30
  import { catalogPathErrorBody, CatalogPathError, resolveCatalogRelativePath, resolveCatalogStartInput, resolveDefaultWriteRoot, resolveWritableCatalogRoot, } from "../config/catalogRelativePath.js";
30
31
  import { listExtensions } from "../config/listExtensions.js";
31
32
  import { listSkills } from "../config/listSkills.js";
@@ -36,6 +37,7 @@ import { validateCatalog } from "../config/validateCatalog.js";
36
37
  import { artifactMediaType, classifyArtifactContent, readRunArtifact, readRunArtifactBytes, } from "../mcp/readArtifact.js";
37
38
  import { getRunDiff, listCheckoutChanges, readCheckoutFileBytes, } from "../mcp/checkoutTools.js";
38
39
  import { readStageVerificationHistory } from "../runstore/verificationHistory.js";
40
+ import { sqliteConnectionFromStore } from "../runstore/createStore.js";
39
41
  import { BackupError, createBackup, resolveBackupDownloadPath, } from "../runstore/backup.js";
40
42
  import { RestoreError, resolveBackupNameForRestore, stageRestoreForBoot, } from "../runstore/restore.js";
41
43
  import { iterateExportNdjson } from "../cli/exportAllCommand.js";
@@ -51,7 +53,15 @@ import { isTaskFile } from "../runtime/taskInput.js";
51
53
  import { findTokenShapedField, tokenRejectedPayload, } from "../runtime/startPayload.js";
52
54
  import { parseAskOperatorAnswer } from "../tools/askOperator.js";
53
55
  import { handleEmailRoutes } from "./emailRoutes.js";
56
+ import { defaultAgentProjectRoot } from "../agents/projectRoot.js";
57
+ import { PublicWebChat } from "../agents/webChat/publicChat.js";
58
+ import { buildModelsResponse } from "../models/response.js";
59
+ import { createAgentHostContext } from "../agents/hostContext.js";
60
+ import { AgentScheduleSource, agentScheduleTickIntervalMsFromEnv, } from "../agents/scheduleSource.js";
61
+ import { handleAgentAuthorRoutes } from "./agentAuthorRoutes.js";
62
+ import { handleAgentRoutes } from "./agentRoutes.js";
54
63
  import { handleChannelRoutes } from "./channelRoutes.js";
64
+ import { handleMemoryRoutes } from "./memoryRoutes.js";
55
65
  import { bootstrapStageflowHost, } from "./bootstrap.js";
56
66
  import { assertAllowedHttpAccess, isTrustedLocalHttpRequest, resolveAllowedHosts, } from "./allowedHosts.js";
57
67
  import { enforceBearerAuth, loadControlTokens, requiredScopeFor, } from "./controlToken.js";
@@ -173,13 +183,38 @@ export function defaultUiDistDir() {
173
183
  return path.resolve(here, "../ui");
174
184
  }
175
185
  export function createOperatorRoutes(deps) {
176
- const { manager, store, cwd, agentDir, rootDir, providerAuthContext, uiDistDir, workshopOperatorHost, } = deps;
177
- const workshopChatRegistry = deps.workshopChatRegistry ??
178
- new WorkshopChatSessionRegistry(workshopOperatorHost ??
179
- resolveAgentPort({
186
+ const { manager, store, cwd, agentDir, rootDir, providerAuthContext, uiDistDir, workshopOperatorHost, agentConversationBackend, agentHost: injectedHost, } = deps;
187
+ const agentConnection = injectedHost ? undefined : sqliteConnectionFromStore(store);
188
+ const agentHost = injectedHost ??
189
+ (agentConnection
190
+ ? createAgentHostContext({
180
191
  cwd,
181
- projectRoot: cwd,
182
- }));
192
+ agentDir,
193
+ connection: agentConnection,
194
+ ...(agentConversationBackend ? { backend: agentConversationBackend } : {}),
195
+ })
196
+ : undefined);
197
+ const agentScheduleSource = agentHost
198
+ ? new AgentScheduleSource({
199
+ service: agentHost.service,
200
+ projectRoot: defaultAgentProjectRoot(cwd),
201
+ intervalMs: agentScheduleTickIntervalMsFromEnv(),
202
+ })
203
+ : undefined;
204
+ void agentScheduleSource?.start(async (event) => {
205
+ await agentHost.chat.send({
206
+ projectRoot: defaultAgentProjectRoot(cwd),
207
+ agentId: event.agentId,
208
+ text: event.message,
209
+ source: "schedule",
210
+ });
211
+ });
212
+ const operatorHost = workshopOperatorHost ??
213
+ resolveAgentPort({
214
+ cwd,
215
+ projectRoot: cwd,
216
+ });
217
+ const workshopChatRegistry = deps.workshopChatRegistry ?? new WorkshopChatSessionRegistry(operatorHost);
183
218
  const workshopSessionStoreRoot = () => resolveWorkshopSessionStoreRoot();
184
219
  const workshopBuildStoreRoot = () => resolveWorkshopBuildStoreRoot();
185
220
  const allowedHosts = deps.allowedHosts ?? resolveAllowedHosts();
@@ -215,6 +250,98 @@ export function createOperatorRoutes(deps) {
215
250
  try {
216
251
  if (await liveView.handle(req, res))
217
252
  return true;
253
+ if (pathname === "/api/agent-author/chat" || pathname === "/api/agent-author/undo") {
254
+ if (await handleAgentAuthorRoutes(req, method, pathname, cwd, operatorHost, res, json))
255
+ return true;
256
+ }
257
+ if (PublicWebChat.owns(pathname)) {
258
+ if (!agentHost) {
259
+ json(res, 503, { code: "web_chat.unavailable" });
260
+ return true;
261
+ }
262
+ return await agentHost.publicChat.handle(req, res, method, pathname);
263
+ }
264
+ if (method === "GET" && uiDistDir !== undefined && /^\/chat\/[^/]+$/.test(pathname)) {
265
+ const page = path.join(uiDistDir, "chat.html");
266
+ try {
267
+ const html = await readFile(page);
268
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store", "Referrer-Policy": "no-referrer", "X-Robots-Tag": "noindex" });
269
+ res.end(html);
270
+ return true;
271
+ }
272
+ catch {
273
+ json(res, 404, { error: "Chat page not built. Run npm run ui:build." });
274
+ return true;
275
+ }
276
+ }
277
+ const agentInbound = method === "POST" ? pathname.match(/^\/api\/agent-inbound\/([^/]+)$/) : null;
278
+ if (agentInbound) {
279
+ if (!agentHost) {
280
+ json(res, 503, { code: "agent.store_unavailable" });
281
+ return true;
282
+ }
283
+ const declared = Number(req.headers["content-length"] ?? 0);
284
+ if (declared > 1024 * 1024) {
285
+ json(res, 413, {});
286
+ return true;
287
+ }
288
+ const chunks = [];
289
+ let received = 0;
290
+ for await (const chunk of req) {
291
+ const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
292
+ received += buffer.length;
293
+ if (received > 1024 * 1024) {
294
+ json(res, 413, {});
295
+ req.destroy();
296
+ return true;
297
+ }
298
+ chunks.push(buffer);
299
+ }
300
+ const raw = Buffer.concat(chunks);
301
+ const headers = {};
302
+ for (const [name, value] of Object.entries(req.headers))
303
+ headers[name] = Array.isArray(value) ? value[0] : value;
304
+ const outcome = await agentHost.inbound.handle(decodeURIComponent(agentInbound[1]), headers, raw.toString("utf8"));
305
+ json(res, outcome.status, outcome.body ?? {});
306
+ return true;
307
+ }
308
+ if (pathname === "/api/agents" || pathname.startsWith("/api/agents/")) {
309
+ if (!agentHost) {
310
+ json(res, 503, { code: "agent.store_unavailable" });
311
+ return true;
312
+ }
313
+ // Default: the Host's own project. An explicit project_root must be a known catalog root
314
+ // (writable for writes); its filesystem path, not its wire id, is where agents/ lives.
315
+ let agentProjectRoot = defaultAgentProjectRoot(cwd);
316
+ const requestedRoot = url.searchParams.get("project_root");
317
+ if (requestedRoot !== null && path.resolve(requestedRoot) !== path.resolve(agentProjectRoot)) {
318
+ try {
319
+ const resolved = method === "GET"
320
+ ? await resolveCatalogStartInput({ store, bootCwd: cwd }, requestedRoot)
321
+ : await resolveWritableCatalogRoot({ store, bootCwd: cwd }, requestedRoot);
322
+ agentProjectRoot = resolved.wireRoot.path;
323
+ }
324
+ catch (err) {
325
+ if (err instanceof CatalogPathError) {
326
+ json(res, 400, catalogPathErrorBody(err));
327
+ return true;
328
+ }
329
+ throw err;
330
+ }
331
+ }
332
+ if (await handleAgentRoutes(req, method, pathname, agentHost, agentProjectRoot, res, json))
333
+ return true;
334
+ }
335
+ if (pathname === "/api/memories" || pathname.startsWith("/api/memories/")) {
336
+ if (!assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: method !== "GET" }))
337
+ return true;
338
+ if (!boot.memories) {
339
+ json(res, 503, { code: "memory.unavailable" });
340
+ return true;
341
+ }
342
+ if (await handleMemoryRoutes(req, method, pathname, boot.memories, res, json))
343
+ return true;
344
+ }
218
345
  if (pathname === "/api/connections" || pathname.startsWith("/api/connections/")) {
219
346
  if (!assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: method !== "GET" }))
220
347
  return true;
@@ -2730,27 +2857,26 @@ export function createOperatorRoutes(deps) {
2730
2857
  });
2731
2858
  return true;
2732
2859
  }
2733
- if (method === "GET" && pathname === "/api/models") {
2860
+ if ((method === "GET" && pathname === "/api/models") || (method === "POST" && pathname === "/api/models/refresh")) {
2861
+ if (method === "POST" && !assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: true }))
2862
+ return true;
2734
2863
  const filter = url.searchParams.get("project_root") ?? undefined;
2735
- const result = await listModelsMultiProject({
2864
+ const { response, used } = await buildModelsResponse({
2736
2865
  store,
2737
- bootCwd: cwd,
2866
+ cwd,
2738
2867
  projectRootFilter: filter,
2868
+ refresh: method === "POST",
2739
2869
  });
2740
- if (result.root_errors.some((e) => e.code === "unknown_project_root") &&
2741
- result.items.length === 0) {
2870
+ if (used.root_errors.some((e) => e.code === "unknown_project_root") &&
2871
+ used.items.length === 0) {
2742
2872
  json(res, 400, {
2743
- error: result.root_errors[0].message,
2873
+ error: used.root_errors[0].message,
2744
2874
  code: "unknown_project_root",
2745
- root_errors: result.root_errors,
2875
+ root_errors: used.root_errors,
2746
2876
  });
2747
2877
  return true;
2748
2878
  }
2749
- json(res, 200, {
2750
- models: result.models,
2751
- entries: result.items,
2752
- root_errors: result.root_errors,
2753
- });
2879
+ json(res, 200, response);
2754
2880
  return true;
2755
2881
  }
2756
2882
  if (await handleProviderRoutes(req, res, {
@@ -2763,6 +2889,7 @@ export function createOperatorRoutes(deps) {
2763
2889
  }
2764
2890
  if (await handleProjectMcpRoutes(req, res, {
2765
2891
  projectRoot: rootDir,
2892
+ readJsonBody,
2766
2893
  json,
2767
2894
  })) {
2768
2895
  return true;
@@ -2876,6 +3003,20 @@ export function createOperatorRoutes(deps) {
2876
3003
  await handleApiHealth(req, res, boot);
2877
3004
  return true;
2878
3005
  }
3006
+ if (method === "GET" && pathname === "/api/overview") {
3007
+ const rangeParam = url.searchParams.get("range") ?? "7d";
3008
+ if (!isOverviewRange(rangeParam)) {
3009
+ json(res, 400, {
3010
+ error: `range must be one of ${OVERVIEW_RANGES.join(", ")}`,
3011
+ code: "invalid_range",
3012
+ });
3013
+ return true;
3014
+ }
3015
+ const now = new Date();
3016
+ const since = new Date(now.getTime() - 2 * overviewWindowMs(rangeParam)).toISOString();
3017
+ json(res, 200, buildOverview(await store.listRunOverviewRows(since), rangeParam, now));
3018
+ return true;
3019
+ }
2879
3020
  if (method === "GET" && pathname === "/api/settings") {
2880
3021
  const health = manager.getHealth();
2881
3022
  const credential = getCredentialSourceSettings(cwd);
@@ -2972,7 +3113,14 @@ export function createOperatorRoutes(deps) {
2972
3113
  }
2973
3114
  }
2974
3115
  };
2975
- return Object.assign(handler, { dispose: () => liveView.dispose() });
3116
+ return Object.assign(handler, {
3117
+ dispose: async () => {
3118
+ await agentScheduleSource?.stop();
3119
+ if (!injectedHost)
3120
+ await agentHost?.stop();
3121
+ await liveView.dispose();
3122
+ },
3123
+ });
2976
3124
  }
2977
3125
  export async function startUiServer(options) {
2978
3126
  const host = options.host ?? "127.0.0.1";
@@ -3002,6 +3150,10 @@ export async function startUiServer(options) {
3002
3150
  ...(options.workshopOperatorHost
3003
3151
  ? { workshopOperatorHost: options.workshopOperatorHost }
3004
3152
  : {}),
3153
+ ...(boot.agentHost ? { agentHost: boot.agentHost } : {}),
3154
+ ...(options.agentConversationBackend
3155
+ ? { agentConversationBackend: options.agentConversationBackend }
3156
+ : {}),
3005
3157
  });
3006
3158
  const envelope = await createHttpHost({
3007
3159
  boot,
@@ -27,6 +27,7 @@ export async function startMcpServer(options) {
27
27
  allowedHosts,
28
28
  controlTokens,
29
29
  getShutdown: () => shutdown,
30
+ ...(boot.agentHost ? { agentHost: boot.agentHost } : {}),
30
31
  });
31
32
  const envelope = await createHttpHost({
32
33
  boot,
@@ -0,0 +1,5 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { type MemoryHost } from "../memory/host.js";
3
+ type Json = (res: ServerResponse, status: number, body: unknown) => void;
4
+ export declare function handleMemoryRoutes(req: IncomingMessage, method: string, pathname: string, host: MemoryHost, res: ServerResponse, json: Json): Promise<boolean>;
5
+ export {};
@@ -0,0 +1,146 @@
1
+ import { MemoryError } from "../memory/host.js";
2
+ const MAX_BODY_BYTES = 256 * 1024;
3
+ const STATUS = {
4
+ MEMORY_INVALID_ID: 400,
5
+ MEMORY_INVALID_DESCRIPTION: 400,
6
+ MEMORY_INVALID_KEY: 400,
7
+ MEMORY_TOO_LARGE: 400,
8
+ MEMORY_TOO_MANY_CARDS: 400,
9
+ MEMORY_NOT_ATTACHED: 403,
10
+ MEMORY_NOT_FOUND: 404,
11
+ MEMORY_NO_PREVIOUS: 404,
12
+ MEMORY_CARD_NOT_FOUND: 404,
13
+ MEMORY_EXISTS: 409,
14
+ MEMORY_CARD_EXISTS: 409,
15
+ MEMORY_MISMATCH: 422,
16
+ MEMORY_UNREADABLE: 500,
17
+ };
18
+ async function readBody(req) {
19
+ const chunks = [];
20
+ let size = 0;
21
+ for await (const chunk of req) {
22
+ const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
23
+ size += buffer.length;
24
+ if (size > MAX_BODY_BYTES)
25
+ throw new MemoryError("MEMORY_TOO_LARGE");
26
+ chunks.push(buffer);
27
+ }
28
+ if (chunks.length === 0)
29
+ return {};
30
+ try {
31
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
32
+ }
33
+ catch {
34
+ throw new MemoryError("MEMORY_INVALID_ID");
35
+ }
36
+ }
37
+ function memoryId(raw) {
38
+ try {
39
+ return decodeURIComponent(raw);
40
+ }
41
+ catch {
42
+ throw new MemoryError("MEMORY_INVALID_ID");
43
+ }
44
+ }
45
+ function queryKey(req) {
46
+ const raw = req.url ?? "";
47
+ const mark = raw.indexOf("?");
48
+ if (mark < 0)
49
+ return null;
50
+ const params = new URLSearchParams(raw.slice(mark + 1));
51
+ if (!params.has("key"))
52
+ return null;
53
+ return params.get("key") ?? "";
54
+ }
55
+ function textField(value, code) {
56
+ if (typeof value !== "string")
57
+ throw new MemoryError(code);
58
+ return value;
59
+ }
60
+ export async function handleMemoryRoutes(req, method, pathname, host, res, json) {
61
+ const previousCard = pathname.match(/^\/api\/memories\/([^/]+)\/cards\/previous$/);
62
+ const cards = pathname.match(/^\/api\/memories\/([^/]+)\/cards$/);
63
+ const previousCover = pathname.match(/^\/api\/memories\/([^/]+)\/previous$/);
64
+ const detail = pathname.match(/^\/api\/memories\/([^/]+)$/);
65
+ const collection = pathname === "/api/memories";
66
+ if (!collection && !detail && !cards && !previousCard && !previousCover)
67
+ return false;
68
+ if (cards && method === "POST")
69
+ return false;
70
+ try {
71
+ if (collection && method === "GET") {
72
+ json(res, 200, { memories: await host.list() });
73
+ return true;
74
+ }
75
+ if (collection && method === "POST") {
76
+ const body = await readBody(req);
77
+ if (typeof body.id !== "string" || typeof body.description !== "string") {
78
+ throw new MemoryError("MEMORY_INVALID_ID");
79
+ }
80
+ const created = await host.create({ id: body.id, description: body.description });
81
+ json(res, 201, created);
82
+ return true;
83
+ }
84
+ if (detail && method === "GET") {
85
+ const id = memoryId(detail[1]);
86
+ const meta = await host.readMeta(id);
87
+ const page = await host.read(id);
88
+ json(res, 200, { ...meta, body: page });
89
+ return true;
90
+ }
91
+ if (detail && method === "PATCH") {
92
+ const id = memoryId(detail[1]);
93
+ const payload = await readBody(req);
94
+ const body = textField(payload.body, "MEMORY_TOO_LARGE");
95
+ await host.replace(id, body);
96
+ const meta = await host.readMeta(id);
97
+ json(res, 200, { ...meta, body });
98
+ return true;
99
+ }
100
+ if (previousCover && method === "GET") {
101
+ const id = memoryId(previousCover[1]);
102
+ json(res, 200, { body: await host.readPreviousCover(id) });
103
+ return true;
104
+ }
105
+ if (cards && method === "GET") {
106
+ const id = memoryId(cards[1]);
107
+ const key = queryKey(req);
108
+ if (key === null) {
109
+ json(res, 200, { keys: await host.listCards(id) });
110
+ return true;
111
+ }
112
+ json(res, 200, { body: await host.readCard(id, key) });
113
+ return true;
114
+ }
115
+ if (cards && method === "PATCH") {
116
+ const id = memoryId(cards[1]);
117
+ const payload = await readBody(req);
118
+ const key = textField(payload.key, "MEMORY_INVALID_KEY");
119
+ const body = textField(payload.body, "MEMORY_TOO_LARGE");
120
+ await host.updateCard(id, key, body);
121
+ json(res, 200, { body });
122
+ return true;
123
+ }
124
+ if (cards && method === "DELETE") {
125
+ const id = memoryId(cards[1]);
126
+ const payload = await readBody(req);
127
+ await host.deleteCard(id, textField(payload.key, "MEMORY_INVALID_KEY"));
128
+ json(res, 200, { removed: true });
129
+ return true;
130
+ }
131
+ if (previousCard && method === "GET") {
132
+ const id = memoryId(previousCard[1]);
133
+ const key = queryKey(req);
134
+ if (key === null)
135
+ throw new MemoryError("MEMORY_INVALID_KEY");
136
+ json(res, 200, { body: await host.readPreviousCard(id, key) });
137
+ return true;
138
+ }
139
+ }
140
+ catch (error) {
141
+ const code = error instanceof MemoryError ? error.code : "MEMORY_UNREADABLE";
142
+ json(res, STATUS[code], { code });
143
+ return true;
144
+ }
145
+ return false;
146
+ }
@@ -1,6 +1,7 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  export type ProjectMcpRoutesCtx = {
3
3
  projectRoot: string;
4
+ readJsonBody: (req: IncomingMessage) => Promise<unknown>;
4
5
  json: (res: ServerResponse, status: number, body: unknown) => void;
5
6
  };
6
7
  export declare function handleProjectMcpRoutes(req: IncomingMessage, res: ServerResponse, ctx: ProjectMcpRoutesCtx): Promise<boolean>;