@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,26 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ /**
5
+ * Add `workflow_name` + `workflow_args_json` columns to `cron_jobs`.
6
+ *
7
+ * These back the new `workflow` schedule mode, which triggers a saved
8
+ * workflow by name on a schedule. Both are nullable and only populated for
9
+ * `mode = 'workflow'` rows; existing schedules stay NULL and unchanged.
10
+ *
11
+ * Idempotent — each ALTER is wrapped so a re-run (column already present)
12
+ * is a no-op.
13
+ */
14
+ export function migrateScheduleWorkflowMode(database: DrizzleDb): void {
15
+ const raw = getSqliteFrom(database);
16
+ try {
17
+ raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_name TEXT`);
18
+ } catch {
19
+ /* Column already exists */
20
+ }
21
+ try {
22
+ raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_args_json TEXT`);
23
+ } catch {
24
+ /* Column already exists */
25
+ }
26
+ }
@@ -0,0 +1,27 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ /**
5
+ * Add a `trust_json` column to `workflow_runs`.
6
+ *
7
+ * Persists the originating {@link TrustContext} (trust metadata — NOT secret
8
+ * material) so a crash-orphaned run can reconstruct the exact trust class it
9
+ * started under when it is resumed after a restart. Without this, resume could
10
+ * only fall back to a default, and the previous fallback was the internal
11
+ * guardian context — a privilege escalation, since a run started by a low-trust
12
+ * actor would resume with the side-effect approval gate cleared.
13
+ *
14
+ * Nullable — legacy rows written before this column stay NULL and resume at the
15
+ * low-trust fallback (never guardian).
16
+ *
17
+ * Idempotent — the ALTER is wrapped so a re-run (column already present) is a
18
+ * no-op.
19
+ */
20
+ export function migrateWorkflowRunTrust(database: DrizzleDb): void {
21
+ const raw = getSqliteFrom(database);
22
+ try {
23
+ raw.exec(`ALTER TABLE workflow_runs ADD COLUMN trust_json TEXT`);
24
+ } catch {
25
+ /* Column already exists */
26
+ }
27
+ }
@@ -0,0 +1,15 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+
3
+ /**
4
+ * Add an index on `origin_channel` to support the new `originChannel` query
5
+ * parameter on `GET /v1/conversations`. Channel-based sidebar sections fetch
6
+ * conversations filtered by origin channel; without this index, each such
7
+ * query requires a full table scan.
8
+ */
9
+ export function migrateConversationOriginChannelIndex(
10
+ database: DrizzleDb,
11
+ ): void {
12
+ database.run(
13
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_conversations_origin_channel ON conversations(origin_channel)`,
14
+ );
15
+ }
@@ -0,0 +1,43 @@
1
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
+
3
+ /**
4
+ * Fully normalize `conversations.origin_channel` so no rows remain NULL.
5
+ *
6
+ * Step 1: Backfill from `external_conversation_bindings.source_channel` for
7
+ * conversations that have a channel binding but NULL origin_channel (created
8
+ * before origin_channel was consistently set on first inbound message).
9
+ *
10
+ * Step 2: Set remaining NULL rows to 'vellum'. These are native/local
11
+ * conversations created before the mechanism existed — they have no external
12
+ * binding and were always implicitly vellum-channel conversations.
13
+ */
14
+ export function migrateBackfillOriginChannelFromBindings(
15
+ database: DrizzleDb,
16
+ ): void {
17
+ const raw = getSqliteFrom(database);
18
+
19
+ // Step 1: Backfill from external bindings
20
+ raw
21
+ .query(
22
+ /*sql*/ `
23
+ UPDATE conversations
24
+ SET origin_channel = (
25
+ SELECT source_channel
26
+ FROM external_conversation_bindings
27
+ WHERE external_conversation_bindings.conversation_id = conversations.id
28
+ )
29
+ WHERE origin_channel IS NULL
30
+ AND id IN (
31
+ SELECT conversation_id FROM external_conversation_bindings
32
+ )
33
+ `,
34
+ )
35
+ .run();
36
+
37
+ // Step 2: Remaining NULL rows are native/local — set to 'vellum'
38
+ raw
39
+ .query(
40
+ /*sql*/ `UPDATE conversations SET origin_channel = 'vellum' WHERE origin_channel IS NULL`,
41
+ )
42
+ .run();
43
+ }
@@ -0,0 +1,115 @@
1
+ import { getLogger } from "../../util/logger.js";
2
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
3
+
4
+ const log = getLogger("migration-289");
5
+
6
+ /**
7
+ * Deduplicates historical case collisions in contact_channels.
8
+ *
9
+ * Pure dedup — removes duplicate rows but does not change any stored values.
10
+ *
11
+ * Steps:
12
+ * 1. Deduplicate by (type, address) case-insensitively — keeps the best row.
13
+ * 2. Deduplicate by (type, external_user_id) case-insensitively — ensures
14
+ * at most one row per external identity.
15
+ * 3. Remove cross-column collision blockers — rows with NULL external_user_id
16
+ * whose address equals another row's external_user_id.
17
+ */
18
+ export function migrateContactChannelsUniqueExtUser(database: DrizzleDb): void {
19
+ const raw = getSqliteFrom(database);
20
+
21
+ // Step 1: Deduplicate historical case collisions. After this, the existing
22
+ // case-sensitive UNIQUE(type, address) constraint remains valid because
23
+ // only one row per case-insensitive group survives.
24
+ const addressDedupResult = raw.run(/*sql*/ `
25
+ DELETE FROM contact_channels
26
+ WHERE id NOT IN (
27
+ SELECT id FROM (
28
+ SELECT id,
29
+ ROW_NUMBER() OVER (
30
+ PARTITION BY type, address COLLATE NOCASE
31
+ ORDER BY
32
+ CASE status
33
+ WHEN 'blocked' THEN 0
34
+ WHEN 'revoked' THEN 1
35
+ WHEN 'active' THEN 2
36
+ WHEN 'unverified' THEN 3
37
+ ELSE 4
38
+ END,
39
+ updated_at DESC
40
+ ) AS rn
41
+ FROM contact_channels
42
+ )
43
+ WHERE rn = 1
44
+ )
45
+ `);
46
+
47
+ if (addressDedupResult.changes > 0) {
48
+ log.info(
49
+ { rowsDeleted: addressDedupResult.changes },
50
+ "Deduplicated contact_channels by (type, address) case-insensitive",
51
+ );
52
+ }
53
+
54
+ // Step 2: Deduplicate by (type, external_user_id) so that a future
55
+ // normalization (SET address = external_user_id) cannot produce collisions.
56
+ // Two rows with different addresses but the same external_user_id would
57
+ // both get the same address after normalization.
58
+ const extIdDedupResult = raw.run(/*sql*/ `
59
+ DELETE FROM contact_channels
60
+ WHERE external_user_id IS NOT NULL
61
+ AND id NOT IN (
62
+ SELECT id FROM (
63
+ SELECT id,
64
+ ROW_NUMBER() OVER (
65
+ PARTITION BY type, external_user_id COLLATE NOCASE
66
+ ORDER BY
67
+ CASE status
68
+ WHEN 'blocked' THEN 0
69
+ WHEN 'revoked' THEN 1
70
+ WHEN 'active' THEN 2
71
+ WHEN 'unverified' THEN 3
72
+ ELSE 4
73
+ END,
74
+ updated_at DESC
75
+ ) AS rn
76
+ FROM contact_channels
77
+ WHERE external_user_id IS NOT NULL
78
+ )
79
+ WHERE rn = 1
80
+ )
81
+ `);
82
+
83
+ if (extIdDedupResult.changes > 0) {
84
+ log.info(
85
+ { rowsDeleted: extIdDedupResult.changes },
86
+ "Deduplicated contact_channels by (type, external_user_id)",
87
+ );
88
+ }
89
+
90
+ // Step 3: Remove rows that would block future normalization due to
91
+ // cross-column collisions. A row with NULL external_user_id whose address
92
+ // equals another row's external_user_id would prevent that row's
93
+ // normalization in a later migration.
94
+ const crossColResult = raw.run(/*sql*/ `
95
+ DELETE FROM contact_channels
96
+ WHERE external_user_id IS NULL
97
+ AND id IN (
98
+ SELECT blocker.id
99
+ FROM contact_channels AS blocker
100
+ INNER JOIN contact_channels AS normalizer
101
+ ON normalizer.type = blocker.type
102
+ AND normalizer.external_user_id = blocker.address COLLATE NOCASE
103
+ AND normalizer.address != normalizer.external_user_id
104
+ AND normalizer.external_user_id IS NOT NULL
105
+ AND normalizer.id != blocker.id
106
+ )
107
+ `);
108
+
109
+ if (crossColResult.changes > 0) {
110
+ log.info(
111
+ { rowsDeleted: crossColResult.changes },
112
+ "Removed cross-column collision blockers",
113
+ );
114
+ }
115
+ }
@@ -0,0 +1,77 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { describe, expect, test } from "bun:test";
3
+
4
+ import { drizzle } from "drizzle-orm/bun-sqlite";
5
+
6
+ import * as schema from "../schema.js";
7
+ import { migrateScheduleCapabilities } from "./290-schedule-capabilities.js";
8
+
9
+ function createTestDb() {
10
+ const sqlite = new Database(":memory:");
11
+ // Pre-290 shape: no capabilities_json column (trimmed to the columns the
12
+ // migration and assertions touch).
13
+ sqlite.exec(/*sql*/ `
14
+ CREATE TABLE cron_jobs (
15
+ id TEXT PRIMARY KEY,
16
+ name TEXT NOT NULL,
17
+ message TEXT NOT NULL,
18
+ next_run_at INTEGER NOT NULL,
19
+ created_by TEXT NOT NULL,
20
+ created_at INTEGER NOT NULL,
21
+ updated_at INTEGER NOT NULL
22
+ )
23
+ `);
24
+ return { sqlite, db: drizzle(sqlite, { schema }) };
25
+ }
26
+
27
+ function columnNames(sqlite: Database): string[] {
28
+ return (
29
+ sqlite.query("PRAGMA table_info(cron_jobs)").all() as Array<{
30
+ name: string;
31
+ }>
32
+ ).map((c) => c.name);
33
+ }
34
+
35
+ describe("migration 290: cron_jobs capabilities_json", () => {
36
+ test("adds a nullable capabilities_json column", () => {
37
+ const { sqlite, db } = createTestDb();
38
+ expect(columnNames(sqlite)).not.toContain("capabilities_json");
39
+
40
+ migrateScheduleCapabilities(db);
41
+
42
+ const column = (
43
+ sqlite.query("PRAGMA table_info(cron_jobs)").all() as Array<{
44
+ name: string;
45
+ notnull: number;
46
+ }>
47
+ ).find((c) => c.name === "capabilities_json");
48
+ expect(column).toBeDefined();
49
+ expect(column?.notnull).toBe(0);
50
+ });
51
+
52
+ test("existing rows read back with capabilities_json null", () => {
53
+ const { sqlite, db } = createTestDb();
54
+ sqlite.exec(/*sql*/ `
55
+ INSERT INTO cron_jobs (id, name, message, next_run_at, created_by, created_at, updated_at)
56
+ VALUES ('job-1', 'Daily digest', 'write the digest', 1000, 'agent', 1000, 1000)
57
+ `);
58
+
59
+ migrateScheduleCapabilities(db);
60
+
61
+ const row = sqlite
62
+ .query("SELECT capabilities_json FROM cron_jobs WHERE id = 'job-1'")
63
+ .get() as { capabilities_json: string | null };
64
+ expect(row.capabilities_json).toBeNull();
65
+ });
66
+
67
+ test("is idempotent — re-run is a no-op", () => {
68
+ const { sqlite, db } = createTestDb();
69
+
70
+ migrateScheduleCapabilities(db);
71
+ expect(() => migrateScheduleCapabilities(db)).not.toThrow();
72
+
73
+ expect(
74
+ columnNames(sqlite).filter((name) => name === "capabilities_json"),
75
+ ).toHaveLength(1);
76
+ });
77
+ });
@@ -0,0 +1,25 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ /**
5
+ * Add a nullable `capabilities_json TEXT` column to `cron_jobs`.
6
+ *
7
+ * Persists the capability manifest a scheduled workflow run should execute
8
+ * under. `NULL` (all pre-existing rows) means no persisted manifest — runs
9
+ * fall back to the hardcoded read-only manifest.
10
+ *
11
+ * Idempotent — the PRAGMA guard makes re-running a no-op once the column
12
+ * exists.
13
+ */
14
+ export function migrateScheduleCapabilities(database: DrizzleDb): void {
15
+ const raw = getSqliteFrom(database);
16
+
17
+ const columns = raw.query(`PRAGMA table_info(cron_jobs)`).all() as Array<{
18
+ name: string;
19
+ }>;
20
+ const columnNames = new Set(columns.map((c) => c.name));
21
+
22
+ if (!columnNames.has("capabilities_json")) {
23
+ raw.exec(`ALTER TABLE cron_jobs ADD COLUMN capabilities_json TEXT`);
24
+ }
25
+ }
@@ -0,0 +1,96 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { describe, expect, test } from "bun:test";
3
+
4
+ import { drizzle } from "drizzle-orm/bun-sqlite";
5
+
6
+ import { getSqliteFrom } from "../../db-connection.js";
7
+ import * as schema from "../../schema.js";
8
+ import { migrateMemoryRetrospectiveState } from "../245-memory-retrospective-state.js";
9
+ import { migrateMemoryRetrospectiveRememberedLog } from "../281-memory-retrospective-remembered-log.js";
10
+
11
+ interface ColumnRow {
12
+ name: string;
13
+ type: string;
14
+ notnull: number;
15
+ dflt_value: string | null;
16
+ pk: number;
17
+ }
18
+
19
+ function createTestDb() {
20
+ const sqlite = new Database(":memory:");
21
+ sqlite.exec("PRAGMA journal_mode=WAL");
22
+ sqlite.exec("PRAGMA foreign_keys = ON");
23
+ // `withCrashRecovery` (used by the migrations) reads/writes a
24
+ // `memory_checkpoints` table. Seed a minimal version so the migrations'
25
+ // structural setup can be exercised without booting the entire
26
+ // db-init pipeline.
27
+ sqlite.exec(`
28
+ CREATE TABLE memory_checkpoints (
29
+ key TEXT PRIMARY KEY,
30
+ value TEXT NOT NULL,
31
+ updated_at INTEGER NOT NULL
32
+ )
33
+ `);
34
+ // The state table FK references `conversations(id)`. Minimal stand-in.
35
+ sqlite.exec(`
36
+ CREATE TABLE conversations (
37
+ id TEXT PRIMARY KEY,
38
+ created_at INTEGER NOT NULL
39
+ )
40
+ `);
41
+ return drizzle(sqlite, { schema });
42
+ }
43
+
44
+ describe("migration 281 — memory_retrospective_state.remembered_log", () => {
45
+ test("adds a nullable remembered_log TEXT column", () => {
46
+ const db = createTestDb();
47
+ const raw = getSqliteFrom(db);
48
+
49
+ migrateMemoryRetrospectiveState(db);
50
+ migrateMemoryRetrospectiveRememberedLog(db);
51
+
52
+ const cols = raw
53
+ .query(`PRAGMA table_info(memory_retrospective_state)`)
54
+ .all() as ColumnRow[];
55
+ const col = cols.find((c) => c.name === "remembered_log");
56
+
57
+ expect(col).toBeDefined();
58
+ expect(col!.type).toBe("TEXT");
59
+ expect(col!.notnull).toBe(0);
60
+ expect(col!.pk).toBe(0);
61
+ });
62
+
63
+ test("is idempotent — running twice does not throw or duplicate the column", () => {
64
+ const db = createTestDb();
65
+ const raw = getSqliteFrom(db);
66
+
67
+ migrateMemoryRetrospectiveState(db);
68
+ migrateMemoryRetrospectiveRememberedLog(db);
69
+ expect(() => migrateMemoryRetrospectiveRememberedLog(db)).not.toThrow();
70
+
71
+ const cols = raw
72
+ .query(`PRAGMA table_info(memory_retrospective_state)`)
73
+ .all() as ColumnRow[];
74
+ expect(cols.filter((c) => c.name === "remembered_log")).toHaveLength(1);
75
+ });
76
+
77
+ test("pre-existing rows get NULL for the new column", () => {
78
+ const db = createTestDb();
79
+ const raw = getSqliteFrom(db);
80
+
81
+ migrateMemoryRetrospectiveState(db);
82
+ raw.exec(`INSERT INTO conversations (id, created_at) VALUES ('c1', 0)`);
83
+ raw.exec(
84
+ `INSERT INTO memory_retrospective_state (conversation_id, last_processed_message_id, last_run_at) VALUES ('c1', 'm1', 1000)`,
85
+ );
86
+
87
+ migrateMemoryRetrospectiveRememberedLog(db);
88
+
89
+ const row = raw
90
+ .query(
91
+ `SELECT remembered_log FROM memory_retrospective_state WHERE conversation_id = 'c1'`,
92
+ )
93
+ .get() as { remembered_log: string | null };
94
+ expect(row.remembered_log).toBeNull();
95
+ });
96
+ });