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,29 @@
1
+ export const AGENT_AUTHOR_PLAYBOOK = `You are the Stageflow Agent author. You draft one sealed project agent with the operator. You are not a task runner, and you do not write files.
2
+
3
+ ## How you talk
4
+ - Short turns, plain words, present tense. One to four sentences.
5
+ - Do not narrate tool names. Say what changed in the config.
6
+ - The config panel shows the draft. The operator can edit it directly; your next patch builds on what they changed.
7
+
8
+ ## Tools
9
+ - read_project_file and list_project_files are read-only. Use them when the operator points at the project, an existing agent file, or AGENTS.md.
10
+ - list_mcp_servers names the servers in .mcp.json. Only grant MCP servers that exist there; if the operator wants one that isn't there, say so.
11
+ - update_agent_draft applies a patch to the draft immediately. Call it as soon as you know enough. Do not wait for Keep. If it reports issues, fix them with another patch.
12
+ - Never use bash, write, or edit. Save is the operator's button, not a tool.
13
+
14
+ ## The draft (agents/<id>.agent.yaml schema)
15
+ - id (snake_case, starts with a letter; derived from name when omitted), name, description, greeting, instructions (required).
16
+ - model: { backend: pi | claude, id?: "provider/model", thinking: low | medium | high }. Leave id out to use the project default unless they name one, and say so.
17
+ - tools: { builtin: [read | write | shell] (whole groups), builtin_tools: [read | grep | find | ls | write | edit | bash] (single tools), stageflow: { groups: [...], tools: [...] }, mcp: { <server>: [tool names] or ["*"] }, skills: [names], browser: bool }.
18
+ - approvals: { ask_first: [tool ids], approvers: { operators: bool, channel_ids: [] }, timeout_minutes, on_timeout: skip | deny }.
19
+ Tool ids: builtin names (read, grep, find, ls, write, edit, bash), "<server>.<tool>" for MCP, "agent:<id>" for sub-agents. ask_first may only name granted tools.
20
+ - sub_agents: [agent ids], observability: { capture: off | metadata | full }, limits: { session_minutes, max_turns, cost_usd | null, long_context: summarize | truncate }, guardrails: { block_patterns: [JavaScript regex] }, schedule: { cron, timezone?, message? }.
21
+
22
+ ## Defaults
23
+ - A new agent saves disabled and starts sealed: read only. Write and shell stay off unless the job needs them.
24
+ - Anything that sends, posts, writes, merges or deletes goes in approvals.ask_first.
25
+ - Capture stays metadata unless they ask for full content.
26
+
27
+ ## After an edit
28
+ - One sentence on what the agent will do now. Mention any assumption or open question. Do not paste YAML unless they ask.
29
+ `;
@@ -1,4 +1,5 @@
1
1
  import { parse as parseYaml } from "yaml";
2
+ import { peekDefaultModel } from "../../models/catalog.js";
2
3
  import { validateDraftPackage, } from "../../config/draftPackage.js";
3
4
  import { createFilesystemDocsRetriever, } from "../docsRetrieval.js";
4
5
  import { readDraftFromContext, workshopDraftContextAdapter, WORKSHOP_AUTHOR_GREETING, } from "../draftContext.js";
