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
@@ -0,0 +1,132 @@
1
+ import { z } from "zod";
2
+ import { defaultAgentProjectRoot } from "../agents/projectRoot.js";
3
+ import { AGENT_ID_PATTERN } from "../config/loadAgent.js";
4
+ import { AgentError, isAgentError } from "../agents/errors.js";
5
+ import { SESSION_STATUSES } from "../agents/store.js";
6
+ import { textResult } from "./toolResults.js";
7
+ const agentId = z.string().regex(AGENT_ID_PATTERN);
8
+ const MCP_ACTOR = { source: "api", author: "mcp" };
9
+ function required(value, what) {
10
+ if (value === undefined)
11
+ throw new Error(`MCP agent tools require ${what} from host bootstrap`);
12
+ return value;
13
+ }
14
+ /** Run a service call and turn domain failures into tool errors with their code and field issues. */
15
+ async function call(work) {
16
+ try {
17
+ return textResult(await work());
18
+ }
19
+ catch (error) {
20
+ if (isAgentError(error)) {
21
+ return textResult({ code: error.code, message: error.message, ...(error.issues.length ? { issues: error.issues } : {}) }, true);
22
+ }
23
+ throw error;
24
+ }
25
+ }
26
+ /**
27
+ * Agent management for operators driving Stageflow over MCP. A running agent reaches these
28
+ * only through the Stageflow tool groups it is granted (`agents.*`); the guard in
29
+ * `toolkit/guard.ts` stops it acting on its own sessions.
30
+ */
31
+ export function registerAgentTools(server, deps) {
32
+ const service = () => required(deps.agentService, "agentService");
33
+ const chat = () => required(deps.agentChat, "agentChat");
34
+ const root = () => defaultAgentProjectRoot(deps.cwd);
35
+ /** A session must belong to the agent named in the call, or it is not found (as in the HTTP routes). */
36
+ const sessionFor = async (id, sessionId) => {
37
+ const detail = await service().getSession(root(), sessionId);
38
+ if (detail.session.agentId !== id)
39
+ throw new AgentError("agent.session_not_found");
40
+ return detail;
41
+ };
42
+ server.registerTool("list_agents", { description: "List project agents (agents/*.agent.yaml) with status and session stats.", inputSchema: z.object({}) }, () => call(async () => ({ agents: await service().list(root()) })));
43
+ server.registerTool("get_agent", {
44
+ description: "Get one agent: summary, full definition, canonical YAML, and warnings.",
45
+ inputSchema: z.object({ id: agentId }),
46
+ }, ({ id }) => call(() => service().get(root(), id)));
47
+ server.registerTool("validate_agent", {
48
+ description: "Validate an agent definition without saving. Returns field issues, warnings, and canonical YAML.",
49
+ inputSchema: z.object({ definition: z.record(z.string(), z.unknown()) }),
50
+ }, ({ definition }) => call(() => service().validate(root(), definition)));
51
+ server.registerTool("create_agent", {
52
+ description: "Create an agent. It always starts disabled; enable it with enable_agent.",
53
+ inputSchema: z.object({ definition: z.record(z.string(), z.unknown()) }),
54
+ }, ({ definition }) => call(() => service().create(root(), definition, MCP_ACTOR)));
55
+ server.registerTool("save_agent", {
56
+ description: "Save a full agent definition as a new revision. baseRevision must be the current revision (optimistic lock).",
57
+ inputSchema: z.object({
58
+ id: agentId,
59
+ baseRevision: z.number().int().nonnegative(),
60
+ definition: z.record(z.string(), z.unknown()),
61
+ summary: z.string().max(200).optional(),
62
+ }),
63
+ }, ({ id, baseRevision, definition, summary }) => call(() => service().save(root(), id, { baseRevision, definition, summary: summary ?? null }, MCP_ACTOR)));
64
+ server.registerTool("enable_agent", { description: "Enable an agent for new sessions and turns.", inputSchema: z.object({ id: agentId }) }, ({ id }) => call(() => service().setEnabled(root(), id, true, MCP_ACTOR)));
65
+ server.registerTool("disable_agent", {
66
+ description: "Disable an agent. New sessions and turns are refused; a running turn finishes.",
67
+ inputSchema: z.object({ id: agentId }),
68
+ }, ({ id }) => call(() => service().setEnabled(root(), id, false, MCP_ACTOR)));
69
+ server.registerTool("list_agent_revisions", { description: "List an agent's revisions, newest first.", inputSchema: z.object({ id: agentId }) }, ({ id }) => call(async () => ({ revisions: await service().listRevisions(root(), id) })));
70
+ server.registerTool("restore_agent_revision", {
71
+ description: "Save an earlier revision's definition as the newest revision.",
72
+ inputSchema: z.object({
73
+ id: agentId,
74
+ revision: z.number().int().positive(),
75
+ baseRevision: z.number().int().nonnegative(),
76
+ }),
77
+ }, ({ id, revision, baseRevision }) => call(() => service().restore(root(), id, { revision, baseRevision }, MCP_ACTOR)));
78
+ server.registerTool("agent_chat", {
79
+ description: "Send one message to an agent and wait for its reply. Omit sessionId to start a new session.",
80
+ inputSchema: z.object({ id: agentId, message: z.string().min(1), sessionId: z.string().optional() }),
81
+ }, ({ id, message, sessionId }) => call(() => chat().send({
82
+ projectRoot: root(),
83
+ agentId: id,
84
+ text: message,
85
+ source: "mcp",
86
+ ...(sessionId ? { sessionId } : {}),
87
+ })));
88
+ server.registerTool("get_agent_session", {
89
+ description: "Fetch a session of an agent: transcript, turns, approvals.",
90
+ inputSchema: z.object({ id: agentId, sessionId: z.string() }),
91
+ }, ({ id, sessionId }) => call(() => sessionFor(id, sessionId)));
92
+ server.registerTool("list_agent_sessions", {
93
+ description: "List an agent's sessions, newest first, with turn counts, tokens and cost.",
94
+ inputSchema: z.object({
95
+ id: agentId,
96
+ status: z.enum(SESSION_STATUSES).optional(),
97
+ search: z.string().optional(),
98
+ limit: z.number().int().min(1).max(100).optional(),
99
+ cursor: z.string().optional(),
100
+ }),
101
+ }, ({ id, status, search, limit, cursor }) => call(() => service().listSessions(root(), id, {
102
+ limit: limit ?? 25,
103
+ ...(status ? { status } : {}),
104
+ ...(search ? { search } : {}),
105
+ ...(cursor ? { cursor } : {}),
106
+ })));
107
+ server.registerTool("stop_agent_session", {
108
+ description: "Stop the turn currently running in a session. The session stays open.",
109
+ inputSchema: z.object({ id: agentId, sessionId: z.string() }),
110
+ }, ({ id, sessionId }) => call(async () => {
111
+ await sessionFor(id, sessionId);
112
+ return { stopped: chat().stop(sessionId) };
113
+ }));
114
+ server.registerTool("archive_agent", {
115
+ description: "Archive an agent: its definition leaves the project and new sessions are refused. Past sessions are kept.",
116
+ inputSchema: z.object({ id: agentId }),
117
+ }, ({ id }) => call(async () => { await service().archive(root(), id); return { archived: true }; }));
118
+ server.registerTool("approve_agent_action", {
119
+ description: "Approve the pending ask-first action on a session of an agent, as an operator.",
120
+ inputSchema: z.object({ id: agentId, sessionId: z.string() }),
121
+ }, ({ id, sessionId }) => call(async () => {
122
+ await sessionFor(id, sessionId);
123
+ return service().decideApproval(root(), sessionId, "approve", { kind: "operator" });
124
+ }));
125
+ server.registerTool("deny_agent_action", {
126
+ description: "Deny the pending ask-first action on a session of an agent. The agent is told it was refused.",
127
+ inputSchema: z.object({ id: agentId, sessionId: z.string() }),
128
+ }, ({ id, sessionId }) => call(async () => {
129
+ await sessionFor(id, sessionId);
130
+ return service().decideApproval(root(), sessionId, "deny", { kind: "operator" });
131
+ }));
132
+ }
@@ -1,6 +1,7 @@
1
+ import { buildModelsResponse } from "../models/response.js";
1
2
  import { z } from "zod";
