@vellumai/assistant 0.8.12 → 0.9.0-staging.2

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 (442) hide show
  1. package/AGENTS.md +0 -14
  2. package/ARCHITECTURE.md +45 -45
  3. package/README.md +1 -1
  4. package/bun.lock +200 -154
  5. package/docs/architecture/integrations.md +3 -3
  6. package/docs/architecture/memory.md +2 -2
  7. package/docs/architecture/security.md +10 -10
  8. package/docs/runbook-trusted-contacts.md +12 -12
  9. package/docs/skills.md +6 -6
  10. package/docs/workflows-testing.md +221 -0
  11. package/docs/workflows.md +510 -0
  12. package/examples/plugins/echo/README.md +5 -5
  13. package/knip.json +2 -0
  14. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
  15. package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
  16. package/openapi.yaml +7197 -5708
  17. package/package.json +8 -4
  18. package/scripts/generate-openapi.ts +66 -114
  19. package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
  20. package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
  21. package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
  22. package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  24. package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
  25. package/src/__tests__/app-compiler.test.ts +7 -1
  26. package/src/__tests__/app-dir-path-guard.test.ts +27 -3
  27. package/src/__tests__/app-executors.test.ts +43 -0
  28. package/src/__tests__/approval-cascade.test.ts +0 -5
  29. package/src/__tests__/approval-routes-http.test.ts +91 -0
  30. package/src/__tests__/assistant-stream-state.test.ts +107 -0
  31. package/src/__tests__/browser-fill-credential.test.ts +3 -3
  32. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
  33. package/src/__tests__/compaction-events.test.ts +63 -7
  34. package/src/__tests__/compaction-trail-store.test.ts +74 -1
  35. package/src/__tests__/compaction.benchmark.test.ts +63 -41
  36. package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
  37. package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
  39. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
  40. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  41. package/src/__tests__/conversation-process-callsite.test.ts +0 -5
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
  43. package/src/__tests__/conversation-queue.test.ts +0 -5
  44. package/src/__tests__/conversation-slash-queue.test.ts +0 -5
  45. package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
  46. package/src/__tests__/conversation-speed-override.test.ts +0 -5
  47. package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
  48. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  49. package/src/__tests__/conversation-usage.test.ts +2 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
  52. package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
  53. package/src/__tests__/credential-broker-server-use.test.ts +2 -2
  54. package/src/__tests__/credential-broker.test.ts +1 -1
  55. package/src/__tests__/credential-prompt-route.test.ts +417 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault.test.ts +37 -0
  58. package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
  59. package/src/__tests__/dynamic-page-surface.test.ts +219 -0
  60. package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
  61. package/src/__tests__/gateway-flag-listener.test.ts +24 -7
  62. package/src/__tests__/guardian-action-sweep.test.ts +56 -219
  63. package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
  64. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  65. package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
  66. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
  67. package/src/__tests__/llm-resolver.test.ts +115 -0
  68. package/src/__tests__/managed-profile-guard.test.ts +6 -5
  69. package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
  70. package/src/__tests__/media-generate-image.test.ts +20 -9
  71. package/src/__tests__/mock-gateway-ipc.ts +23 -0
  72. package/src/__tests__/model-intents.test.ts +1 -1
  73. package/src/__tests__/normalize-onboarding.test.ts +26 -0
  74. package/src/__tests__/notification-decision-strategy.test.ts +4 -2
  75. package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
  76. package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
  77. package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
  78. package/src/__tests__/require-fresh-approval.test.ts +425 -1
  79. package/src/__tests__/resolve-app-id.test.ts +56 -0
  80. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
  81. package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
  82. package/src/__tests__/schedule-routes.test.ts +257 -4
  83. package/src/__tests__/schedule-store.test.ts +60 -0
  84. package/src/__tests__/schedule-tools.test.ts +247 -2
  85. package/src/__tests__/skill-execute-input.test.ts +85 -0
  86. package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
  87. package/src/__tests__/skills.test.ts +3 -3
  88. package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
  89. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  90. package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
  91. package/src/__tests__/system-prompt.test.ts +1 -1
  92. package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
  93. package/src/__tests__/task-scheduler.test.ts +299 -0
  94. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
  95. package/src/__tests__/tool-result-spool.test.ts +3 -1
  96. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
  97. package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
  98. package/src/agent/compaction-circuit.ts +11 -0
  99. package/src/agent/loop.ts +181 -12
  100. package/src/api/constants/call-sites.ts +12 -0
  101. package/src/api/events/assistant-thinking-delta.ts +10 -0
  102. package/src/api/events/usage-update.ts +7 -0
  103. package/src/api/index.ts +4 -1
  104. package/src/api/responses/memory-v3-selection-log.ts +18 -11
  105. package/src/approvals/approval-primitive.ts +2 -2
  106. package/src/background-wake/background-wake-routes.test.ts +5 -2
  107. package/src/bundler/compiler-tools.ts +1 -1
  108. package/src/bundler/package-resolver.ts +0 -1
  109. package/src/calls/call-domain.ts +1 -1
  110. package/src/calls/guardian-action-sweep.ts +16 -93
  111. package/src/cli/AGENTS.md +4 -0
  112. package/src/cli/commands/__tests__/schedules.test.ts +430 -1
  113. package/src/cli/commands/credentials.ts +28 -24
  114. package/src/cli/commands/image-generation.ts +23 -9
  115. package/src/cli/commands/notifications.ts +1 -1
  116. package/src/cli/commands/plugins.ts +89 -46
  117. package/src/cli/commands/schedules.ts +384 -11
  118. package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
  119. package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
  120. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
  121. package/src/cli/lib/inspect-plugin.ts +62 -1
  122. package/src/cli/lib/install-from-github.ts +52 -5
  123. package/src/cli/lib/upgrade-plugin.ts +18 -0
  124. package/src/config/__tests__/workflows-schema.test.ts +60 -0
  125. package/src/config/bundled-skills/acp/SKILL.md +2 -2
  126. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  127. package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
  128. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
  129. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
  130. package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
  131. package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
  132. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
  133. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
  134. package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
  135. package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
  136. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
  137. package/src/config/bundled-skills/schedule/SKILL.md +7 -2
  138. package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
  139. package/src/config/bundled-skills/workflows/SKILL.md +214 -0
  140. package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
  141. package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
  142. package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
  143. package/src/config/bundled-tool-registry.ts +14 -2
  144. package/src/config/call-site-defaults.ts +5 -0
  145. package/src/config/feature-flag-registry.json +12 -4
  146. package/src/config/llm-context-resolution.ts +8 -0
  147. package/src/config/llm-resolver.ts +30 -0
  148. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
  149. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
  150. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
  151. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
  152. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
  153. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
  154. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
  155. package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
  156. package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
  157. package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
  158. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
  159. package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
  160. package/src/config/schema.ts +2 -0
  161. package/src/config/schemas/call-site-catalog.ts +7 -0
  162. package/src/config/schemas/heartbeat.ts +4 -1
  163. package/src/config/schemas/llm.ts +33 -26
  164. package/src/config/schemas/memory-retrospective.ts +19 -0
  165. package/src/config/schemas/platform.ts +8 -0
  166. package/src/config/schemas/services.ts +5 -2
  167. package/src/config/schemas/workflows.ts +42 -0
  168. package/src/config/skills.ts +3 -3
  169. package/src/context/compactor.ts +273 -39
  170. package/src/context/post-turn-tool-result-truncation.ts +23 -6
  171. package/src/context/tool-result-spool.ts +12 -17
  172. package/src/credential-execution/executable-discovery.ts +1 -1
  173. package/src/credential-execution/process-manager.ts +37 -3
  174. package/src/credential-execution/prompted-credential.ts +205 -0
  175. package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
  176. package/src/daemon/conversation-process.ts +11 -2
  177. package/src/daemon/conversation-surfaces.ts +167 -3
  178. package/src/daemon/conversation-tool-setup.ts +103 -26
  179. package/src/daemon/conversation-usage.ts +2 -0
  180. package/src/daemon/conversation.ts +115 -11
  181. package/src/daemon/handlers/shared.ts +26 -14
  182. package/src/daemon/host-cu-proxy.ts +15 -12
  183. package/src/daemon/host-file-proxy.ts +15 -12
  184. package/src/daemon/host-transfer-proxy.ts +30 -24
  185. package/src/daemon/lifecycle.ts +40 -3
  186. package/src/daemon/message-protocol.ts +3 -0
  187. package/src/daemon/message-types/messages.ts +2 -10
  188. package/src/daemon/message-types/workflows.ts +49 -0
  189. package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
  190. package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
  191. package/src/daemon/process-message.ts +6 -0
  192. package/src/daemon/tool-setup-types.ts +57 -0
  193. package/src/daemon/wake-conversation-ops.ts +18 -0
  194. package/src/heartbeat/heartbeat-run-store.ts +8 -2
  195. package/src/home/feed-types.ts +1 -1
  196. package/src/ipc/gateway-flag-listener.ts +28 -6
  197. package/src/mcp/mcp-auth-state.ts +8 -20
  198. package/src/media/__tests__/image-models.test.ts +57 -0
  199. package/src/media/image-models.ts +66 -0
  200. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
  201. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
  202. package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
  203. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
  204. package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
  205. package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
  206. package/src/memory/auto-analysis-enqueue.ts +14 -1
  207. package/src/memory/compaction-log-store-clickhouse.ts +6 -4
  208. package/src/memory/conversation-crud.ts +9 -2
  209. package/src/memory/conversation-disk-view.ts +1 -1
  210. package/src/memory/conversation-queries.ts +22 -7
  211. package/src/memory/db-init.ts +20 -0
  212. package/src/memory/db-maintenance.ts +16 -0
  213. package/src/memory/embedding-runtime-manager.ts +1 -1
  214. package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
  215. package/src/memory/llm-request-log-source-local.ts +19 -1
  216. package/src/memory/llm-request-log-source.ts +35 -6
  217. package/src/memory/llm-request-log-store.ts +90 -2
  218. package/src/memory/memory-retrospective-constants.ts +9 -0
  219. package/src/memory/memory-retrospective-enqueue.ts +3 -6
  220. package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
  221. package/src/memory/memory-retrospective-job.ts +500 -208
  222. package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
  223. package/src/memory/memory-retrospective-state.ts +85 -2
  224. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
  225. package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
  226. package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
  227. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
  228. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
  229. package/src/memory/migrations/284-workflow-runs.ts +51 -0
  230. package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
  231. package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
  232. package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
  233. package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
  234. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
  235. package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
  236. package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
  237. package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
  238. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
  239. package/src/memory/migrations/index.ts +10 -0
  240. package/src/memory/preloaded-apps.ts +116 -0
  241. package/src/memory/schema/infrastructure.ts +4 -0
  242. package/src/memory/schema/memory-core.ts +4 -0
  243. package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
  244. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
  245. package/src/memory/v2/__tests__/page-store.test.ts +13 -2
  246. package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
  247. package/src/memory/v2/frontmatter-sweep.ts +7 -6
  248. package/src/memory/v2/page-store.ts +4 -3
  249. package/src/memory/v2/qdrant.ts +42 -3
  250. package/src/memory/v2/types.ts +16 -10
  251. package/src/messaging/draft-store.ts +1 -1
  252. package/src/notifications/access-request-copy.ts +200 -113
  253. package/src/notifications/adapters/slack.ts +250 -111
  254. package/src/notifications/adapters/telegram.ts +7 -44
  255. package/src/notifications/approval-card-builder.ts +93 -0
  256. package/src/notifications/broadcaster.ts +74 -0
  257. package/src/notifications/conversation-pairing.ts +8 -6
  258. package/src/notifications/copy-composer.ts +32 -26
  259. package/src/notifications/decision-engine.ts +59 -7
  260. package/src/notifications/guardian-question-mode.ts +145 -155
  261. package/src/notifications/home-feed-side-effect.ts +28 -11
  262. package/src/notifications/notification-utils.ts +66 -0
  263. package/src/notifications/signal.ts +6 -0
  264. package/src/notifications/tool-approval-copy.ts +142 -0
  265. package/src/notifications/types.ts +19 -0
  266. package/src/permissions/threshold.ts +11 -0
  267. package/src/plugin-api/types.ts +16 -4
  268. package/src/plugins/defaults/compaction/window-manager.ts +44 -0
  269. package/src/plugins/defaults/index.ts +46 -0
  270. package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
  271. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
  272. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
  273. package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
  274. package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
  275. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
  276. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
  277. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
  278. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
  282. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
  283. package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
  284. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
  285. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
  286. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
  287. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
  288. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
  289. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
  290. package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
  291. package/src/prompts/__tests__/system-prompt.test.ts +100 -1
  292. package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
  293. package/src/prompts/normalize-onboarding.ts +2 -0
  294. package/src/prompts/persona-resolver.ts +3 -0
  295. package/src/prompts/system-prompt.ts +51 -2
  296. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
  297. package/src/prompts/templates/system-sections.ts +8 -3
  298. package/src/providers/call-site-routing.ts +6 -3
  299. package/src/providers/fireworks/client.ts +3 -0
  300. package/src/providers/inference/auth.ts +52 -46
  301. package/src/providers/model-intents.ts +2 -2
  302. package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
  303. package/src/providers/openai/chat-completions-provider.ts +47 -9
  304. package/src/providers/openai/coerce-object-args.ts +104 -0
  305. package/src/providers/retry.ts +8 -5
  306. package/src/providers/types.ts +10 -0
  307. package/src/runtime/__tests__/agent-wake.test.ts +629 -7
  308. package/src/runtime/access-request-helper.ts +16 -9
  309. package/src/runtime/agent-wake.ts +302 -51
  310. package/src/runtime/assistant-stream-state.ts +141 -8
  311. package/src/runtime/background-job-runner.ts +9 -0
  312. package/src/runtime/channel-approval-types.ts +1 -0
  313. package/src/runtime/channel-invite-transports/telegram.ts +6 -5
  314. package/src/runtime/channel-invite-transports/voice.ts +2 -2
  315. package/src/runtime/channel-invite-types.ts +4 -2
  316. package/src/runtime/channel-retry-sweep.ts +19 -41
  317. package/src/runtime/finalize-event-delivery.ts +72 -0
  318. package/src/runtime/guardian-action-message-composer.ts +0 -54
  319. package/src/runtime/http-server.ts +6 -14
  320. package/src/runtime/http-types.ts +0 -1
  321. package/src/runtime/message-composer-types.ts +0 -9
  322. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
  323. package/src/runtime/middleware/auth.ts +27 -3
  324. package/src/runtime/middleware/rate-limiter.ts +28 -1
  325. package/src/runtime/migrations/vbundle-builder.ts +6 -5
  326. package/src/runtime/pending-interactions.ts +20 -1
  327. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
  328. package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
  329. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
  330. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
  331. package/src/runtime/routes/approval-routes.ts +35 -8
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
  333. package/src/runtime/routes/btw-routes.ts +37 -1
  334. package/src/runtime/routes/channel-delivery-routes.ts +11 -7
  335. package/src/runtime/routes/channel-route-definitions.ts +3 -0
  336. package/src/runtime/routes/channel-route-shared.ts +3 -1
  337. package/src/runtime/routes/consolidation-routes.ts +17 -13
  338. package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
  339. package/src/runtime/routes/conversation-list-routes.ts +41 -4
  340. package/src/runtime/routes/conversation-query-routes.ts +196 -11
  341. package/src/runtime/routes/conversation-routes.ts +15 -1
  342. package/src/runtime/routes/credential-prompt-routes.ts +39 -17
  343. package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
  344. package/src/runtime/routes/heartbeat-routes.ts +18 -13
  345. package/src/runtime/routes/image-generation-routes.ts +20 -2
  346. package/src/runtime/routes/inbound-message-handler.ts +13 -12
  347. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
  348. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
  349. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
  350. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  351. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
  352. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
  353. package/src/runtime/routes/index.ts +6 -0
  354. package/src/runtime/routes/log-export/AGENTS.md +1 -1
  355. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  356. package/src/runtime/routes/migration-routes.ts +5 -9
  357. package/src/runtime/routes/plugins-routes.ts +26 -0
  358. package/src/runtime/routes/ps-routes.ts +10 -8
  359. package/src/runtime/routes/retrospective-routes.ts +235 -0
  360. package/src/runtime/routes/runs-pagination.ts +75 -0
  361. package/src/runtime/routes/schedule-routes.ts +367 -59
  362. package/src/runtime/routes/sounds-config-routes.ts +239 -0
  363. package/src/runtime/routes/surface-action-routes.ts +84 -4
  364. package/src/runtime/routes/workflow-routes.test.ts +372 -0
  365. package/src/runtime/routes/workflow-routes.ts +363 -0
  366. package/src/runtime/routes/workspace-routes.test.ts +61 -1
  367. package/src/runtime/routes/workspace-routes.ts +26 -1
  368. package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
  369. package/src/runtime/services/analyze-conversation.ts +26 -13
  370. package/src/schedule/inference-profile.ts +28 -0
  371. package/src/schedule/schedule-store.ts +152 -4
  372. package/src/schedule/scheduler-types.ts +6 -0
  373. package/src/schedule/scheduler.ts +96 -0
  374. package/src/security/secret-allowlist.ts +1 -1
  375. package/src/skills/path-classifier.ts +1 -1
  376. package/src/tools/apps/executors.ts +23 -0
  377. package/src/tools/apps/resolve-app-id.ts +42 -0
  378. package/src/tools/browser/browser-execution.ts +9 -11
  379. package/src/tools/credentials/broker.ts +4 -4
  380. package/src/tools/credentials/vault.ts +26 -137
  381. package/src/tools/executor.ts +69 -0
  382. package/src/tools/flag-gated-tools.test.ts +76 -0
  383. package/src/tools/permission-checker.ts +8 -1
  384. package/src/tools/registry.ts +51 -0
  385. package/src/tools/schedule/create.ts +77 -1
  386. package/src/tools/schedule/list.ts +1 -0
  387. package/src/tools/schedule/update.ts +73 -1
  388. package/src/tools/skills/execute.ts +56 -0
  389. package/src/tools/terminal/shell.ts +1 -1
  390. package/src/tools/ui-surface/definitions.ts +91 -2
  391. package/src/tools/workflows/manage-workflows.ts +183 -0
  392. package/src/tools/workflows/run-workflow.test.ts +442 -0
  393. package/src/tools/workflows/run-workflow.ts +88 -0
  394. package/src/types/onboarding-context.ts +2 -0
  395. package/src/usage/attribution.ts +24 -0
  396. package/src/util/canonicalize-identity.ts +12 -3
  397. package/src/util/platform.ts +17 -17
  398. package/src/watcher/__tests__/engine.test.ts +24 -0
  399. package/src/watcher/__tests__/telemetry.test.ts +135 -0
  400. package/src/watcher/engine.ts +7 -0
  401. package/src/watcher/telemetry.ts +74 -0
  402. package/src/workflows/capabilities.test.ts +365 -0
  403. package/src/workflows/capabilities.ts +359 -0
  404. package/src/workflows/deterministic-stringify.ts +27 -0
  405. package/src/workflows/engine-integration.test.ts +656 -0
  406. package/src/workflows/engine.test.ts +1144 -0
  407. package/src/workflows/engine.ts +1078 -0
  408. package/src/workflows/fanout-load.test.ts +168 -0
  409. package/src/workflows/journal-store.test.ts +369 -0
  410. package/src/workflows/journal-store.ts +470 -0
  411. package/src/workflows/leaf-runner.test.ts +704 -0
  412. package/src/workflows/leaf-runner.ts +589 -0
  413. package/src/workflows/library.test.ts +134 -0
  414. package/src/workflows/library.ts +124 -0
  415. package/src/workflows/run-manager.test.ts +711 -0
  416. package/src/workflows/run-manager.ts +593 -0
  417. package/src/workflows/sandbox-escape.test.ts +339 -0
  418. package/src/workflows/sandbox.test.ts +251 -0
  419. package/src/workflows/sandbox.ts +447 -0
  420. package/src/workspace/adaptive-thinking-repair.ts +33 -11
  421. package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
  422. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
  423. package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
  424. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
  425. package/src/workspace/migrations/031-drop-user-md.ts +1 -4
  426. package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
  427. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
  428. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
  429. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
  430. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
  431. package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
  432. package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
  433. package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
  434. package/src/workspace/migrations/registry.ts +6 -0
  435. package/src/workspace/migrations/runner.ts +1 -1
  436. package/tsconfig.json +1 -1
  437. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
  438. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
  439. package/src/__tests__/guardian-action-store.test.ts +0 -106
  440. package/src/daemon/guardian-action-generators.ts +0 -71
  441. package/src/memory/guardian-action-store.ts +0 -484
  442. package/src/runtime/guardian-action-grant-minter.ts +0 -150
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Typed persistence for the workflow orchestration engine.
3
+ *
4
+ * Two tables (created by migration 282):
5
+ *
6
+ * - `workflow_runs` — one row per orchestration run (a sandboxed script that
7
+ * spawns parallel leaf agents), tracking lifecycle status and token usage.
8
+ * - `workflow_journal` — append-only `(run_id, seq)` log of every leaf call
9
+ * (agent / host function / nested workflow). On resume after a daemon
10
+ * restart, the engine replays cached results for the unchanged call prefix
11
+ * instead of re-spawning agents.
12
+ *
13
+ * This module is pure persistence — no `workflows` feature-flag logic. Callers
14
+ * (the engine, a later PR) own gating.
15
+ */
16
+
17
+ import { rawAll, rawGet, rawRun } from "../memory/raw-query.js";
18
+
19
+ export type WorkflowRunStatus =
20
+ | "running"
21
+ | "completed"
22
+ | "failed"
23
+ | "aborted"
24
+ | "cap_exceeded"
25
+ | "interrupted";
26
+
27
+ /**
28
+ * The journal records leaf-agent calls (`"agent"`) and nested `workflow()`
29
+ * resolutions (`"workflow"`). A `"workflow"` entry snapshots the resolved child
30
+ * source under its own `seq` so a resumed run re-executes the SAME child code
31
+ * the original launch did, even if the saved workflow was since edited or
32
+ * deleted. The column stays TEXT so adding kinds later needs no migration.
33
+ */
34
+ export type WorkflowJournalKind = "agent" | "workflow";
35
+
36
+ /** A persisted workflow run row, with JSON columns parsed into values. */
37
+ export interface WorkflowRun {
38
+ id: string;
39
+ name: string | null;
40
+ scriptSource: string;
41
+ scriptHash: string;
42
+ args: unknown;
43
+ capabilities: unknown;
44
+ status: WorkflowRunStatus;
45
+ conversationId: string | null;
46
+ /**
47
+ * Originating trust context (parsed), or null for legacy rows written before
48
+ * the column existed. Reconstructed on resume so a run never resumes with more
49
+ * trust than it started under — see {@link CreateRunInput.trust}.
50
+ */
51
+ trust: unknown;
52
+ agentsSpawned: number;
53
+ inputTokens: number;
54
+ outputTokens: number;
55
+ result: unknown;
56
+ error: string | null;
57
+ createdAt: number | null;
58
+ updatedAt: number | null;
59
+ finishedAt: number | null;
60
+ }
61
+
62
+ /** A persisted journal entry, with JSON columns parsed into values. */
63
+ export interface WorkflowJournalEntry {
64
+ runId: string;
65
+ seq: number;
66
+ callHash: string;
67
+ kind: WorkflowJournalKind;
68
+ request: unknown;
69
+ result: unknown;
70
+ status: string;
71
+ createdAt: number | null;
72
+ }
73
+
74
+ export interface CreateRunInput {
75
+ id: string;
76
+ name?: string | null;
77
+ scriptSource: string;
78
+ scriptHash: string;
79
+ args?: unknown;
80
+ capabilities?: unknown;
81
+ status?: WorkflowRunStatus;
82
+ conversationId?: string | null;
83
+ /**
84
+ * Originating trust context (trust metadata — `{ sourceChannel, trustClass,
85
+ * ... }`, no secret material), serialized so a crash-orphaned run can
86
+ * reconstruct the exact trust class it started under when resumed. Omit on
87
+ * legacy callers; resume then falls back to low trust, never guardian.
88
+ */
89
+ trust?: unknown;
90
+ }
91
+
92
+ export interface UpdateRunInput {
93
+ status?: WorkflowRunStatus;
94
+ agentsSpawned?: number;
95
+ inputTokens?: number;
96
+ outputTokens?: number;
97
+ conversationId?: string | null;
98
+ }
99
+
100
+ export interface FinishRunInput {
101
+ status: WorkflowRunStatus;
102
+ result?: unknown;
103
+ error?: string | null;
104
+ }
105
+
106
+ export interface AppendJournalEntryInput {
107
+ runId: string;
108
+ seq: number;
109
+ callHash: string;
110
+ kind: WorkflowJournalKind;
111
+ request?: unknown;
112
+ result?: unknown;
113
+ status: string;
114
+ }
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // Row shapes + mappers
118
+ // ---------------------------------------------------------------------------
119
+
120
+ interface WorkflowRunRow {
121
+ id: string;
122
+ name: string | null;
123
+ script_source: string;
124
+ script_hash: string;
125
+ args_json: string | null;
126
+ capabilities_json: string | null;
127
+ status: string;
128
+ conversation_id: string | null;
129
+ trust_json: string | null;
130
+ agents_spawned: number;
131
+ input_tokens: number;
132
+ output_tokens: number;
133
+ result_json: string | null;
134
+ error: string | null;
135
+ created_at: number | null;
136
+ updated_at: number | null;
137
+ finished_at: number | null;
138
+ }
139
+
140
+ interface WorkflowJournalRow {
141
+ run_id: string;
142
+ seq: number;
143
+ call_hash: string;
144
+ kind: string;
145
+ request_json: string | null;
146
+ result_json: string | null;
147
+ status: string;
148
+ created_at: number | null;
149
+ }
150
+
151
+ /** Parse a nullable JSON column; malformed values collapse to null. */
152
+ function parseJsonColumn(value: string | null): unknown {
153
+ if (value === null) return null;
154
+ try {
155
+ return JSON.parse(value);
156
+ } catch {
157
+ return null;
158
+ }
159
+ }
160
+
161
+ /** Serialize a value for a nullable JSON column. `undefined` stores as null. */
162
+ function serializeJsonColumn(value: unknown): string | null {
163
+ return value === undefined || value === null ? null : JSON.stringify(value);
164
+ }
165
+
166
+ function rowToRun(row: WorkflowRunRow): WorkflowRun {
167
+ return {
168
+ id: row.id,
169
+ name: row.name,
170
+ scriptSource: row.script_source,
171
+ scriptHash: row.script_hash,
172
+ args: parseJsonColumn(row.args_json),
173
+ capabilities: parseJsonColumn(row.capabilities_json),
174
+ status: row.status as WorkflowRunStatus,
175
+ conversationId: row.conversation_id,
176
+ trust: parseJsonColumn(row.trust_json),
177
+ agentsSpawned: row.agents_spawned,
178
+ inputTokens: row.input_tokens,
179
+ outputTokens: row.output_tokens,
180
+ result: parseJsonColumn(row.result_json),
181
+ error: row.error,
182
+ createdAt: row.created_at,
183
+ updatedAt: row.updated_at,
184
+ finishedAt: row.finished_at,
185
+ };
186
+ }
187
+
188
+ function rowToJournalEntry(row: WorkflowJournalRow): WorkflowJournalEntry {
189
+ return {
190
+ runId: row.run_id,
191
+ seq: row.seq,
192
+ callHash: row.call_hash,
193
+ kind: row.kind as WorkflowJournalKind,
194
+ request: parseJsonColumn(row.request_json),
195
+ result: parseJsonColumn(row.result_json),
196
+ status: row.status,
197
+ createdAt: row.created_at,
198
+ };
199
+ }
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // Runs — write
203
+ // ---------------------------------------------------------------------------
204
+
205
+ /** Insert a new workflow run. Defaults status to `running`. */
206
+ export function createRun(input: CreateRunInput): WorkflowRun {
207
+ const now = Date.now();
208
+ const status = input.status ?? "running";
209
+ rawRun(
210
+ /*sql*/ `
211
+ INSERT INTO workflow_runs (
212
+ id, name, script_source, script_hash, args_json, capabilities_json,
213
+ status, conversation_id, trust_json, agents_spawned, input_tokens,
214
+ output_tokens, result_json, error, created_at, updated_at, finished_at
215
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, 0, NULL, NULL, ?, ?, NULL)
216
+ `,
217
+ input.id,
218
+ input.name ?? null,
219
+ input.scriptSource,
220
+ input.scriptHash,
221
+ serializeJsonColumn(input.args),
222
+ serializeJsonColumn(input.capabilities),
223
+ status,
224
+ input.conversationId ?? null,
225
+ serializeJsonColumn(input.trust),
226
+ now,
227
+ now,
228
+ );
229
+ return getRun(input.id)!;
230
+ }
231
+
232
+ /**
233
+ * Patch mutable fields on a run and bump `updated_at`. Only the fields present
234
+ * in `input` are written. Returns the updated run, or null if it doesn't exist.
235
+ */
236
+ export function updateRun(
237
+ runId: string,
238
+ input: UpdateRunInput,
239
+ ): WorkflowRun | null {
240
+ const sets: string[] = [];
241
+ const params: (string | number | null)[] = [];
242
+
243
+ if (input.status !== undefined) {
244
+ sets.push("status = ?");
245
+ params.push(input.status);
246
+ }
247
+ if (input.agentsSpawned !== undefined) {
248
+ sets.push("agents_spawned = ?");
249
+ params.push(input.agentsSpawned);
250
+ }
251
+ if (input.inputTokens !== undefined) {
252
+ sets.push("input_tokens = ?");
253
+ params.push(input.inputTokens);
254
+ }
255
+ if (input.outputTokens !== undefined) {
256
+ sets.push("output_tokens = ?");
257
+ params.push(input.outputTokens);
258
+ }
259
+ if (input.conversationId !== undefined) {
260
+ sets.push("conversation_id = ?");
261
+ params.push(input.conversationId);
262
+ }
263
+
264
+ sets.push("updated_at = ?");
265
+ params.push(Date.now());
266
+ params.push(runId);
267
+
268
+ rawRun(`UPDATE workflow_runs SET ${sets.join(", ")} WHERE id = ?`, ...params);
269
+ return getRun(runId);
270
+ }
271
+
272
+ /**
273
+ * Mark a run terminal: set its final status, result/error payload, and stamp
274
+ * both `updated_at` and `finished_at`. Returns the updated run, or null if it
275
+ * doesn't exist.
276
+ */
277
+ export function finishRun(
278
+ runId: string,
279
+ input: FinishRunInput,
280
+ ): WorkflowRun | null {
281
+ const now = Date.now();
282
+ rawRun(
283
+ /*sql*/ `
284
+ UPDATE workflow_runs
285
+ SET status = ?, result_json = ?, error = ?, updated_at = ?, finished_at = ?
286
+ WHERE id = ?
287
+ `,
288
+ input.status,
289
+ serializeJsonColumn(input.result),
290
+ input.error ?? null,
291
+ now,
292
+ now,
293
+ runId,
294
+ );
295
+ return getRun(runId);
296
+ }
297
+
298
+ // ---------------------------------------------------------------------------
299
+ // Journal — write
300
+ // ---------------------------------------------------------------------------
301
+
302
+ /**
303
+ * Append a leaf-call entry to a run's journal, upserting on `(run_id, seq)`.
304
+ *
305
+ * A genuine duplicate re-append (same hash/result — the crash-recovery case
306
+ * where a write replays after a crash between the call and its journal write)
307
+ * is idempotent: the conflicting row is overwritten with identical values.
308
+ *
309
+ * On resume, a leaf whose input CHANGED re-runs and produces a new
310
+ * hash/result/status at the same `seq`; the UPSERT rewrites the stale first-run
311
+ * row so the persisted journal agrees with the value the engine returned (and
312
+ * the call doesn't re-run on every future resume). `created_at` is left at its
313
+ * original value on conflict so the row keeps its first-write timestamp.
314
+ */
315
+ export function appendJournalEntry(
316
+ input: AppendJournalEntryInput,
317
+ ): WorkflowJournalEntry {
318
+ rawRun(
319
+ /*sql*/ `
320
+ INSERT INTO workflow_journal (
321
+ run_id, seq, call_hash, kind, request_json, result_json, status, created_at
322
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
323
+ ON CONFLICT(run_id, seq) DO UPDATE SET
324
+ call_hash = excluded.call_hash,
325
+ kind = excluded.kind,
326
+ request_json = excluded.request_json,
327
+ result_json = excluded.result_json,
328
+ status = excluded.status
329
+ `,
330
+ input.runId,
331
+ input.seq,
332
+ input.callHash,
333
+ input.kind,
334
+ serializeJsonColumn(input.request),
335
+ serializeJsonColumn(input.result),
336
+ input.status,
337
+ Date.now(),
338
+ );
339
+ return getJournalEntry(input.runId, input.seq)!;
340
+ }
341
+
342
+ // ---------------------------------------------------------------------------
343
+ // Runs + journal — read
344
+ // ---------------------------------------------------------------------------
345
+
346
+ /** Fetch a single run by id, or null if it doesn't exist. */
347
+ export function getRun(runId: string): WorkflowRun | null {
348
+ const row = rawGet<WorkflowRunRow>(
349
+ `SELECT * FROM workflow_runs WHERE id = ?`,
350
+ runId,
351
+ );
352
+ return row ? rowToRun(row) : null;
353
+ }
354
+
355
+ /** Fetch a run's full journal in sequence order for resume replay. */
356
+ export function getJournal(runId: string): WorkflowJournalEntry[] {
357
+ const rows = rawAll<WorkflowJournalRow>(
358
+ `SELECT * FROM workflow_journal WHERE run_id = ? ORDER BY seq ASC`,
359
+ runId,
360
+ );
361
+ return rows.map(rowToJournalEntry);
362
+ }
363
+
364
+ /** Fetch a single journal entry by its `(run_id, seq)` key, or null. */
365
+ export function getJournalEntry(
366
+ runId: string,
367
+ seq: number,
368
+ ): WorkflowJournalEntry | null {
369
+ const row = rawGet<WorkflowJournalRow>(
370
+ `SELECT * FROM workflow_journal WHERE run_id = ? AND seq = ?`,
371
+ runId,
372
+ seq,
373
+ );
374
+ return row ? rowToJournalEntry(row) : null;
375
+ }
376
+
377
+ export interface ListRunsOptions {
378
+ limit: number;
379
+ status?: WorkflowRunStatus;
380
+ }
381
+
382
+ /** List runs newest-first, optionally filtered by status. */
383
+ export function listRuns(options: ListRunsOptions): WorkflowRun[] {
384
+ const rows = options.status
385
+ ? rawAll<WorkflowRunRow>(
386
+ `SELECT * FROM workflow_runs WHERE status = ? ORDER BY created_at DESC LIMIT ?`,
387
+ options.status,
388
+ options.limit,
389
+ )
390
+ : rawAll<WorkflowRunRow>(
391
+ `SELECT * FROM workflow_runs ORDER BY created_at DESC LIMIT ?`,
392
+ options.limit,
393
+ );
394
+ return rows.map(rowToRun);
395
+ }
396
+
397
+ // ---------------------------------------------------------------------------
398
+ // Startup reconciliation
399
+ // ---------------------------------------------------------------------------
400
+
401
+ /**
402
+ * Flip every `running` run to `interrupted`. Called once at daemon startup: a
403
+ * row left in `running` means the process died mid-run (nothing else can leave
404
+ * a row `running` after the engine exits, which always finishes it). Marking it
405
+ * `interrupted` makes it eligible for an explicit resume.
406
+ *
407
+ * STATUS ONLY — `agents_spawned`/`input_tokens`/`output_tokens` are NOT touched.
408
+ * The run row is the source of truth for ACCOUNTING (it seeds the resumed run's
409
+ * cap counters) while the journal is the source of truth for REPLAY; rewriting
410
+ * the counters here would drift the cap accounting. Returns the number of rows
411
+ * reconciled.
412
+ */
413
+ export function markRunningAsInterrupted(): number {
414
+ return rawRun(
415
+ /*sql*/ `UPDATE workflow_runs SET status = 'interrupted', updated_at = ? WHERE status = 'running'`,
416
+ Date.now(),
417
+ );
418
+ }
419
+
420
+ // ---------------------------------------------------------------------------
421
+ // Retention
422
+ // ---------------------------------------------------------------------------
423
+
424
+ /**
425
+ * Set of genuinely TERMINAL run statuses — the only rows `pruneRuns` may reap.
426
+ * A run is non-terminal (and therefore retained regardless of age) when it is
427
+ * still `running` OR `interrupted`: an `interrupted` run is a crash-orphaned
428
+ * row awaiting an explicit resume, so pruning it would destroy resumability.
429
+ */
430
+ const TERMINAL_RUN_STATUSES: readonly WorkflowRunStatus[] = [
431
+ "completed",
432
+ "failed",
433
+ "aborted",
434
+ "cap_exceeded",
435
+ ];
436
+
437
+ /** SQL list literal of the terminal statuses, e.g. `'completed','failed',...`. */
438
+ const TERMINAL_RUN_STATUS_SQL = TERMINAL_RUN_STATUSES.map((s) => `'${s}'`).join(
439
+ ", ",
440
+ );
441
+
442
+ /**
443
+ * Delete TERMINAL runs older than `retentionDays` (by `created_at`) and their
444
+ * journal entries. Non-terminal runs are never pruned, regardless of age:
445
+ * `running` rows could be reaped out from under the engine, and `interrupted`
446
+ * rows are crash-orphaned and still resumable — deleting either would destroy
447
+ * an in-flight or resumable run. Returns the number of runs deleted.
448
+ */
449
+ export function pruneRuns(retentionDays: number): number {
450
+ const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
451
+ rawRun(
452
+ /*sql*/ `
453
+ DELETE FROM workflow_journal
454
+ WHERE run_id IN (
455
+ SELECT id FROM workflow_runs
456
+ WHERE status IN (${TERMINAL_RUN_STATUS_SQL})
457
+ AND created_at IS NOT NULL AND created_at < ?
458
+ )
459
+ `,
460
+ cutoff,
461
+ );
462
+ return rawRun(
463
+ /*sql*/ `
464
+ DELETE FROM workflow_runs
465
+ WHERE status IN (${TERMINAL_RUN_STATUS_SQL})
466
+ AND created_at IS NOT NULL AND created_at < ?
467
+ `,
468
+ cutoff,
469
+ );
470
+ }