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
@@ -0,0 +1,394 @@
1
+ import { copyFile, link, mkdir, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
2
+ import { readFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
5
+ import { globalStageflowHome } from "../project/globalHome.js";
6
+ import { MEMORY_CARD_MAX, MEMORY_DESCRIPTION_MAX, MEMORY_KEY_MAX, MEMORY_PAGE_CAP, isMemoryId, memoryCardFileName, } from "./ids.js";
7
+ const MEMORY_SEARCH_MAX = 20;
8
+ const CARD_FILE = /^[0-9a-f]{64}$/;
9
+ const COVER_PREVIOUS = ".body.md.prev";
10
+ export const MEMORY_ERROR_CODES = [
11
+ "MEMORY_INVALID_ID",
12
+ "MEMORY_INVALID_DESCRIPTION",
13
+ "MEMORY_INVALID_KEY",
14
+ "MEMORY_EXISTS",
15
+ "MEMORY_NOT_FOUND",
16
+ "MEMORY_NO_PREVIOUS",
17
+ "MEMORY_CARD_EXISTS",
18
+ "MEMORY_CARD_NOT_FOUND",
19
+ "MEMORY_TOO_MANY_CARDS",
20
+ "MEMORY_MISMATCH",
21
+ "MEMORY_UNREADABLE",
22
+ "MEMORY_TOO_LARGE",
23
+ "MEMORY_NOT_ATTACHED",
24
+ ];
25
+ export class MemoryError extends Error {
26
+ code;
27
+ constructor(code) {
28
+ super(code);
29
+ this.name = "MemoryError";
30
+ this.code = code;
31
+ }
32
+ }
33
+ export function memoryHome(env = process.env) {
34
+ const raw = env.STAGEFLOW_HOME?.trim();
35
+ if (raw)
36
+ return raw;
37
+ return globalStageflowHome();
38
+ }
39
+ export function memoryDir(home, id) {
40
+ return path.join(home, "memories", id);
41
+ }
42
+ function checkId(id) {
43
+ if (!isMemoryId(id))
44
+ throw new MemoryError("MEMORY_INVALID_ID");
45
+ }
46
+ function checkDescription(description) {
47
+ const text = description.trim();
48
+ if (text.length < 1 || text.length > MEMORY_DESCRIPTION_MAX) {
49
+ throw new MemoryError("MEMORY_INVALID_DESCRIPTION");
50
+ }
51
+ return text;
52
+ }
53
+ function checkBody(body) {
54
+ if (typeof body !== "string" || body.length > MEMORY_PAGE_CAP)
55
+ throw new MemoryError("MEMORY_TOO_LARGE");
56
+ }
57
+ function checkKey(key) {
58
+ if (typeof key !== "string")
59
+ throw new MemoryError("MEMORY_INVALID_KEY");
60
+ const text = key.trim();
61
+ if (text.length < 1 || text.length > MEMORY_KEY_MAX)
62
+ throw new MemoryError("MEMORY_INVALID_KEY");
63
+ return text;
64
+ }
65
+ export function inspectMemorySync(home, id) {
66
+ if (!isMemoryId(id))
67
+ return { ok: false, code: "MEMORY_INVALID_ID" };
68
+ const file = path.join(memoryDir(home, id), "memory.yaml");
69
+ let text;
70
+ try {
71
+ text = readFileSync(file, "utf8");
72
+ }
73
+ catch (error) {
74
+ const code = error.code;
75
+ if (code === "ENOENT")
76
+ return { ok: false, code: "MEMORY_NOT_FOUND" };
77
+ return { ok: false, code: "MEMORY_UNREADABLE" };
78
+ }
79
+ return inspectYaml(id, text);
80
+ }
81
+ function inspectYaml(id, text) {
82
+ let raw;
83
+ try {
84
+ raw = parseYaml(text);
85
+ }
86
+ catch {
87
+ return { ok: false, code: "MEMORY_UNREADABLE" };
88
+ }
89
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
90
+ return { ok: false, code: "MEMORY_UNREADABLE" };
91
+ }
92
+ const doc = raw;
93
+ if (doc.version !== 1)
94
+ return { ok: false, code: "MEMORY_UNREADABLE" };
95
+ if (doc.id !== id)
96
+ return { ok: false, code: "MEMORY_MISMATCH" };
97
+ if (typeof doc.description !== "string" || doc.description.trim().length < 1 || doc.description.trim().length > MEMORY_DESCRIPTION_MAX) {
98
+ return { ok: false, code: "MEMORY_UNREADABLE" };
99
+ }
100
+ return { ok: true, meta: { id, description: doc.description.trim() } };
101
+ }
102
+ async function readMetaAt(home, id) {
103
+ checkId(id);
104
+ const file = path.join(memoryDir(home, id), "memory.yaml");
105
+ let text;
106
+ try {
107
+ text = await readFile(file, "utf8");
108
+ }
109
+ catch (error) {
110
+ const code = error.code;
111
+ if (code === "ENOENT")
112
+ throw new MemoryError("MEMORY_NOT_FOUND");
113
+ throw new MemoryError("MEMORY_UNREADABLE");
114
+ }
115
+ const inspected = inspectYaml(id, text);
116
+ if (!inspected.ok)
117
+ throw new MemoryError(inspected.code);
118
+ return inspected.meta;
119
+ }
120
+ function encodeCard(key, body) {
121
+ return JSON.stringify({ key, body });
122
+ }
123
+ function decodeCard(text) {
124
+ let raw;
125
+ try {
126
+ raw = JSON.parse(text);
127
+ }
128
+ catch {
129
+ throw new MemoryError("MEMORY_UNREADABLE");
130
+ }
131
+ if (typeof raw !== "object" || raw === null)
132
+ throw new MemoryError("MEMORY_UNREADABLE");
133
+ const key = raw.key;
134
+ const body = raw.body;
135
+ if (typeof key !== "string" || typeof body !== "string")
136
+ throw new MemoryError("MEMORY_UNREADABLE");
137
+ return { key, body };
138
+ }
139
+ /** A kept copy is either the text or absent. Cover and card files share this. */
140
+ async function readKept(file) {
141
+ try {
142
+ return await readFile(file, "utf8");
143
+ }
144
+ catch (error) {
145
+ if (error.code === "ENOENT")
146
+ return null;
147
+ throw new MemoryError("MEMORY_UNREADABLE");
148
+ }
149
+ }
150
+ async function readCardFile(file) {
151
+ const text = await readKept(file);
152
+ if (text === null)
153
+ return undefined;
154
+ return decodeCard(text);
155
+ }
156
+ function cardPaths(dir, key) {
157
+ const name = memoryCardFileName(key);
158
+ return {
159
+ name,
160
+ current: path.join(dir, name),
161
+ previous: path.join(dir, `.${name}.prev`),
162
+ };
163
+ }
164
+ function tempPath(dir, name) {
165
+ return path.join(dir, `.${name}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
166
+ }
167
+ async function commitNew(dir, name, content) {
168
+ const tmp = tempPath(dir, name);
169
+ const dest = path.join(dir, name);
170
+ try {
171
+ await writeFile(tmp, content, "utf8");
172
+ try {
173
+ await link(tmp, dest);
174
+ await rm(tmp, { force: true });
175
+ }
176
+ catch (linkError) {
177
+ if (linkError.code === "EEXIST")
178
+ throw new MemoryError("MEMORY_CARD_EXISTS");
179
+ await copyFile(tmp, dest);
180
+ await rm(tmp, { force: true });
181
+ }
182
+ }
183
+ catch (error) {
184
+ await rm(tmp, { force: true });
185
+ if (error instanceof MemoryError)
186
+ throw error;
187
+ if (error.code === "EEXIST")
188
+ throw new MemoryError("MEMORY_CARD_EXISTS");
189
+ throw new MemoryError("MEMORY_UNREADABLE");
190
+ }
191
+ }
192
+ async function commitReplacing(dir, name, previousName, content) {
193
+ const tmp = tempPath(dir, name);
194
+ try {
195
+ await writeFile(tmp, content, "utf8");
196
+ try {
197
+ await copyFile(path.join(dir, name), path.join(dir, previousName));
198
+ }
199
+ catch (error) {
200
+ if (error.code !== "ENOENT")
201
+ throw error;
202
+ }
203
+ await rename(tmp, path.join(dir, name));
204
+ }
205
+ catch (error) {
206
+ await rm(tmp, { force: true });
207
+ if (error instanceof MemoryError)
208
+ throw error;
209
+ throw new MemoryError("MEMORY_UNREADABLE");
210
+ }
211
+ }
212
+ async function currentCards(dir) {
213
+ let names;
214
+ try {
215
+ names = await readdir(dir);
216
+ }
217
+ catch (error) {
218
+ if (error.code === "ENOENT")
219
+ throw new MemoryError("MEMORY_NOT_FOUND");
220
+ throw new MemoryError("MEMORY_UNREADABLE");
221
+ }
222
+ const cards = [];
223
+ for (const name of names) {
224
+ if (!CARD_FILE.test(name))
225
+ continue;
226
+ const card = await readCardFile(path.join(dir, name));
227
+ if (card)
228
+ cards.push(card);
229
+ }
230
+ return cards;
231
+ }
232
+ function snippetFor(key, body, query) {
233
+ const needle = query.toLowerCase();
234
+ const bodyAt = body.toLowerCase().indexOf(needle);
235
+ const source = bodyAt >= 0 ? body : key;
236
+ const at = bodyAt >= 0 ? bodyAt : source.toLowerCase().indexOf(needle);
237
+ if (at < 0)
238
+ return source.slice(0, 80);
239
+ const start = Math.max(0, at - 40);
240
+ const end = Math.min(source.length, at + needle.length + 40);
241
+ return source.slice(start, end);
242
+ }
243
+ export function createMemoryHost(home) {
244
+ return {
245
+ async create(input) {
246
+ checkId(input.id);
247
+ const description = checkDescription(input.description);
248
+ const dir = memoryDir(home, input.id);
249
+ await mkdir(path.join(home, "memories"), { recursive: true });
250
+ try {
251
+ await mkdir(dir);
252
+ }
253
+ catch (error) {
254
+ if (error.code === "EEXIST")
255
+ throw new MemoryError("MEMORY_EXISTS");
256
+ throw error;
257
+ }
258
+ const yaml = stringifyYaml({ id: input.id, version: 1, description }, { lineWidth: 0 });
259
+ try {
260
+ await writeFile(path.join(dir, "memory.yaml"), yaml, "utf8");
261
+ await writeFile(path.join(dir, "body.md"), "", "utf8");
262
+ }
263
+ catch (error) {
264
+ await rm(dir, { recursive: true, force: true });
265
+ throw error;
266
+ }
267
+ return { id: input.id, description, body: "" };
268
+ },
269
+ async list() {
270
+ let names;
271
+ try {
272
+ names = await readdir(path.join(home, "memories"));
273
+ }
274
+ catch (error) {
275
+ if (error.code === "ENOENT")
276
+ return [];
277
+ throw error;
278
+ }
279
+ const metas = [];
280
+ for (const name of names) {
281
+ const inspected = inspectMemorySync(home, name);
282
+ if (!inspected.ok)
283
+ continue;
284
+ const cards = await currentCards(memoryDir(home, name));
285
+ metas.push({ id: inspected.meta.id, description: inspected.meta.description, cardCount: cards.length });
286
+ }
287
+ metas.sort((a, b) => a.id.localeCompare(b.id));
288
+ return metas;
289
+ },
290
+ async read(id) {
291
+ await readMetaAt(home, id);
292
+ try {
293
+ return await readFile(path.join(memoryDir(home, id), "body.md"), "utf8");
294
+ }
295
+ catch {
296
+ throw new MemoryError("MEMORY_UNREADABLE");
297
+ }
298
+ },
299
+ readMeta(id) {
300
+ return readMetaAt(home, id);
301
+ },
302
+ async replace(id, body) {
303
+ await readMetaAt(home, id);
304
+ checkBody(body);
305
+ const dir = memoryDir(home, id);
306
+ await commitReplacing(dir, "body.md", COVER_PREVIOUS, body);
307
+ },
308
+ async readPreviousCover(id) {
309
+ await readMetaAt(home, id);
310
+ const text = await readKept(path.join(memoryDir(home, id), COVER_PREVIOUS));
311
+ if (text === null)
312
+ throw new MemoryError("MEMORY_NO_PREVIOUS");
313
+ return text;
314
+ },
315
+ async addCard(id, key, body) {
316
+ const dir = memoryDir(home, id);
317
+ await readMetaAt(home, id);
318
+ const trimmed = checkKey(key);
319
+ checkBody(body);
320
+ const paths = cardPaths(dir, trimmed);
321
+ if (await readCardFile(paths.current))
322
+ throw new MemoryError("MEMORY_CARD_EXISTS");
323
+ if ((await currentCards(dir)).length >= MEMORY_CARD_MAX)
324
+ throw new MemoryError("MEMORY_TOO_MANY_CARDS");
325
+ await commitNew(dir, paths.name, encodeCard(trimmed, body));
326
+ },
327
+ async updateCard(id, key, body) {
328
+ const dir = memoryDir(home, id);
329
+ await readMetaAt(home, id);
330
+ const trimmed = checkKey(key);
331
+ checkBody(body);
332
+ const paths = cardPaths(dir, trimmed);
333
+ if (!(await readCardFile(paths.current)))
334
+ throw new MemoryError("MEMORY_CARD_NOT_FOUND");
335
+ await commitReplacing(dir, paths.name, `.${paths.name}.prev`, encodeCard(trimmed, body));
336
+ },
337
+ async readCard(id, key) {
338
+ await readMetaAt(home, id);
339
+ const trimmed = checkKey(key);
340
+ const card = await readCardFile(cardPaths(memoryDir(home, id), trimmed).current);
341
+ if (!card)
342
+ throw new MemoryError("MEMORY_CARD_NOT_FOUND");
343
+ return card.body;
344
+ },
345
+ async readPreviousCard(id, key) {
346
+ await readMetaAt(home, id);
347
+ const trimmed = checkKey(key);
348
+ const card = await readCardFile(cardPaths(memoryDir(home, id), trimmed).previous);
349
+ if (!card)
350
+ throw new MemoryError("MEMORY_CARD_NOT_FOUND");
351
+ return card.body;
352
+ },
353
+ async listCards(id) {
354
+ const dir = memoryDir(home, id);
355
+ await readMetaAt(home, id);
356
+ const keys = (await currentCards(dir)).map((card) => card.key);
357
+ keys.sort((a, b) => a.localeCompare(b));
358
+ return keys;
359
+ },
360
+ async searchCards(id, query) {
361
+ const dir = memoryDir(home, id);
362
+ await readMetaAt(home, id);
363
+ if (typeof query !== "string" || query.length === 0)
364
+ return [];
365
+ const needle = query.toLowerCase();
366
+ const hits = [];
367
+ for (const card of await currentCards(dir)) {
368
+ if (!card.key.toLowerCase().includes(needle) && !card.body.toLowerCase().includes(needle))
369
+ continue;
370
+ hits.push({ key: card.key, snippet: snippetFor(card.key, card.body, query) });
371
+ }
372
+ hits.sort((a, b) => a.key.localeCompare(b.key));
373
+ return hits.slice(0, MEMORY_SEARCH_MAX);
374
+ },
375
+ async deleteCard(id, key) {
376
+ const dir = memoryDir(home, id);
377
+ await readMetaAt(home, id);
378
+ const trimmed = checkKey(key);
379
+ const paths = cardPaths(dir, trimmed);
380
+ if (!(await readCardFile(paths.current)))
381
+ throw new MemoryError("MEMORY_CARD_NOT_FOUND");
382
+ try {
383
+ await rm(paths.current, { force: true });
384
+ await rm(paths.previous, { force: true });
385
+ }
386
+ catch {
387
+ throw new MemoryError("MEMORY_UNREADABLE");
388
+ }
389
+ },
390
+ };
391
+ }
392
+ export function memoryHostFor(env = process.env) {
393
+ return createMemoryHost(memoryHome(env));
394
+ }
@@ -0,0 +1,15 @@
1
+ export declare const MEMORY_ID_PATTERN: RegExp;
2
+ export declare const MEMORY_PAGE_CAP = 64000;
3
+ export declare const MEMORY_DESCRIPTION_MAX = 500;
4
+ export declare const MEMORY_KEY_MAX = 1024;
5
+ export declare const MEMORY_CARD_MAX = 200;
6
+ export declare function memoryCardFileName(key: string): string;
7
+ export declare function isMemoryId(value: string): boolean;
8
+ /** `undefined` when the key is absent or an empty list. Duplicates and bad ids fail. */
9
+ export declare function parseMemoryIdList(raw: unknown): {
10
+ ok: true;
11
+ value: string[] | undefined;
12
+ } | {
13
+ ok: false;
14
+ message: string;
15
+ };
@@ -0,0 +1,31 @@
1
+ import { createHash } from "node:crypto";
2
+ export const MEMORY_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
3
+ export const MEMORY_PAGE_CAP = 64_000;
4
+ export const MEMORY_DESCRIPTION_MAX = 500;
5
+ export const MEMORY_KEY_MAX = 1024;
6
+ export const MEMORY_CARD_MAX = 200;
7
+ export function memoryCardFileName(key) {
8
+ return createHash("sha256").update(key, "utf8").digest("hex");
9
+ }
10
+ export function isMemoryId(value) {
11
+ return MEMORY_ID_PATTERN.test(value);
12
+ }
13
+ /** `undefined` when the key is absent or an empty list. Duplicates and bad ids fail. */
14
+ export function parseMemoryIdList(raw) {
15
+ if (raw === undefined)
16
+ return { ok: true, value: undefined };
17
+ if (!Array.isArray(raw))
18
+ return { ok: false, message: "memories must be a list of ids" };
19
+ if (raw.length === 0)
20
+ return { ok: true, value: undefined };
21
+ const ids = [];
22
+ for (const item of raw) {
23
+ if (typeof item !== "string" || !isMemoryId(item)) {
24
+ return { ok: false, message: "each memory id must be lowercase letters, digits, _ or -, starting with a letter, at most 64 characters" };
25
+ }
26
+ ids.push(item);
27
+ }
28
+ if (new Set(ids).size !== ids.length)
29
+ return { ok: false, message: "duplicate memory id" };
30
+ return { ok: true, value: ids };
31
+ }
@@ -0,0 +1 @@
1
+ export declare function workerMemoryForStage(ids: readonly string[]): Promise<import("./access.js").StageMemory>;
@@ -0,0 +1,84 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { MemoryError } from "./host.js";
3
+ import { attachMemory } from "./access.js";
4
+ const TIMEOUT_MS = 30_000;
5
+ function request(type, input) {
6
+ return new Promise((resolve, reject) => {
7
+ if (!process.connected || !process.send) {
8
+ reject(new MemoryError("MEMORY_NOT_ATTACHED"));
9
+ return;
10
+ }
11
+ const requestId = randomUUID();
12
+ const finish = (error, response) => {
13
+ clearTimeout(timer);
14
+ process.off("message", receive);
15
+ process.off("disconnect", disconnected);
16
+ if (error)
17
+ reject(error);
18
+ else
19
+ resolve(response);
20
+ };
21
+ const disconnected = () => finish(new MemoryError("MEMORY_UNREADABLE"));
22
+ const receive = (message) => {
23
+ const response = message;
24
+ if (response?.type !== "memory.response" || response.requestId !== requestId)
25
+ return;
26
+ finish(undefined, response);
27
+ };
28
+ const timer = setTimeout(disconnected, TIMEOUT_MS);
29
+ process.on("message", receive);
30
+ process.once("disconnect", disconnected);
31
+ process.send({ type, requestId, input }, (error) => {
32
+ if (error)
33
+ disconnected();
34
+ });
35
+ });
36
+ }
37
+ export function workerMemoryForStage(ids) {
38
+ return attachMemory(ids, workerStageMemory());
39
+ }
40
+ function workerStageMemory() {
41
+ return {
42
+ async read(id) {
43
+ const response = await request("memory.read", { memory: id });
44
+ if (response.error)
45
+ throw new MemoryError(response.error.code);
46
+ return response.body ?? "";
47
+ },
48
+ async replace(id, content) {
49
+ const response = await request("memory.replace", { memory: id, content });
50
+ if (response.error)
51
+ throw new MemoryError(response.error.code);
52
+ },
53
+ async meta(id) {
54
+ const response = await request("memory.meta", { memory: id });
55
+ if (response.error || !response.meta)
56
+ throw new MemoryError(response.error?.code ?? "MEMORY_UNREADABLE");
57
+ return response.meta;
58
+ },
59
+ async addCard(id, key, body) {
60
+ const response = await request("memory.addCard", { memory: id, key, content: body });
61
+ if (response.error)
62
+ throw new MemoryError(response.error.code);
63
+ },
64
+ async updateCard(id, key, body) {
65
+ const response = await request("memory.updateCard", { memory: id, key, content: body });
66
+ if (response.error)
67
+ throw new MemoryError(response.error.code);
68
+ },
69
+ async readCard(id, key) {
70
+ const response = await request("memory.readCard", { memory: id, key });
71
+ if (response.error)
72
+ throw new MemoryError(response.error.code);
73
+ return response.body ?? "";
74
+ },
75
+ async searchCards(id, query) {
76
+ const response = await request("memory.searchCards", { memory: id, query });
77
+ if (response.error)
78
+ throw new MemoryError(response.error.code);
79
+ if (!response.hits)
80
+ throw new MemoryError("MEMORY_UNREADABLE");
81
+ return response.hits;
82
+ },
83
+ };
84
+ }
@@ -0,0 +1,113 @@
1
+ /** Last resort when nothing is connected and nothing is configured. Pi resolves unknown OpenRouter ids. */
2
+ export declare const LAST_RESORT_MODEL = "openrouter/nvidia/nemotron-3.5-lightning:free";
3
+ export type CatalogModel = {
4
+ /** `provider/model`, the form Pi resolves and stage, pipeline and agent files store. */
5
+ id: string;
6
+ provider: string;
7
+ name: string;
8
+ free: boolean;
9
+ contextWindow?: number;
10
+ reasoning?: boolean;
11
+ /** The provider runs its own file and shell tools beside Pi's, so agent grants and approvals cannot hold. */
12
+ nativeTools: boolean;
13
+ };
14
+ export type CatalogProvider = {
15
+ id: string;
16
+ name: string;
17
+ models: CatalogModel[];
18
+ /** `live`: Pi's catalog for this provider refreshed; `fallback`: built-in or cached list after an error. */
19
+ source: "live" | "fallback";
20
+ error?: string;
21
+ };
22
+ export type ModelCatalogSnapshot = {
23
+ /** Connected providers only. */
24
+ providers: CatalogProvider[];
25
+ /** Every model id across `providers`, sorted. */
26
+ models: string[];
27
+ fetchedAt: string;
28
+ };
29
+ /** The slice of Pi's ModelRuntime the catalog reads. */
30
+ export type CatalogRuntime = {
31
+ getProviders(): ReadonlyArray<{
32
+ id: string;
33
+ name?: string;
34
+ }>;
35
+ getProviderAuthStatus(providerId: string): {
36
+ configured: boolean;
37
+ };
38
+ listCredentials(): Promise<ReadonlyArray<{
39
+ providerId: string;
40
+ }>>;
41
+ getModels(providerId?: string): ReadonlyArray<{
42
+ id: string;
43
+ name?: string;
44
+ provider: string;
45
+ contextWindow?: number;
46
+ reasoning?: boolean;
47
+ cost?: {
48
+ input?: number;
49
+ output?: number;
50
+ };
51
+ }>;
52
+ refresh(options?: {
53
+ allowNetwork?: boolean;
54
+ providers?: readonly string[];
55
+ force?: boolean;
56
+ signal?: AbortSignal;
57
+ }): Promise<{
58
+ errors: ReadonlyMap<string, Error>;
59
+ } | void>;
60
+ };
61
+ export type CursorModelSource = () => {
62
+ connected: boolean;
63
+ modelIds: string[];
64
+ };
65
+ export type ModelCatalogOptions = {
66
+ cwd: string;
67
+ createRuntime?: (authPath: string) => Promise<CatalogRuntime>;
68
+ cursor?: CursorModelSource;
69
+ now?: () => number;
70
+ /** How long a built snapshot is reused before connections are re-read. */
71
+ ttlMs?: number;
72
+ refreshTimeoutMs?: number;
73
+ };
74
+ /**
75
+ * The one place model lists and the default model come from. Models are those of connected
76
+ * providers, asked of Pi's registry (live where the provider lists models, built-in otherwise).
77
+ */
78
+ export declare class ModelCatalog {
79
+ private readonly options;
80
+ private cached;
81
+ private inflight;
82
+ private lastDefault;
83
+ constructor(options: ModelCatalogOptions);
84
+ private now;
85
+ /** Cached list; rebuilt when older than the TTL or when `refresh` forces Pi to refetch provider catalogs. */
86
+ snapshot(options?: {
87
+ refresh?: boolean;
88
+ }): Promise<ModelCatalogSnapshot>;
89
+ refresh(): Promise<ModelCatalogSnapshot>;
90
+ /** True when the id belongs to a connected provider. Custom ids still run; this only drives warnings. */
91
+ isKnown(id: string): Promise<boolean>;
92
+ /**
93
+ * stageflow.yaml `model`, then the Settings workshop model, then the catalog's pick: the last-resort
94
+ * model when connected, else the first free model, else the first model, else the last resort.
95
+ */
96
+ defaultModel(): Promise<string>;
97
+ /** Synchronous default for call sites that cannot await: the last resolved default, else the last resort. */
98
+ peekDefault(): string;
99
+ private remember;
100
+ private configuredDefault;
101
+ private configuredDefaultSync;
102
+ private pick;
103
+ private build;
104
+ }
105
+ /**
106
+ * The most recently resolved default, for call sites that cannot await. Hosts resolve it at boot,
107
+ * so this is the catalog's answer, not a second source.
108
+ */
109
+ export declare function peekDefaultModel(): string;
110
+ /** The Host's catalog for a working directory, shared by every consumer in the process. */
111
+ export declare function getModelCatalog(cwd: string): ModelCatalog;
112
+ export declare function resetModelCatalogsForTests(): void;
113
+ export declare function setModelCatalogForTests(cwd: string, catalog: ModelCatalog): void;