2
3
  import { catalogPathErrorBody, CatalogPathError, resolveCatalogRelativePath, resolveCatalogStartInput, selectCatalogRootForStart, } from "../config/catalogRelativePath.js";
3
- import { listModelsMultiProject, listPipelinesMultiProject, listTasksMultiProject, } from "../config/multiProjectCatalog.js";
4
+ import { listPipelinesMultiProject, listTasksMultiProject, } from "../config/multiProjectCatalog.js";
4
5
  import { resolveCatalogRoots } from "../config/resolveCatalogRoots.js";
5
6
  import { describePipeline } from "../config/describePipeline.js";
6
7
  import { loadPipeline } from "../config/loadPipeline.js";
@@ -63,6 +64,7 @@ const inlinePipelineSchema = z
63
64
  model: z.unknown().optional(),
64
65
  schemas: z.unknown().optional(),
65
66
  requires: z.unknown().optional(),
67
+ channels: z.unknown().optional(),
66
68
  })
67
69
  .strict();
68
70
  const startRunSchema = z
@@ -232,14 +234,14 @@ export function registerCatalogTools(server, deps) {
232
234
  });
233
235
  });
234
236
  server.registerTool("list_models", {
235
- description: "List catalog model ids across every project this host knows about (same multi-root source as GET /api/models). Optional project_root filter narrows.",
237
+ description: "List model ids from the providers connected on this host, plus ids pipelines use (same source as GET /api/models). Returns models, providers (grouped), the default model and unlisted ids. Optional project_root filter narrows the pipeline-used ids.",
236
238
  inputSchema: z.object({
237
239
  project_root: z.string().optional(),
238
240
  }),
239
241
  }, async ({ project_root }) => {
240
- const result = await listModelsMultiProject({
242
+ const { response, used: result } = await buildModelsResponse({
241
243
  store,
242
- bootCwd: cwd,
244
+ cwd,
243
245
  projectRootFilter: project_root,
244
246
  });
245
247
  if (result.root_errors.some((e) => e.code === "unknown_project_root") &&
@@ -250,11 +252,7 @@ export function registerCatalogTools(server, deps) {
250
252
  root_errors: result.root_errors,
251
253
  }, true);
252
254
  }
253
- return textResult({
254
- models: result.models,
255
- entries: result.items,
256
- root_errors: result.root_errors,
257
- });
255
+ return textResult(response);
258
256
  });
259
257
  server.registerTool("list_runs", {
260
258
  description: "List known pipeline runs. Optional filters: status, since (ISO created_at lower bound), pipeline (id or path), caller_id.",
@@ -1,5 +1,8 @@
1
1
  import type { BootProviderResult } from "../agent/bootProviderConfig.js";
2
2
  import type { ProviderAuthContext } from "../agent/providerAuth.js";
3
+ import type { AgentChatController } from "../agents/chatTurn.js";
4
+ import type { ConversationBackend } from "../agents/sessions.js";
5
+ import type { AgentService } from "../agents/service.js";
3
6
  import type { RunStore } from "../runstore/port.js";
4
7
  import type { RunChangeBus } from "../runtime/runChangeBus.js";
5
8
  import type { RunManager } from "../runtime/runManager.js";
@@ -11,6 +14,9 @@ export type McpToolDeps = {
11
14
  providerAuthContext?: ProviderAuthContext;
12
15
  projectRoot?: string;
13
16
  providerBoot?: BootProviderResult;
17
+ agentService?: AgentService;
18
+ agentChat?: AgentChatController;
19
+ agentConversationBackend?: ConversationBackend;
14
20
  };
15
21
  export type McpHttpDeps = McpToolDeps & {
16
22
  runChangeBus: RunChangeBus;
@@ -0,0 +1,19 @@
1
+ import { loadDraftPackage, validateDraftPackage, type DraftPackage } from "../config/draftPackage.js";
2
+ export { loadDraftPackage, validateDraftPackage, type DraftPackage };
3
+ export type PublishFacadeResult = {
4
+ ok: true;
5
+ pipelinePath: string;
6
+ stagePaths: string[];
7
+ taskPath?: string;
8
+ } | {
9
+ ok: false;
10
+ error: string;
11
+ status: number;
12
+ };
13
+ /** Write a package the way the Workshop's save does, flattening its result shape. */
14
+ export declare function publishDraftPackageFacade(projectRoot: string, input: {
15
+ directory: string;
16
+ draft: DraftPackage;
17
+ pipelineFilename: string;
18
+ allowInvalid?: boolean;
19
+ }, mode: "create" | "overwrite"): Promise<PublishFacadeResult>;
@@ -0,0 +1,10 @@
1
+ import { loadDraftPackage, validateDraftPackage } from "../config/draftPackage.js";
2
+ import { publishDraftPackage } from "../config/publishDraftPackage.js";
3
+ export { loadDraftPackage, validateDraftPackage };
4
+ /** Write a package the way the Workshop's save does, flattening its result shape. */
5
+ export async function publishDraftPackageFacade(projectRoot, input, mode) {
6
+ const { write } = await publishDraftPackage(projectRoot, input, mode);
7
+ if (!write.ok)
8
+ return { ok: false, error: write.error, status: write.status };
9
+ return { ok: true, pipelinePath: write.pipelinePath, stagePaths: write.stagePaths, ...(write.taskPath !== undefined ? { taskPath: write.taskPath } : {}) };
10
+ }
@@ -0,0 +1,8 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { McpToolDeps } from "./deps.js";
3
+ /**
4
+ * Author pipelines and tasks straight on disk. Each mutation loads the package,
5
+ * applies the same builder the Workshop uses, and writes it back through the
6
+ * Workshop's publish path (validate first, nothing written when invalid).
7
+ */
8
+ export declare function registerPipelineAuthorTools(server: McpServer, deps: McpToolDeps): void;
@@ -0,0 +1,109 @@
1
+ import path from "node:path";
2
+ import { z } from "zod";
3
+ import { loadDraftPackage, publishDraftPackageFacade, validateDraftPackage } from "./pipelineAuthorSupport.js";
4
+ import { buildCreatePipelineDraft, buildCreateStageDraft, buildCreateTaskDraft, buildEditPipelineDraft, buildEditStageDraft, buildEditTaskDraft, } from "../operatorAgent/profiles/workshopAuthorTools.js";
5
+ import { textResult } from "./toolResults.js";
6
+ const pipelinePath = z
7
+ .string()
8
+ .describe("Pipeline YAML path relative to the project root, e.g. workshop/release/pipeline.yaml");
9
+ const record = z.record(z.string(), z.unknown());
10
+ const allowInvalid = z
11
+ .boolean()
12
+ .optional()
13
+ .describe("Write even when validation fails, so a pipeline can be built up step by step. Default false.");
14
+ /**
15
+ * Author pipelines and tasks straight on disk. Each mutation loads the package,
16
+ * applies the same builder the Workshop uses, and writes it back through the
17
+ * Workshop's publish path (validate first, nothing written when invalid).
18
+ */
19
+ export function registerPipelineAuthorTools(server, deps) {
20
+ const root = () => deps.projectRoot ?? deps.cwd;
21
+ async function mutate(relPath, mode, build, options = {}) {
22
+ const projectRoot = root();
23
+ let draft = { pipeline: { id: "", stages: [] } };
24
+ if (mode === "overwrite") {
25
+ const loaded = await loadDraftPackage(projectRoot, relPath, options.taskPath ? { taskPath: options.taskPath } : {});
26
+ if (!loaded.ok)
27
+ return textResult({ error: loaded.error, status: loaded.status }, true);
28
+ draft = loaded.draft;
29
+ }
30
+ const built = build(draft);
31
+ if (!built.ok)
32
+ return textResult({ error: built.error }, true);
33
+ const directory = path.posix.dirname(relPath.replace(/\\/g, "/")) || ".";
34
+ const published = await publishDraftPackageFacade(projectRoot, {
35
+ directory,
36
+ draft: built.nextDraft,
37
+ pipelineFilename: path.posix.basename(relPath),
38
+ ...(options.allowInvalid ? { allowInvalid: true } : {}),
39
+ }, mode);
40
+ if (!published.ok)
41
+ return textResult({ error: published.error, status: published.status }, true);
42
+ return textResult({ pipelinePath: published.pipelinePath, stagePaths: published.stagePaths, taskPath: published.taskPath ?? null });
43
+ }
44
+ server.registerTool("read_pipeline", {
45
+ description: "Read a pipeline package from disk (pipeline, stage files, optional task) with its validation result. Use before editing.",
46
+ inputSchema: z.object({ path: pipelinePath, task_path: z.string().optional().describe("Task file to include, relative to the project root") }),
47
+ }, async ({ path: relPath, task_path }) => {
48
+ const loaded = await loadDraftPackage(root(), relPath, task_path ? { taskPath: task_path } : {});
49
+ if (!loaded.ok)
50
+ return textResult({ error: loaded.error, status: loaded.status }, true);
51
+ const validation = await validateDraftPackage(loaded.draft, { cwd: deps.cwd, projectRoot: root() });
52
+ return textResult({ draft: loaded.draft, destination: loaded.destination, validation });
53
+ });
54
+ server.registerTool("create_pipeline", {
55
+ description: "Create a new pipeline package on disk (id, optional model/stages). Fails if it already exists; use edit_pipeline instead. A pipeline needs at least one stage to validate, so add stages or pass allow_invalid and then create_stage.",
56
+ inputSchema: z.object({
57
+ directory: z.string().describe("Catalog folder relative to the project root, e.g. workshop/release. The file is written as <id>.pipeline.yaml"),
58
+ id: z.string(),
59
+ model: z.string().optional(),
60
+ agent: z.unknown().optional(),
61
+ stages: z.array(record).optional(),
62
+ pipeline: record.optional().describe("Whole pipeline object, instead of id/model/stages"),
63
+ allow_invalid: allowInvalid,
64
+ }),
65
+ }, ({ directory, allow_invalid, ...args }) => {
66
+ const folder = directory.replace(/[\\/]+$/, "");
67
+ const file = `${typeof args.id === "string" ? args.id : ""}.pipeline.yaml`;
68
+ return mutate(folder === "" || folder === "." ? file : `${folder}/${file}`, "create", (draft) => buildCreatePipelineDraft(draft, args), { allowInvalid: allow_invalid });
69
+ });
70
+ server.registerTool("edit_pipeline", {
71
+ description: "Edit pipeline fields on disk (id, model, agent, stages wiring, schemas, requires). Pass the fields to change under patch.",
72
+ inputSchema: z.object({ path: pipelinePath, patch: record, allow_invalid: allowInvalid }),
73
+ }, ({ path: relPath, patch, allow_invalid }) => mutate(relPath, "overwrite", (draft) => buildEditPipelineDraft(draft, { patch }), { allowInvalid: allow_invalid }));
74
+ server.registerTool("create_stage", {
75
+ description: "Add a file-backed stage to a pipeline on disk (uses: ./id.yaml plus the stage body with io).",
76
+ inputSchema: z.object({
77
+ path: pipelinePath,
78
+ id: z.string().optional(),
79
+ summary: z.string().optional(),
80
+ system_prompt: z.string().optional(),
81
+ model: z.string().optional(),
82
+ entry: z.boolean().optional(),
83
+ route: z.array(record).optional(),
84
+ io: record.optional(),
85
+ body: record.optional().describe("Extra stage body fields"),
86
+ allow_invalid: allowInvalid,
87
+ }),
88
+ }, ({ path: relPath, allow_invalid, ...args }) => mutate(relPath, "overwrite", (draft) => buildCreateStageDraft(draft, args), { allowInvalid: allow_invalid }));
89
+ server.registerTool("edit_stage", {
90
+ description: "Edit an existing stage on disk by id: body fields and/or its pipeline entry (route, entry, uses).",
91
+ inputSchema: z.looseObject({ path: pipelinePath, id: z.string(), allow_invalid: allowInvalid }),
92
+ }, ({ path: relPath, allow_invalid, ...args }) => mutate(relPath, "overwrite", (draft) => buildEditStageDraft(draft, args), { allowInvalid: allow_invalid }));
93
+ server.registerTool("create_task", {
94
+ description: "Create or replace the task beside a pipeline on disk (id and goal, optional body).",
95
+ inputSchema: z.object({ path: pipelinePath, id: z.string().optional(), goal: z.string().optional(), filename: z.string().optional(), body: record.optional(), allow_invalid: allowInvalid }),
96
+ }, ({ path: relPath, allow_invalid, ...args }) => mutate(relPath, "overwrite", (draft) => buildCreateTaskDraft(draft, args), { allowInvalid: allow_invalid }));
97
+ server.registerTool("edit_task", {
98
+ description: "Edit the task of a pipeline on disk (id, goal, body fields).",
99
+ inputSchema: z.object({
100
+ path: pipelinePath,
101
+ task_path: z.string().describe("Task file relative to the project root"),
102
+ id: z.string().optional(),
103
+ goal: z.string().optional(),
104
+ patch: record.optional(),
105
+ body: record.optional(),
106
+ allow_invalid: allowInvalid,
107
+ }),
108
+ }, ({ path: relPath, task_path, allow_invalid, ...args }) => mutate(relPath, "overwrite", (draft) => buildEditTaskDraft(draft, args), { taskPath: task_path, allowInvalid: allow_invalid }));
109
+ }
@@ -44,6 +44,11 @@ function toolDepsFrom(deps) {
44
44
  : {}),
45
45
  ...(deps.projectRoot !== undefined ? { projectRoot: deps.projectRoot } : {}),
46
46
  ...(deps.providerBoot !== undefined ? { providerBoot: deps.providerBoot } : {}),
47
+ ...(deps.agentService !== undefined ? { agentService: deps.agentService } : {}),
48
+ ...(deps.agentChat !== undefined ? { agentChat: deps.agentChat } : {}),
49
+ ...(deps.agentConversationBackend !== undefined
50
+ ? { agentConversationBackend: deps.agentConversationBackend }
51
+ : {}),
47
52
  };
48
53
  }
49
54
  function attachBusNotifications(server, bus) {
package/dist/mcp/tools.js CHANGED
@@ -3,10 +3,12 @@ import { registerCatalogTools } from "./catalogTools.js";
3
3
  import { registerCheckoutTools } from "./checkoutTools.js";
4
4
  import { registerControlTools } from "./controlTools.js";
5
5
  import { registerProjectMcpTools } from "./projectMcpTools.js";
6
+ import { registerPipelineAuthorTools } from "./pipelineAuthorTools.js";
6
7
  import { registerProviderTools } from "./providerTools.js";
7
8
  import { registerTriggerTools } from "./triggerTools.js";
8
9
  import { textResult } from "./toolResults.js";
9
10
  import { DEFAULT_TIMEOUT_MS, waitRun } from "./waitRun.js";
11
+ import { registerAgentTools } from "./agentTools.js";
10
12
  import { registerGetStartedTool } from "./getStartedTool.js";
11
13
  export function registerMcpTools(server, deps) {
12
14
  const { store } = deps;
@@ -17,6 +19,8 @@ export function registerMcpTools(server, deps) {
17
19
  registerProviderTools(server, deps);
18
20
  registerProjectMcpTools(server, deps);
19
21
  registerTriggerTools(server, deps);
22
+ registerPipelineAuthorTools(server, deps);
23
+ registerAgentTools(server, deps);
20
24
  server.registerTool("wait_run", {
21
25
  description: "Long-poll until a run is waiting for HITL input and/or terminal (succeeded/failed), or until timeout_ms. Default until=any wakes on waiting OR terminal. Abort cancels only this wait (not the run). Optional notifications/progress when the client supplies _meta.progressToken. Compose: wait_run → answer_gate → wait_run.",
22
26
  inputSchema: z.object({
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { CatalogPathError, catalogPathErrorBody, resolveWritableCatalogRoot, } from "../config/catalogRelativePath.js";
3
3
  import { createTrigger, parseCreateTriggerBody } from "../config/createTrigger.js";
4
+ import { parsePatchTriggerBody, parsePutTriggerBody, updateTriggerDefinition, updateTriggerEnabled } from "../config/updateTrigger.js";
4
5
  import { loadCatalogTriggers } from "../config/triggerCatalog.js";
5
6
  import { writeAudit } from "../logging/audit.js";
6
7
  import { logger as rootLogger } from "../logging/logger.js";
@@ -167,4 +168,35 @@ export function registerTriggerTools(server, deps) {
167
168
  });
168
169
  return textResult(result.trigger);
169
170
  });
171
+ server.registerTool("update_trigger", {
172
+ description: "Replace a trigger's definition (pipeline, task, kind, schedule or event, enabled). Same validation and write path as PUT /api/triggers/:id; the id cannot change.",
173
+ inputSchema: z.object({
174
+ id: z.string(),
175
+ project_root: z.string().optional(),
176
+ pipeline: z.string(),
177
+ task: z.string().optional(),
178
+ kind: z.enum(["manual", "schedule", "event"]),
179
+ schedule: triggerScheduleSchema.optional(),
180
+ event: triggerEventSchema.optional(),
181
+ enabled: z.boolean().optional(),
182
+ }),
183
+ }, async ({ id, project_root, ...body }) => {
184
+ const parsed = parsePutTriggerBody(body, id);
185
+ if ("ok" in parsed)
186
+ return textResult({ error: parsed.error, status: parsed.status }, true);
187
+ const result = await updateTriggerDefinition(cwd, store, id, parsed, { projectRootFilter: project_root });
188
+ writeAudit(auditLog, { caller_id: callerIdFromRequestAuth(), surface: getRequestAuth()?.surface ?? "mcp", action: "trigger_update", outcome: result.ok ? "ok" : "error" });
189
+ return result.ok ? textResult(result.trigger) : textResult({ error: result.error, status: result.status }, true);
190
+ });
191
+ server.registerTool("set_trigger_enabled", {
192
+ description: "Enable or disable a trigger. Same write path as PATCH /api/triggers/:id.",
193
+ inputSchema: z.object({ id: z.string(), enabled: z.boolean(), project_root: z.string().optional() }),
194
+ }, async ({ id, enabled, project_root }) => {
195
+ const parsed = parsePatchTriggerBody({ enabled });
196
+ if ("ok" in parsed)
197
+ return textResult({ error: parsed.error, status: parsed.status }, true);
198
+ const result = await updateTriggerEnabled(cwd, store, id, parsed.enabled, { projectRootFilter: project_root });
199
+ writeAudit(auditLog, { caller_id: callerIdFromRequestAuth(), surface: getRequestAuth()?.surface ?? "mcp", action: "trigger_patch", outcome: result.ok ? "ok" : "error" });
200
+ return result.ok ? textResult(result.trigger) : textResult({ error: result.error, status: result.status }, true);
201
+ });
170
202
  }
@@ -0,0 +1,36 @@
1
+ import { type MemoryCardHit, type MemoryHost, type MemoryMeta } from "./host.js";
2
+ /** What a stage or agent session is allowed to do with the memories attached to it. */
3
+ export type StageMemory = {
4
+ attachments: MemoryMeta[];
5
+ refreshAttachments(): Promise<MemoryMeta[]>;
6
+ read(id: string): Promise<string>;
7
+ replace(id: string, content: string): Promise<void>;
8
+ addCard(id: string, key: string, body: string): Promise<void>;
9
+ updateCard(id: string, key: string, body: string): Promise<void>;
10
+ readCard(id: string, key: string): Promise<string>;
11
+ searchCards(id: string, query: string): Promise<MemoryCardHit[]>;
12
+ };
13
+ /** Unchecked reads and writes. The attachment allowlist sits in front of this. */
14
+ export type MemoryPort = {
15
+ meta(id: string): Promise<MemoryMeta>;
16
+ read(id: string): Promise<string>;
17
+ replace(id: string, content: string): Promise<void>;
18
+ addCard(id: string, key: string, body: string): Promise<void>;
19
+ updateCard(id: string, key: string, body: string): Promise<void>;
20
+ readCard(id: string, key: string): Promise<string>;
21
+ searchCards(id: string, query: string): Promise<MemoryCardHit[]>;
22
+ };
23
+ declare const MEMORY_VERBS: readonly ["memory.read", "memory.replace", "memory.meta", "memory.addCard", "memory.updateCard", "memory.readCard", "memory.searchCards"];
24
+ export type MemoryVerb = (typeof MEMORY_VERBS)[number];
25
+ export declare function isMemoryVerb(type: string | undefined): type is MemoryVerb;
26
+ /** One allowlist in front of either the host or the worker port. */
27
+ export declare function attachMemory(ids: readonly string[], port: MemoryPort): Promise<StageMemory>;
28
+ export declare function bindStageMemory(host: MemoryHost, ids: readonly string[]): Promise<StageMemory>;
29
+ /** Parent-side dispatch for a stage worker. Refuses an id the stage was not given. */
30
+ export declare function runMemoryRequest(host: MemoryHost, allowed: readonly string[], type: MemoryVerb, input: unknown): Promise<{
31
+ body?: string;
32
+ meta?: MemoryMeta;
33
+ hits?: MemoryCardHit[];
34
+ }>;
35
+ export declare function formatMemoryPrompt(attachments: readonly MemoryMeta[]): string;
36
+ export {};
@@ -0,0 +1,116 @@
1
+ import { MemoryError } from "./host.js";
2
+ const MEMORY_VERBS = [
3
+ "memory.read",
4
+ "memory.replace",
5
+ "memory.meta",
6
+ "memory.addCard",
7
+ "memory.updateCard",
8
+ "memory.readCard",
9
+ "memory.searchCards",
10
+ ];
11
+ export function isMemoryVerb(type) {
12
+ return type !== undefined && MEMORY_VERBS.includes(type);
13
+ }
14
+ function gate(allowed, id) {
15
+ if (!allowed.has(id))
16
+ throw new MemoryError("MEMORY_NOT_ATTACHED");
17
+ }
18
+ /** One allowlist in front of either the host or the worker port. */
19
+ export async function attachMemory(ids, port) {
20
+ const attachments = [];
21
+ for (const id of ids)
22
+ attachments.push(await port.meta(id));
23
+ const allowed = new Set(ids);
24
+ return {
25
+ attachments,
26
+ async refreshAttachments() {
27
+ const next = [];
28
+ for (const id of ids)
29
+ next.push(await port.meta(id));
30
+ attachments.length = 0;
31
+ attachments.push(...next);
32
+ return next;
33
+ },
34
+ async read(id) {
35
+ gate(allowed, id);
36
+ return port.read(id);
37
+ },
38
+ async replace(id, content) {
39
+ gate(allowed, id);
40
+ await port.replace(id, content);
41
+ },
42
+ async addCard(id, key, body) {
43
+ gate(allowed, id);
44
+ await port.addCard(id, key, body);
45
+ },
46
+ async updateCard(id, key, body) {
47
+ gate(allowed, id);
48
+ await port.updateCard(id, key, body);
49
+ },
50
+ async readCard(id, key) {
51
+ gate(allowed, id);
52
+ return port.readCard(id, key);
53
+ },
54
+ async searchCards(id, query) {
55
+ gate(allowed, id);
56
+ return port.searchCards(id, query);
57
+ },
58
+ };
59
+ }
60
+ export function bindStageMemory(host, ids) {
61
+ return attachMemory(ids, {
62
+ meta: (id) => host.readMeta(id),
63
+ read: (id) => host.read(id),
64
+ replace: (id, content) => host.replace(id, content),
65
+ addCard: (id, key, body) => host.addCard(id, key, body),
66
+ updateCard: (id, key, body) => host.updateCard(id, key, body),
67
+ readCard: (id, key) => host.readCard(id, key),
68
+ searchCards: (id, query) => host.searchCards(id, query),
69
+ });
70
+ }
71
+ /** Parent-side dispatch for a stage worker. Refuses an id the stage was not given. */
72
+ export async function runMemoryRequest(host, allowed, type, input) {
73
+ const args = (input ?? {});
74
+ const id = args.memory;
75
+ if (typeof id !== "string" || !allowed.includes(id))
76
+ throw new MemoryError("MEMORY_NOT_ATTACHED");
77
+ if (type === "memory.read")
78
+ return { body: await host.read(id) };
79
+ if (type === "memory.replace") {
80
+ if (typeof args.content !== "string")
81
+ throw new MemoryError("MEMORY_UNREADABLE");
82
+ await host.replace(id, args.content);
83
+ return {};
84
+ }
85
+ if (type === "memory.addCard" || type === "memory.updateCard") {
86
+ if (typeof args.key !== "string")
87
+ throw new MemoryError("MEMORY_INVALID_KEY");
88
+ if (typeof args.content !== "string")
89
+ throw new MemoryError("MEMORY_UNREADABLE");
90
+ if (type === "memory.addCard")
91
+ await host.addCard(id, args.key, args.content);
92
+ else
93
+ await host.updateCard(id, args.key, args.content);
94
+ return {};
95
+ }
96
+ if (type === "memory.readCard") {
97
+ if (typeof args.key !== "string")
98
+ throw new MemoryError("MEMORY_INVALID_KEY");
99
+ return { body: await host.readCard(id, args.key) };
100
+ }
101
+ if (type === "memory.searchCards") {
102
+ const query = typeof args.query === "string" ? args.query : "";
103
+ return { hits: await host.searchCards(id, query) };
104
+ }
105
+ return { meta: await host.readMeta(id) };
106
+ }
107
+ export function formatMemoryPrompt(attachments) {
108
+ const lines = attachments.map((item) => `- ${item.id}: ${item.description}`);
109
+ return [
110
+ "Memories you can read and replace:",
111
+ ...lines,
112
+ "Call read_memory with the memory id to get the page. Call replace_memory with the full new page.",
113
+ "Call add_memory_card, update_memory_card, and read_memory_card with the memory id and the card key. Call search_memory_cards with the memory id and a query.",
114
+ "The page is not shown here. Card bodies are not shown here. Do not try to open memory files yourself.",
115
+ ].join("\n");
116
+ }
@@ -0,0 +1,3 @@
1
+ import type { AgentIssue } from "../config/loadAgent.js";
2
+ /** Hard errors for an agent that names a memory the data directory does not have. */
3
+ export declare function agentMemoryIssues(ids: readonly string[] | undefined, env?: NodeJS.ProcessEnv): AgentIssue[];
@@ -0,0 +1,23 @@
1
+ import { inspectMemorySync, memoryHome } from "./host.js";
2
+ const MESSAGE = {
3
+ MEMORY_NOT_FOUND: (id) => `Memory "${id}" does not exist. Create it before attaching it.`,
4
+ MEMORY_MISMATCH: (id) => `Memory "${id}" does not match the id in its file.`,
5
+ MEMORY_UNREADABLE: (id) => `Memory "${id}" could not be read.`,
6
+ MEMORY_INVALID_ID: (id) => `Memory "${id}" is not a valid id.`,
7
+ };
8
+ /** Hard errors for an agent that names a memory the data directory does not have. */
9
+ export function agentMemoryIssues(ids, env = process.env) {
10
+ if (!ids?.length)
11
+ return [];
12
+ const home = memoryHome(env);
13
+ return ids.flatMap((id, index) => {
14
+ const inspected = inspectMemorySync(home, id);
15
+ if (inspected.ok)
16
+ return [];
17
+ return [{
18
+ path: `memories.${index}`,
19
+ code: "agent.memory_unavailable",
20
+ message: (MESSAGE[inspected.code] ?? MESSAGE.MEMORY_UNREADABLE)(id),
21
+ }];
22
+ });
23
+ }
@@ -0,0 +1,6 @@
1
+ import type { ExtensionFactory } from "@earendil-works/pi-coding-agent";
2
+ export declare const MEMORY_STORE_BASH_DENIED = "The memory store cannot be accessed with shell commands. Use the memory tools.";
3
+ export declare function memoryStorePathInCommand(command: string, home: string): boolean;
4
+ export declare function createMemoryStoreBashDenyExtension(options: {
5
+ home: string;
6
+ }): ExtensionFactory;