@@ -8,7 +9,6 @@ import { buildCreateStageDraft, buildCreateTaskDraft, createSaveTool, createWork
8
9
  export const WORKSHOP_AUTHOR_PROFILE_ID = "workshop-author";
9
10
  export { WORKSHOP_AUTHOR_PLAYBOOK };
10
11
  export { WORKSHOP_AUTHOR_TOOL_NAMES, WORKSHOP_FORBIDDEN_AGENT_TOOLS, buildCreateStageDraft, buildCreateTaskDraft, buildEditStageDraft, buildEditTaskDraft, buildCreatePipelineDraft, buildEditPipelineDraft, createSaveTool, createWorkshopAuthorMutatingTools, } from "./workshopAuthorTools.js";
11
- const DEFAULT_MODEL = "anthropic/claude-sonnet-4-5";
12
12
  const REQUIRED_IO = {
13
13
  io: {
14
14
  input: { schema: { type: "object" } },
@@ -56,7 +56,7 @@ export function buildStageAddProposal(draft, userText) {
56
56
  body: {
57
57
  id: stageId,
58
58
  system_prompt: `Stage for: ${userText.trim() || "workshop draft"}`,
59
- model: DEFAULT_MODEL,
59
+ model: peekDefaultModel(),
60
60
  ...REQUIRED_IO,
61
61
  },
62
62
  },
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { peekDefaultModel } from "../../models/catalog.js";
2
3
  import { parse as parseYaml } from "yaml";
3
4
  import { CatalogPathError, resolveDefaultWriteRoot, } from "../../config/catalogRelativePath.js";
4
5
  import { defaultWorkshopPackageDirectory } from "../../config/ensureCatalogScanRoot.js";
@@ -33,7 +34,6 @@ export const WORKSHOP_AUTHOR_TOOL_NAMES = [
33
34
  "retrieve_docs",
34
35
  ];
35
36
  export const WORKSHOP_FORBIDDEN_AGENT_TOOLS = ["bash", "write", "edit"];
36
- const DEFAULT_MODEL = "anthropic/claude-sonnet-4-5";
37
37
  const REQUIRED_IO = {
38
38
  io: {
39
39
  input: { schema: { type: "object" } },
@@ -193,7 +193,7 @@ export function buildCreateStageDraft(draft, args) {
193
193
  system_prompt: stringArg(args, "system_prompt") ??
194
194
  stringArg(args, "systemPrompt") ??
195
195
  `Stage for: ${stringArg(args, "summary") ?? stageId}`,
196
- model: stringArg(args, "model") ?? DEFAULT_MODEL,
196
+ model: stringArg(args, "model") ?? peekDefaultModel(),
197
197
  ...REQUIRED_IO,
198
198
  ...(bodyFromArgs ?? {}),
199
199
  };
@@ -265,7 +265,7 @@ export function buildEditStageDraft(draft, args) {
265
265
  ...(existingArt?.body ?? {
266
266
  id: stageId,
267
267
  system_prompt: `Stage ${stageId}`,
268
- model: DEFAULT_MODEL,
268
+ model: peekDefaultModel(),
269
269
  ...REQUIRED_IO,
270
270
  }),
271
271
  ...patch,
@@ -21,6 +21,7 @@ export type UndoMutationResult = {
21
21
  notice?: string;
22
22
  };
23
23
  export declare function draftFingerprint(draft: DraftPackage): string;
24
+ export declare function operatorContextFingerprint(context: unknown): string;
24
25
  export declare function formatArtifact(value: unknown): string;
25
26
  export declare function diffDraftPackages(before: DraftPackage, after: DraftPackage): ProposalArtifactDiff[];
26
27
  export declare function affectedStageIds(before: DraftPackage, after: DraftPackage): string[];
@@ -1,8 +1,14 @@
1
+ import { isAgentAuthorContext } from "./agentAuthorContext.js";
1
2
  import { readDraftFromContext } from "./draftContext.js";
2
3
  const UNDO_CONFLICT_NOTICE = "Undo blocked: the draft changed after this mutation. Ask the agent to reverse the change instead of rejecting.";
3
4
  export function draftFingerprint(draft) {
4
5
  return JSON.stringify(draft);
5
6
  }
7
+ export function operatorContextFingerprint(context) {
8
+ if (isAgentAuthorContext(context))
9
+ return JSON.stringify(context.draft);
10
+ return draftFingerprint(readDraftFromContext(context));
11
+ }
6
12
  export function formatArtifact(value) {
7
13
  return JSON.stringify(value, null, 2);
8
14
  }
@@ -117,6 +123,21 @@ export function affectedStageIds(before, after) {
117
123
  return [...ids];
118
124
  }
119
125
  export function enrichProposal(proposal, currentContext) {
126
+ if (isAgentAuthorContext(proposal.nextContext) ||
127
+ isAgentAuthorContext(currentContext) ||
128
+ isAgentAuthorContext(proposal.baseContext)) {
129
+ const base = isAgentAuthorContext(proposal.baseContext)
130
+ ? proposal.baseContext
131
+ : currentContext;
132
+ return {
133
+ ...proposal,
134
+ baseContext: base,
135
+ baseFingerprint: proposal.baseFingerprint ?? operatorContextFingerprint(base),
136
+ appliedFingerprint: proposal.appliedFingerprint ?? operatorContextFingerprint(proposal.nextContext),
137
+ artifacts: proposal.artifacts ?? [],
138
+ affectedStageIds: proposal.affectedStageIds ?? [],
139
+ };
140
+ }
120
141
  const baseDraft = readDraftFromContext(proposal.baseContext ?? currentContext);
121
142
  const nextDraft = readDraftFromContext(proposal.nextContext);
122
143
  const baseContext = proposal.baseContext ??
@@ -132,11 +153,11 @@ export function enrichProposal(proposal, currentContext) {
132
153
  }
133
154
  /** True when the live draft no longer matches the post-apply fingerprint. */
134
155
  export function isMutationConflict(mutation, currentContext) {
135
- const current = draftFingerprint(readDraftFromContext(currentContext));
156
+ const current = operatorContextFingerprint(currentContext);
136
157
  if (mutation.appliedFingerprint) {
137
158
  return mutation.appliedFingerprint !== current;
138
159
  }
139
- const next = draftFingerprint(readDraftFromContext(mutation.nextContext));
160
+ const next = operatorContextFingerprint(mutation.nextContext);
140
161
  return next !== current;
141
162
  }
142
163
  export const UNDO_MUTATION_CONFLICT_NOTICE = UNDO_CONFLICT_NOTICE;
@@ -1,3 +1,3 @@
1
1
  export declare const PACKAGE_NAME = "stageflow";
2
- export declare const PACKAGE_VERSION = "0.36.0";
2
+ export declare const PACKAGE_VERSION = "0.40.0";
3
3
  export declare const BUILD_SHA: string;
@@ -1,3 +1,3 @@
1
1
  export const PACKAGE_NAME = "stageflow";
2
- export const PACKAGE_VERSION = "0.36.0";
2
+ export const PACKAGE_VERSION = "0.40.0";
3
3
  export const BUILD_SHA = process.env.STAGEFLOW_BUILD_SHA ?? "unknown";
@@ -115,6 +115,7 @@ export async function checkStageMcpResolution(loaded, options) {
115
115
  allowlist,
116
116
  env: stageEnv,
117
117
  stageId: stage.id,
118
+ scope: { kind: "stage", secrets: (stage.secrets ?? []).map((d) => d.name) },
118
119
  });
119
120
  for (const server of allowlist) {
120
121
  items.push({
@@ -40,3 +40,5 @@ export declare function createRunStoreWithConnection(config: RunStoreConfig): {
40
40
  */
41
41
  export declare function createRunStore(config: RunStoreConfig): RunStore;
42
42
  export type { RunStore };
43
+ /** The SQLite connection behind a RunStore, when it has one (tests may inject a fake store). */
44
+ export declare function sqliteConnectionFromStore(store: RunStore): Database.Database | undefined;
@@ -89,3 +89,11 @@ export function createRunStoreWithConnection(config) {
89
89
  export function createRunStore(config) {
90
90
  return createRunStoreWithConnection(config).store;
91
91
  }
92
+ /** The SQLite connection behind a RunStore, when it has one (tests may inject a fake store). */
93
+ export function sqliteConnectionFromStore(store) {
94
+ const connection = store.connection;
95
+ if (connection !== null && typeof connection?.prepare === "function") {
96
+ return connection;
97
+ }
98
+ return undefined;
99
+ }
@@ -0,0 +1,56 @@
1
+ import type { RunStatus } from "./port.js";
2
+ export declare const OVERVIEW_RANGES: readonly ["24h", "7d", "30d"];
3
+ export type OverviewRange = (typeof OVERVIEW_RANGES)[number];
4
+ export declare function isOverviewRange(value: string): value is OverviewRange;
5
+ export declare function overviewWindowMs(range: OverviewRange): number;
6
+ /** One run, as much as the overview needs; no stages. */
7
+ export type OverviewRunRow = {
8
+ run_id: string;
9
+ pipeline_id: string;
10
+ status: RunStatus;
11
+ created_at: string;
12
+ finished_at?: string;
13
+ cost_usd?: number;
14
+ };
15
+ export type OverviewTotals = {
16
+ runs: number;
17
+ succeeded: number;
18
+ failed: number;
19
+ cancelled: number;
20
+ in_flight: number;
21
+ /** succeeded + failed. Cancelled runs are neither a success nor a failure. */
22
+ finished: number;
23
+ success_rate: number | null;
24
+ cost_usd: number;
25
+ };
26
+ export type OverviewBucket = {
27
+ start: string;
28
+ succeeded: number;
29
+ failed: number;
30
+ cancelled: number;
31
+ };
32
+ export type OverviewPipeline = {
33
+ pipeline_id: string;
34
+ runs: number;
35
+ succeeded: number;
36
+ failed: number;
37
+ cancelled: number;
38
+ success_rate: number | null;
39
+ median_duration_ms: number | null;
40
+ cost_usd: number;
41
+ last_run_at: string;
42
+ };
43
+ export type OverviewSummary = {
44
+ range: OverviewRange;
45
+ generated_at: string;
46
+ since: string;
47
+ totals: OverviewTotals;
48
+ previous: OverviewTotals;
49
+ buckets: OverviewBucket[];
50
+ pipelines: OverviewPipeline[];
51
+ };
52
+ /**
53
+ * Rows must cover `[now - 2 * window, now]` by `created_at`; the older half feeds `previous`.
54
+ * Buckets are rolling segments ending at `now`, so they need no time zone.
55
+ */
56
+ export declare function buildOverview(rows: readonly OverviewRunRow[], range: OverviewRange, now?: Date): OverviewSummary;
@@ -0,0 +1,134 @@
1
+ export const OVERVIEW_RANGES = ["24h", "7d", "30d"];
2
+ export function isOverviewRange(value) {
3
+ return OVERVIEW_RANGES.includes(value);
4
+ }
5
+ const HOUR_MS = 3_600_000;
6
+ const RANGE_SHAPE = {
7
+ "24h": { windowMs: 24 * HOUR_MS, bucketMs: 3 * HOUR_MS },
8
+ "7d": { windowMs: 7 * 24 * HOUR_MS, bucketMs: 24 * HOUR_MS },
9
+ "30d": { windowMs: 30 * 24 * HOUR_MS, bucketMs: 3 * 24 * HOUR_MS },
10
+ };
11
+ export function overviewWindowMs(range) {
12
+ return RANGE_SHAPE[range].windowMs;
13
+ }
14
+ function emptyTotals() {
15
+ return {
16
+ runs: 0,
17
+ succeeded: 0,
18
+ failed: 0,
19
+ cancelled: 0,
20
+ in_flight: 0,
21
+ finished: 0,
22
+ success_rate: null,
23
+ cost_usd: 0,
24
+ };
25
+ }
26
+ function tally(rows) {
27
+ const totals = emptyTotals();
28
+ for (const row of rows) {
29
+ totals.runs += 1;
30
+ if (row.status === "succeeded")
31
+ totals.succeeded += 1;
32
+ else if (row.status === "failed")
33
+ totals.failed += 1;
34
+ else if (row.status === "cancelled")
35
+ totals.cancelled += 1;
36
+ else
37
+ totals.in_flight += 1;
38
+ totals.cost_usd += row.cost_usd ?? 0;
39
+ }
40
+ totals.finished = totals.succeeded + totals.failed;
41
+ totals.success_rate = totals.finished > 0 ? totals.succeeded / totals.finished : null;
42
+ totals.cost_usd = roundCost(totals.cost_usd);
43
+ return totals;
44
+ }
45
+ function roundCost(value) {
46
+ return Math.round(value * 10_000) / 10_000;
47
+ }
48
+ function median(values) {
49
+ if (values.length === 0)
50
+ return null;
51
+ const sorted = [...values].sort((a, b) => a - b);
52
+ const mid = Math.floor(sorted.length / 2);
53
+ return sorted.length % 2 === 1 ? sorted[mid] : Math.round((sorted[mid - 1] + sorted[mid]) / 2);
54
+ }
55
+ function durationMs(row) {
56
+ if (row.status !== "succeeded" && row.status !== "failed")
57
+ return null;
58
+ if (!row.finished_at)
59
+ return null;
60
+ const ms = Date.parse(row.finished_at) - Date.parse(row.created_at);
61
+ return Number.isFinite(ms) && ms >= 0 ? ms : null;
62
+ }
63
+ /**
64
+ * Rows must cover `[now - 2 * window, now]` by `created_at`; the older half feeds `previous`.
65
+ * Buckets are rolling segments ending at `now`, so they need no time zone.
66
+ */
67
+ export function buildOverview(rows, range, now = new Date()) {
68
+ const { windowMs, bucketMs } = RANGE_SHAPE[range];
69
+ const nowMs = now.getTime();
70
+ const sinceMs = nowMs - windowMs;
71
+ const previousSinceMs = sinceMs - windowMs;
72
+ const current = [];
73
+ const previous = [];
74
+ for (const row of rows) {
75
+ const created = Date.parse(row.created_at);
76
+ if (!Number.isFinite(created))
77
+ continue;
78
+ if (created >= sinceMs && created <= nowMs)
79
+ current.push(row);
80
+ else if (created >= previousSinceMs && created < sinceMs)
81
+ previous.push(row);
82
+ }
83
+ const bucketCount = windowMs / bucketMs;
84
+ const buckets = Array.from({ length: bucketCount }, (_, i) => ({
85
+ start: new Date(sinceMs + i * bucketMs).toISOString(),
86
+ succeeded: 0,
87
+ failed: 0,
88
+ cancelled: 0,
89
+ }));
90
+ for (const row of current) {
91
+ const index = Math.min(bucketCount - 1, Math.floor((Date.parse(row.created_at) - sinceMs) / bucketMs));
92
+ const bucket = buckets[index];
93
+ if (row.status === "succeeded")
94
+ bucket.succeeded += 1;
95
+ else if (row.status === "failed")
96
+ bucket.failed += 1;
97
+ else if (row.status === "cancelled")
98
+ bucket.cancelled += 1;
99
+ }
100
+ const byPipeline = new Map();
101
+ for (const row of current) {
102
+ const list = byPipeline.get(row.pipeline_id);
103
+ if (list)
104
+ list.push(row);
105
+ else
106
+ byPipeline.set(row.pipeline_id, [row]);
107
+ }
108
+ const pipelines = [...byPipeline.entries()]
109
+ .map(([pipeline_id, list]) => {
110
+ const totals = tally(list);
111
+ const durations = list.map(durationMs).filter((ms) => ms !== null);
112
+ return {
113
+ pipeline_id,
114
+ runs: totals.runs,
115
+ succeeded: totals.succeeded,
116
+ failed: totals.failed,
117
+ cancelled: totals.cancelled,
118
+ success_rate: totals.success_rate,
119
+ median_duration_ms: median(durations),
120
+ cost_usd: totals.cost_usd,
121
+ last_run_at: list.reduce((latest, row) => (row.created_at > latest ? row.created_at : latest), list[0].created_at),
122
+ };
123
+ })
124
+ .sort((a, b) => b.runs - a.runs || a.pipeline_id.localeCompare(b.pipeline_id));
125
+ return {
126
+ range,
127
+ generated_at: now.toISOString(),
128
+ since: new Date(sinceMs).toISOString(),
129
+ totals: tally(current),
130
+ previous: tally(previous),
131
+ buckets,
132
+ pipelines,
133
+ };
134
+ }
@@ -7,6 +7,7 @@ import type { FeedbackLoopConfig, InlinePipelineDefinition, ResolvedPipelineDag
7
7
  import type { CompletionCheck } from "../types/completion.js";
8
8
  import type { StageGateKind } from "../types/stage.js";
9
9
  import type { StageUsage } from "../types/usage.js";
10
+ import type { OverviewRunRow } from "./overviewSummary.js";
10
11
  export type { ConfigOriginRecord };
11
12
  export type RunStatus = "created" | "queued" | "running" | "succeeded" | "failed" | "cancelled";
12
13
  export type RunPipelineDagSnapshot = ResolvedPipelineDag & {
@@ -463,6 +464,8 @@ export interface RunStore {
463
464
  }): Promise<void>;
464
465
  listStageEvents(runId: string, stageId: string, attempt?: number): Promise<StageLogEvent[]>;
465
466
  listRuns(filter?: ListRunsFilter): Promise<RunSummary[]>;
467
+ /** One light row per run created at or after `since` (ISO), newest first, with summed stage cost. No stages are loaded. */
468
+ listRunOverviewRows(since: string): Promise<OverviewRunRow[]>;
466
469
  /** Idempotent upsert; returns the realpath/resolve-normalized absolute key. */
467
470
  ensureProject(absPath: string): Promise<string>;
468
471
  /** Absolute roots from the durable projects registry. */
@@ -2,6 +2,7 @@ import Database from "better-sqlite3";
2
2
  import type { StageEnvelope } from "../../types/envelope.js";
3
3
  import type { StageLogLine } from "../../agent/activity.js";
4
4
  import { type CreateFeedbackLoopInput, type CreateFeedbackReplayInput, type CreateFeedbackReplayStagePassInput, type CreateForkGenerationInput, type CreateRunInput, type CreatedRun, type FeedbackLoopPatch, type FeedbackLoopRecord, type FeedbackLoopState, type FeedbackReplayPatch, type FeedbackReplayRecord, type FeedbackReplayStagePassPatch, type FeedbackReplayStagePassRecord, type ForkGenerationPatch, type ForkGenerationRecord, type ListRunsFilter, type RunDetail, type RunMeta, type RunPipelineDagSnapshot, type RunStatus, type RunStore, type RunSummary, type StageExecution, type StageExecutionPatch, type StageLogEvent, type TriggerRecord, type UpsertTriggerInput, type VerificationCheckResult, type VerificationCheckResultPatch, type ConfigOriginRecord } from "../port.js";
5
+ import type { OverviewRunRow } from "../overviewSummary.js";
5
6
  import { type RunSubmissionRecord } from "../submission.js";
6
7
  type SqliteRunStoreOpenerMode = "assert" | "migrate";
7
8
  export declare class SqliteRunStore implements RunStore {
@@ -76,6 +77,7 @@ export declare class SqliteRunStore implements RunStore {
76
77
  private updateStageStatusDenorm;
77
78
  listStageEvents(runId: string, stageId: string, attempt?: number): Promise<StageLogEvent[]>;
78
79
  listRuns(filter?: ListRunsFilter): Promise<RunSummary[]>;
80
+ listRunOverviewRows(since: string): Promise<OverviewRunRow[]>;
79
81
  ensureProject(absPath: string): Promise<string>;
80
82
  listRegisteredProjects(): Promise<string[]>;
81
83
  upsertTrigger(input: UpsertTriggerInput): Promise<TriggerRecord>;
@@ -870,6 +870,24 @@ export class SqliteRunStore {
870
870
  }
871
871
  return summaries;
872
872
  }
873
+ async listRunOverviewRows(since) {
874
+ await this.ready();
875
+ const rows = this.db
876
+ .prepare(`SELECT r.run_id, r.pipeline_id, r.status, r.created_at, r.finished_at,
877
+ (SELECT SUM(e.cost_usd) FROM stage_executions e WHERE e.run_id = r.run_id) AS cost_usd
878
+ FROM runs r
879
+ WHERE r.created_at >= ?
880
+ ORDER BY r.created_at DESC`)
881
+ .all(since);
882
+ return rows.map((row) => ({
883
+ run_id: row.run_id,
884
+ pipeline_id: row.pipeline_id,
885
+ status: row.status,
886
+ created_at: row.created_at,
887
+ ...(row.finished_at != null ? { finished_at: row.finished_at } : {}),
888
+ ...(row.cost_usd != null ? { cost_usd: row.cost_usd } : {}),
889
+ }));
890
+ }
873
891
  async ensureProject(absPath) {
874
892
  await this.ready();
875
893
  const resolved = path.resolve(absPath);
@@ -0,0 +1,12 @@
1
+ import type Database from "better-sqlite3";
2
+ /**
3
+ * Project agents: revisions, sessions, transcript, approvals, turns, spans.
4
+ * Read and written only by `AgentStore` (src/agents/store.ts); kept out of the
5
+ * run/stage-shaped RunStore port. `project_key` scopes rows to one project root.
6
+ */
7
+ export declare const MIGRATION_013: {
8
+ version: number;
9
+ name: string;
10
+ minStageflowVersion: string;
11
+ up(db: Database.Database): void;
12
+ };
@@ -0,0 +1,156 @@
1
+ import { PACKAGE_VERSION } from "../../../package-meta.js";
2
+ /**
3
+ * Project agents: revisions, sessions, transcript, approvals, turns, spans.
4
+ * Read and written only by `AgentStore` (src/agents/store.ts); kept out of the
5
+ * run/stage-shaped RunStore port. `project_key` scopes rows to one project root.
6
+ */
7
+ export const MIGRATION_013 = {
8
+ version: 13,
9
+ name: "013_agents",
10
+ minStageflowVersion: PACKAGE_VERSION,
11
+ up(db) {
12
+ db.exec(`
13
+ CREATE TABLE agent_definitions (
14
+ project_key TEXT NOT NULL,
15
+ agent_id TEXT NOT NULL,
16
+ current_revision INTEGER NOT NULL,
17
+ archived INTEGER NOT NULL DEFAULT 0,
18
+ file_hash TEXT,
19
+ issues_json TEXT,
20
+ created_at TEXT NOT NULL,
21
+ updated_at TEXT NOT NULL,
22
+ PRIMARY KEY (project_key, agent_id)
23
+ );
24
+
25
+ CREATE TABLE agent_revisions (
26
+ project_key TEXT NOT NULL,
27
+ agent_id TEXT NOT NULL,
28
+ revision INTEGER NOT NULL,
29
+ definition_json TEXT NOT NULL,
30
+ yaml_text TEXT NOT NULL,
31
+ source TEXT NOT NULL CHECK (source IN ('console', 'file', 'author', 'restore', 'api')),
32
+ author TEXT,
33
+ summary TEXT,
34
+ restored_from INTEGER,
35
+ created_at TEXT NOT NULL,
36
+ PRIMARY KEY (project_key, agent_id, revision)
37
+ );
38
+
39
+ CREATE TABLE agent_sessions (
40
+ session_id TEXT PRIMARY KEY NOT NULL,
41
+ project_key TEXT NOT NULL,
42
+ agent_id TEXT NOT NULL,
43
+ revision INTEGER NOT NULL,
44
+ backend TEXT NOT NULL,
45
+ status TEXT NOT NULL CHECK (status IN ('open', 'running', 'waiting', 'held', 'closed')),
46
+ source TEXT NOT NULL,
47
+ source_ref TEXT,
48
+ thread_key TEXT,
49
+ title TEXT,
50
+ created_at TEXT NOT NULL,
51
+ updated_at TEXT NOT NULL
52
+ );
53
+ CREATE INDEX agent_sessions_agent_updated ON agent_sessions (project_key, agent_id, updated_at DESC);
54
+ CREATE UNIQUE INDEX agent_sessions_thread ON agent_sessions (project_key, agent_id, thread_key)
55
+ WHERE thread_key IS NOT NULL;
56
+
57
+ CREATE TABLE agent_messages (
58
+ message_id TEXT PRIMARY KEY NOT NULL,
59
+ session_id TEXT NOT NULL REFERENCES agent_sessions (session_id) ON DELETE CASCADE,
60
+ seq INTEGER NOT NULL,
61
+ turn_id TEXT,
62
+ role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
63
+ author TEXT,
64
+ text TEXT NOT NULL,
65
+ created_at TEXT NOT NULL,
66
+ UNIQUE (session_id, seq)
67
+ );
68
+
69
+ CREATE TABLE agent_turns (
70
+ turn_id TEXT PRIMARY KEY NOT NULL,
71
+ session_id TEXT NOT NULL REFERENCES agent_sessions (session_id) ON DELETE CASCADE,
72
+ seq INTEGER NOT NULL,
73
+ revision INTEGER NOT NULL,
74
+ trace_id TEXT NOT NULL,
75
+ status TEXT NOT NULL CHECK (status IN ('running', 'waiting', 'succeeded', 'failed', 'cancelled')),
76
+ error_code TEXT,
77
+ input_tokens INTEGER,
78
+ output_tokens INTEGER,
79
+ cost_usd REAL,
80
+ started_at TEXT NOT NULL,
81
+ ended_at TEXT,
82
+ UNIQUE (session_id, seq)
83
+ );
84
+
85
+ CREATE TABLE agent_spans (
86
+ span_id TEXT PRIMARY KEY NOT NULL,
87
+ turn_id TEXT NOT NULL REFERENCES agent_turns (turn_id) ON DELETE CASCADE,
88
+ parent_span_id TEXT,
89
+ name TEXT NOT NULL,
90
+ kind TEXT NOT NULL,
91
+ status TEXT NOT NULL,
92
+ started_at TEXT NOT NULL,
93
+ ended_at TEXT,
94
+ duration_ms INTEGER,
95
+ input_tokens INTEGER,
96
+ output_tokens INTEGER,
97
+ attributes_json TEXT NOT NULL DEFAULT '{}',
98
+ input_text TEXT,
99
+ output_text TEXT
100
+ );
101
+ CREATE INDEX agent_spans_turn ON agent_spans (turn_id, started_at);
102
+
103
+ CREATE TABLE agent_eval_scores (
104
+ turn_id TEXT NOT NULL REFERENCES agent_turns (turn_id) ON DELETE CASCADE,
105
+ metric TEXT NOT NULL,
106
+ score REAL NOT NULL,
107
+ recorded_at TEXT NOT NULL,
108
+ PRIMARY KEY (turn_id, metric)
109
+ );
110
+
111
+ CREATE TABLE agent_approvals (
112
+ approval_id TEXT PRIMARY KEY NOT NULL,
113
+ session_id TEXT NOT NULL REFERENCES agent_sessions (session_id) ON DELETE CASCADE,
114
+ turn_id TEXT,
115
+ tool TEXT NOT NULL,
116
+ payload_json TEXT NOT NULL,
117
+ payload_hash TEXT NOT NULL,
118
+ status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'denied', 'expired')),
119
+ created_at TEXT NOT NULL,
120
+ expires_at TEXT NOT NULL,
121
+ decided_at TEXT,
122
+ actor_json TEXT
123
+ );
124
+ CREATE UNIQUE INDEX agent_approvals_one_pending ON agent_approvals (session_id) WHERE status = 'pending';
125
+
126
+ CREATE TABLE agent_executed_payloads (
127
+ session_id TEXT NOT NULL REFERENCES agent_sessions (session_id) ON DELETE CASCADE,
128
+ payload_hash TEXT NOT NULL,
129
+ executed_at TEXT NOT NULL,
130
+ PRIMARY KEY (session_id, payload_hash)
131
+ );
132
+
133
+ CREATE TABLE agent_outbound_sends (
134
+ session_id TEXT NOT NULL REFERENCES agent_sessions (session_id) ON DELETE CASCADE,
135
+ operation_key TEXT NOT NULL,
136
+ state TEXT NOT NULL CHECK (state IN ('submitted', 'unknown')),
137
+ updated_at TEXT NOT NULL,
138
+ PRIMARY KEY (session_id, operation_key)
139
+ );
140
+
141
+ CREATE TABLE agent_inbound_events (
142
+ project_key TEXT NOT NULL,
143
+ dedupe_key TEXT NOT NULL,
144
+ received_at TEXT NOT NULL,
145
+ PRIMARY KEY (project_key, dedupe_key)
146
+ );
147
+
148
+ CREATE TABLE agent_schedules (
149
+ project_key TEXT NOT NULL,
150
+ agent_id TEXT NOT NULL,
151
+ next_run_at TEXT NOT NULL,
152
+ PRIMARY KEY (project_key, agent_id)
153
+ );
154
+ `);
155
+ },
156
+ };
@@ -0,0 +1,11 @@
1
+ import type Database from "better-sqlite3";
2
+ /**
3
+ * Replies to people on a channel (AgentMail, iMessage): one row per turn that has to reach someone, with how
4
+ * the send went, so a failed reply can be retried or resent and a reply of unknown outcome is never duplicated.
5
+ */
6
+ export declare const MIGRATION_014: {
7
+ version: number;
8
+ name: string;
9
+ minStageflowVersion: string;
10
+ up(db: Database.Database): void;
11
+ };
@@ -0,0 +1,32 @@
1
+ import { PACKAGE_VERSION } from "../../../package-meta.js";
2
+ /**
3
+ * Replies to people on a channel (AgentMail, iMessage): one row per turn that has to reach someone, with how
4
+ * the send went, so a failed reply can be retried or resent and a reply of unknown outcome is never duplicated.
5
+ */
6
+ export const MIGRATION_014 = {
7
+ version: 14,
8
+ name: "014_agent_deliveries",
9
+ minStageflowVersion: PACKAGE_VERSION,
10
+ up(db) {
11
+ db.exec(`
12
+ CREATE TABLE agent_deliveries (
13
+ turn_id TEXT PRIMARY KEY REFERENCES agent_turns (turn_id) ON DELETE CASCADE,
14
+ session_id TEXT NOT NULL REFERENCES agent_sessions (session_id) ON DELETE CASCADE,
15
+ connection_id TEXT NOT NULL,
16
+ provider TEXT NOT NULL,
17
+ reply_to TEXT NOT NULL,
18
+ text TEXT NOT NULL,
19
+ state TEXT NOT NULL CHECK (state IN ('pending', 'sending', 'delivered', 'failed', 'unknown')),
20
+ attempts INTEGER NOT NULL DEFAULT 0,
21
+ error_code TEXT,
22
+ error_message TEXT,
23
+ retryable INTEGER NOT NULL DEFAULT 0,
24
+ next_attempt_at TEXT,
25
+ created_at TEXT NOT NULL,
26
+ updated_at TEXT NOT NULL
27
+ );
28
+ CREATE INDEX agent_deliveries_due ON agent_deliveries (state, next_attempt_at);
29
+ CREATE INDEX agent_deliveries_session ON agent_deliveries (session_id);
30
+ `);
31
+ },
32
+ };