@vellumai/assistant 0.8.1 → 0.8.3

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 (630) hide show
  1. package/ARCHITECTURE.md +13 -19
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +17 -0
  5. package/docker-init-apt-root.sh +167 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +642 -5
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
  21. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  23. package/src/__tests__/anthropic-provider.test.ts +45 -0
  24. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  25. package/src/__tests__/app-executors.test.ts +220 -4
  26. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  27. package/src/__tests__/bundled-asset.test.ts +6 -6
  28. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  29. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  30. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  31. package/src/__tests__/clawhub.test.ts +75 -16
  32. package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
  33. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  34. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  35. package/src/__tests__/config-schema.test.ts +21 -0
  36. package/src/__tests__/config-set-route.test.ts +80 -0
  37. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  38. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  39. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  40. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  41. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  42. package/src/__tests__/context-token-estimator.test.ts +31 -65
  43. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  44. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  45. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  46. package/src/__tests__/conversation-agent-loop.test.ts +59 -1
  47. package/src/__tests__/conversation-error.test.ts +42 -3
  48. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  49. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  50. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  51. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  52. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  53. package/src/__tests__/conversation-pairing.test.ts +54 -0
  54. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  55. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  56. package/src/__tests__/conversation-queue.test.ts +4 -1
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
  58. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  59. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  60. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  61. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  64. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  65. package/src/__tests__/date-context.test.ts +45 -0
  66. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  67. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  68. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  69. package/src/__tests__/dm-backfill.test.ts +121 -10
  70. package/src/__tests__/document-tool-security.test.ts +258 -0
  71. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  72. package/src/__tests__/edit-propagation.test.ts +33 -0
  73. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  74. package/src/__tests__/external-plugin-loader.test.ts +151 -55
  75. package/src/__tests__/filing-service.test.ts +140 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  77. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  78. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  79. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  80. package/src/__tests__/heartbeat-service.test.ts +24 -164
  81. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  82. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  83. package/src/__tests__/helpers/wait-for.ts +21 -0
  84. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  85. package/src/__tests__/history-repair.test.ts +73 -0
  86. package/src/__tests__/host-app-control-proxy.test.ts +507 -10
  87. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  88. package/src/__tests__/image-credentials.test.ts +1 -1
  89. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  90. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  91. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  92. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  93. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  94. package/src/__tests__/injector-background-turn.test.ts +153 -0
  95. package/src/__tests__/injector-chain.test.ts +15 -8
  96. package/src/__tests__/install-skill-routing.test.ts +155 -37
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
  98. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  99. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  100. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  101. package/src/__tests__/llm-catalog-parity.test.ts +58 -13
  102. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  103. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  104. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
  105. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  106. package/src/__tests__/llm-resolver.test.ts +255 -2
  107. package/src/__tests__/llm-usage-store.test.ts +114 -0
  108. package/src/__tests__/managed-profile-guard.test.ts +41 -29
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  110. package/src/__tests__/managed-store.test.ts +84 -192
  111. package/src/__tests__/media-generate-image.test.ts +1 -1
  112. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  113. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  114. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  115. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  116. package/src/__tests__/notification-deep-link.test.ts +15 -0
  117. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  118. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  119. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  120. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  121. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  122. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  123. package/src/__tests__/openai-provider.test.ts +242 -3
  124. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  125. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  126. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  127. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  128. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  129. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
  130. package/src/__tests__/platform.test.ts +2 -0
  131. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  132. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  133. package/src/__tests__/plugin-external-api.test.ts +68 -0
  134. package/src/__tests__/plugin-registry.test.ts +0 -77
  135. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  136. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  137. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  138. package/src/__tests__/plugin-types.test.ts +3 -13
  139. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  140. package/src/__tests__/process-message-display-content.test.ts +421 -0
  141. package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  143. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
  144. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  145. package/src/__tests__/schedule-routes.test.ts +50 -3
  146. package/src/__tests__/schedule-store.test.ts +94 -0
  147. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  148. package/src/__tests__/schema-transforms.test.ts +20 -0
  149. package/src/__tests__/search-skills-unified.test.ts +0 -5
  150. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
  151. package/src/__tests__/server-history-render.test.ts +43 -0
  152. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  153. package/src/__tests__/skill-load-tool.test.ts +27 -89
  154. package/src/__tests__/skill-memory.test.ts +23 -3
  155. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  156. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  157. package/src/__tests__/skills-install-extract.test.ts +49 -38
  158. package/src/__tests__/skills-install-staging.test.ts +159 -0
  159. package/src/__tests__/skills-uninstall.test.ts +9 -41
  160. package/src/__tests__/skills.test.ts +51 -58
  161. package/src/__tests__/slack-channel-config.test.ts +9 -0
  162. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  163. package/src/__tests__/system-prompt.test.ts +670 -63
  164. package/src/__tests__/terminal-tools.test.ts +28 -1
  165. package/src/__tests__/thread-backfill.test.ts +557 -27
  166. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  167. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  168. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  169. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  170. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  171. package/src/__tests__/tool-executor.test.ts +16 -4
  172. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  173. package/src/__tests__/turn-events-store.test.ts +256 -0
  174. package/src/__tests__/twilio-routes.test.ts +4 -0
  175. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  176. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  177. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  178. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  179. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  180. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  181. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  182. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  183. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  184. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  185. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  186. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  187. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  188. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  189. package/src/a2a/__tests__/task-store.test.ts +246 -0
  190. package/src/a2a/agent-card.ts +58 -0
  191. package/src/a2a/feature-gate.ts +8 -0
  192. package/src/a2a/protocol-constants.ts +21 -0
  193. package/src/a2a/protocol-errors.ts +50 -0
  194. package/src/a2a/protocol-types.ts +162 -0
  195. package/src/a2a/task-store.ts +168 -0
  196. package/src/acp/resolve-agent.ts +1 -1
  197. package/src/agent/image-optimize.ts +13 -5
  198. package/src/agent/loop.ts +167 -18
  199. package/src/calls/voice-session-bridge.ts +61 -42
  200. package/src/channels/config.ts +9 -0
  201. package/src/channels/types.ts +122 -0
  202. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  203. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  204. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  205. package/src/cli/commands/__tests__/schedules.test.ts +960 -0
  206. package/src/cli/commands/changelog.ts +106 -42
  207. package/src/cli/commands/conversations.ts +102 -17
  208. package/src/cli/commands/default-action.ts +10 -53
  209. package/src/cli/commands/notifications.ts +388 -346
  210. package/src/cli/commands/plugins.ts +252 -0
  211. package/src/cli/commands/schedules.ts +683 -0
  212. package/src/cli/commands/telemetry.ts +40 -0
  213. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  214. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  215. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  216. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  217. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  218. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  219. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  220. package/src/cli/lib/cli-colors.ts +12 -0
  221. package/src/cli/lib/confirm-prompt.ts +79 -0
  222. package/src/cli/lib/install-from-github.ts +303 -0
  223. package/src/cli/lib/list-installed-plugins.ts +137 -0
  224. package/src/cli/lib/search-plugins.ts +163 -0
  225. package/src/cli/lib/uninstall-plugin.ts +82 -0
  226. package/src/cli/lib/unknown-command.ts +111 -0
  227. package/src/cli/program.ts +52 -2
  228. package/src/config/assistant-feature-flags.ts +24 -54
  229. package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
  230. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  231. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  232. package/src/config/bundled-skills/document/SKILL.md +23 -3
  233. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  234. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  235. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  236. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  237. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  238. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  239. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  240. package/src/config/bundled-tool-registry.ts +6 -0
  241. package/src/config/call-site-defaults.ts +105 -0
  242. package/src/config/feature-flag-registry.json +41 -9
  243. package/src/config/llm-resolver.ts +52 -1
  244. package/src/config/loader.ts +64 -38
  245. package/src/config/schema.ts +9 -10
  246. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  247. package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
  248. package/src/config/schemas/channels.ts +17 -0
  249. package/src/config/schemas/compaction.ts +28 -0
  250. package/src/config/schemas/conversations.ts +10 -0
  251. package/src/config/schemas/heartbeat.ts +23 -0
  252. package/src/config/schemas/llm-request-logs.ts +31 -7
  253. package/src/config/schemas/llm.ts +1 -0
  254. package/src/config/schemas/memory-retrieval.ts +18 -0
  255. package/src/config/schemas/memory-retrospective.ts +1 -1
  256. package/src/config/schemas/memory-v2.ts +4 -4
  257. package/src/config/schemas/memory.ts +3 -1
  258. package/src/config/schemas/tools.ts +14 -0
  259. package/src/config/seed-inference-profiles.ts +99 -29
  260. package/src/config/skills.ts +3 -96
  261. package/src/context/compactor.ts +1107 -0
  262. package/src/context/token-estimator.ts +34 -36
  263. package/src/context/window-manager.ts +197 -1520
  264. package/src/credential-execution/managed-catalog.ts +37 -0
  265. package/src/credential-health/credential-health-service.ts +280 -19
  266. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
  267. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  268. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  269. package/src/daemon/approval-generators.ts +8 -6
  270. package/src/daemon/config-watcher.ts +94 -31
  271. package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
  272. package/src/daemon/conversation-agent-loop.ts +198 -11
  273. package/src/daemon/conversation-error.ts +171 -37
  274. package/src/daemon/conversation-lifecycle.ts +53 -40
  275. package/src/daemon/conversation-messaging.ts +25 -6
  276. package/src/daemon/conversation-process.ts +49 -12
  277. package/src/daemon/conversation-runtime-assembly.ts +25 -1
  278. package/src/daemon/conversation-slash.ts +12 -5
  279. package/src/daemon/conversation-store.ts +11 -4
  280. package/src/daemon/conversation-tool-setup.ts +39 -7
  281. package/src/daemon/conversation.ts +33 -8
  282. package/src/daemon/date-context.ts +40 -0
  283. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  284. package/src/daemon/first-greeting.ts +22 -2
  285. package/src/daemon/guardian-action-generators.ts +1 -125
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  287. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  288. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  289. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  290. package/src/daemon/handlers/config-a2a.ts +289 -0
  291. package/src/daemon/handlers/config-model.ts +6 -5
  292. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  293. package/src/daemon/handlers/conversations.ts +1 -0
  294. package/src/daemon/handlers/shared.ts +14 -5
  295. package/src/daemon/handlers/skills.ts +111 -108
  296. package/src/daemon/history-repair.ts +28 -1
  297. package/src/daemon/host-app-control-proxy.ts +153 -27
  298. package/src/daemon/host-proxy-preactivation.ts +85 -18
  299. package/src/daemon/lifecycle.ts +89 -91
  300. package/src/daemon/meet-host-supervisor.ts +5 -4
  301. package/src/daemon/memory-v2-startup.ts +85 -0
  302. package/src/daemon/message-protocol.ts +1 -0
  303. package/src/daemon/message-types/conversations.ts +25 -0
  304. package/src/daemon/message-types/messages.ts +61 -0
  305. package/src/daemon/message-types/notifications.ts +21 -0
  306. package/src/daemon/message-types/subagents.ts +1 -0
  307. package/src/daemon/message-types/sync.ts +1 -0
  308. package/src/daemon/pkb-reminder-builder.test.ts +11 -54
  309. package/src/daemon/pkb-reminder-builder.ts +5 -20
  310. package/src/daemon/plugin-source-watcher.ts +146 -0
  311. package/src/daemon/process-message.ts +24 -3
  312. package/src/daemon/server.ts +11 -2
  313. package/src/daemon/skill-memory-refresh.ts +33 -0
  314. package/src/daemon/wake-target-adapter.ts +2 -0
  315. package/src/documents/document-store.ts +221 -3
  316. package/src/embedded/plugin-api.ts +40 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  318. package/src/export/transcript-formatter.ts +54 -20
  319. package/src/filing/filing-service.ts +39 -0
  320. package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
  321. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  322. package/src/heartbeat/heartbeat-service.ts +73 -189
  323. package/src/home/__tests__/feed-types.test.ts +80 -0
  324. package/src/home/feed-types.ts +36 -2
  325. package/src/home/post-connect-feed.ts +1 -0
  326. package/src/index.ts +18 -1
  327. package/src/ipc/cli-client.ts +147 -45
  328. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  329. package/src/mcp/client.ts +20 -4
  330. package/src/media/image-credentials.ts +3 -3
  331. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  332. package/src/memory/__tests__/conversation-queries.test.ts +483 -0
  333. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  334. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  335. package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
  336. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  337. package/src/memory/__tests__/message-content.test.ts +35 -0
  338. package/src/memory/bookmark-crud.ts +42 -10
  339. package/src/memory/context-search/sources/conversations.ts +62 -2
  340. package/src/memory/context-search/sources/workspace.ts +4 -0
  341. package/src/memory/conversation-crud.ts +63 -19
  342. package/src/memory/conversation-queries.ts +197 -11
  343. package/src/memory/conversation-title-service.ts +26 -4
  344. package/src/memory/db-init.ts +12 -0
  345. package/src/memory/delivery-crud.ts +152 -5
  346. package/src/memory/embedding-backend.ts +4 -4
  347. package/src/memory/external-conversation-store.ts +66 -5
  348. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
  349. package/src/memory/graph/conversation-graph-memory.ts +49 -21
  350. package/src/memory/graph/tools.ts +9 -40
  351. package/src/memory/indexer.ts +34 -29
  352. package/src/memory/invite-store.ts +53 -0
  353. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  354. package/src/memory/jobs/embed-concept-page.ts +20 -11
  355. package/src/memory/jobs-worker.ts +6 -1
  356. package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
  357. package/src/memory/llm-request-log-source.ts +19 -52
  358. package/src/memory/llm-request-log-store.ts +92 -1
  359. package/src/memory/llm-usage-store.ts +125 -5
  360. package/src/memory/memory-retrospective-enqueue.ts +1 -20
  361. package/src/memory/memory-retrospective-job.ts +33 -6
  362. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  363. package/src/memory/message-content.ts +1 -1
  364. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  365. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  366. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  367. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  368. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  369. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  370. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  371. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  372. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  373. package/src/memory/migrations/index.ts +9 -0
  374. package/src/memory/migrations/registry.ts +16 -0
  375. package/src/memory/onboarding-events-store.ts +106 -0
  376. package/src/memory/schema/a2a.ts +15 -0
  377. package/src/memory/schema/bookmarks.ts +0 -2
  378. package/src/memory/schema/calls.ts +1 -0
  379. package/src/memory/schema/index.ts +1 -0
  380. package/src/memory/schema/inference.ts +3 -3
  381. package/src/memory/schema/infrastructure.ts +13 -0
  382. package/src/memory/turn-events-store.ts +127 -2
  383. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  384. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  385. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  386. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  387. package/src/memory/v2/__tests__/injection.test.ts +288 -11
  388. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  389. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  390. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  391. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  392. package/src/memory/v2/__tests__/router.test.ts +15 -0
  393. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  394. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  395. package/src/memory/v2/activation-store.ts +14 -16
  396. package/src/memory/v2/cli-command-content.ts +19 -0
  397. package/src/memory/v2/cli-command-store.ts +304 -0
  398. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  399. package/src/memory/v2/injection.ts +81 -26
  400. package/src/memory/v2/migration.ts +49 -19
  401. package/src/memory/v2/page-index.ts +63 -8
  402. package/src/memory/v2/prompts/router.ts +11 -8
  403. package/src/memory/v2/prompts/sweep.ts +2 -2
  404. package/src/memory/v2/qdrant.ts +135 -7
  405. package/src/memory/v2/router.ts +9 -8
  406. package/src/memory/v2/skill-store.ts +120 -35
  407. package/src/memory/v2/static-context.ts +4 -4
  408. package/src/memory/v2/types.ts +23 -0
  409. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  410. package/src/messaging/providers/a2a/deliver.ts +156 -0
  411. package/src/messaging/providers/gmail/client.ts +9 -2
  412. package/src/messaging/providers/index.ts +11 -2
  413. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  414. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  415. package/src/messaging/providers/slack/adapter.ts +43 -5
  416. package/src/messaging/providers/slack/client.ts +27 -0
  417. package/src/messaging/providers/slack/deep-link.ts +65 -0
  418. package/src/messaging/providers/slack/download.ts +104 -0
  419. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  420. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  421. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  422. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  423. package/src/messaging/providers/slack/types.ts +20 -1
  424. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  425. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  426. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  427. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  428. package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
  429. package/src/notifications/adapters/macos.ts +12 -2
  430. package/src/notifications/broadcaster.ts +29 -4
  431. package/src/notifications/conversation-pairing.ts +2 -1
  432. package/src/notifications/copy-composer.ts +17 -64
  433. package/src/notifications/decision-engine.ts +113 -45
  434. package/src/notifications/deterministic-checks.ts +96 -0
  435. package/src/notifications/emit-signal.ts +21 -1
  436. package/src/notifications/home-feed-side-effect.ts +138 -5
  437. package/src/notifications/signal.ts +3 -5
  438. package/src/notifications/types.ts +8 -0
  439. package/src/oauth/connection-resolver.ts +8 -4
  440. package/src/oauth/platform-connection.test.ts +43 -3
  441. package/src/oauth/platform-connection.ts +19 -6
  442. package/src/oauth/seed-providers.ts +10 -1
  443. package/src/permissions/checker.ts +2 -0
  444. package/src/permissions/ipc-risk-types.ts +1 -0
  445. package/src/permissions/question-prompter.test.ts +416 -0
  446. package/src/permissions/question-prompter.ts +294 -0
  447. package/src/platform/client.test.ts +1 -1
  448. package/src/platform/client.ts +1 -1
  449. package/src/plugin-api/constants.ts +26 -0
  450. package/src/plugin-api/index.ts +34 -1
  451. package/src/plugin-api/types.ts +104 -22
  452. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  453. package/src/plugins/defaults/compaction.ts +0 -4
  454. package/src/plugins/defaults/empty-response.ts +0 -2
  455. package/src/plugins/defaults/history-repair.ts +0 -2
  456. package/src/plugins/defaults/injectors.ts +74 -22
  457. package/src/plugins/defaults/llm-call.ts +0 -2
  458. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  459. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  460. package/src/plugins/defaults/persistence.ts +0 -2
  461. package/src/plugins/defaults/title-generate.ts +0 -5
  462. package/src/plugins/defaults/token-estimate.ts +0 -2
  463. package/src/plugins/defaults/tool-error.ts +0 -7
  464. package/src/plugins/defaults/tool-execute.ts +0 -2
  465. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  466. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  467. package/src/plugins/external-api.ts +104 -0
  468. package/src/plugins/external-plugin-loader.ts +187 -42
  469. package/src/plugins/feature-gate.ts +22 -0
  470. package/src/plugins/pipeline.ts +37 -0
  471. package/src/plugins/registry.ts +48 -80
  472. package/src/plugins/types.ts +40 -26
  473. package/src/plugins/user-loader.ts +21 -2
  474. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  475. package/src/proactive-artifact/job.test.ts +37 -5
  476. package/src/prompts/__tests__/system-prompt.test.ts +10 -43
  477. package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
  478. package/src/prompts/normalize-onboarding.ts +27 -0
  479. package/src/prompts/sections.ts +302 -0
  480. package/src/prompts/system-prompt.ts +63 -174
  481. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  482. package/src/prompts/templates/system-sections.ts +164 -0
  483. package/src/providers/__tests__/inference.test.ts +24 -7
  484. package/src/providers/anthropic/client.ts +28 -28
  485. package/src/providers/call-site-routing.ts +24 -6
  486. package/src/providers/connection-resolution.ts +68 -11
  487. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  488. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  489. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  490. package/src/providers/inference/adapter-factory.ts +32 -6
  491. package/src/providers/inference/auth.ts +12 -0
  492. package/src/providers/inference/backfill.ts +14 -1
  493. package/src/providers/inference/connections.ts +159 -34
  494. package/src/providers/inference/resolve-auth.ts +14 -4
  495. package/src/providers/model-catalog.ts +249 -12
  496. package/src/providers/model-intents.ts +3 -3
  497. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  498. package/src/providers/openai/chat-completions-provider.ts +169 -8
  499. package/src/providers/openrouter/client.ts +49 -4
  500. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
  501. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  502. package/src/providers/provider-availability.ts +17 -2
  503. package/src/providers/provider-catalog-visibility.ts +38 -0
  504. package/src/providers/provider-send-message.ts +27 -12
  505. package/src/providers/registry.ts +52 -15
  506. package/src/providers/retry.ts +47 -1
  507. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  508. package/src/runtime/agent-wake.ts +103 -15
  509. package/src/runtime/auth/route-policy.ts +21 -1
  510. package/src/runtime/btw-sidechain.ts +2 -0
  511. package/src/runtime/http-server.ts +7 -16
  512. package/src/runtime/http-types.ts +19 -47
  513. package/src/runtime/migrations/origin-mode.ts +1 -1
  514. package/src/runtime/pending-interactions.ts +1 -0
  515. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  516. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  517. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  518. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
  519. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  520. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  521. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  522. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  523. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  524. package/src/runtime/routes/acp-routes.ts +5 -3
  525. package/src/runtime/routes/auth-routes.ts +1 -1
  526. package/src/runtime/routes/bookmark-routes.ts +5 -3
  527. package/src/runtime/routes/btw-routes.ts +5 -1
  528. package/src/runtime/routes/channel-availability-routes.ts +126 -0
  529. package/src/runtime/routes/consolidation-routes.ts +100 -0
  530. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  531. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  532. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  533. package/src/runtime/routes/conversation-query-routes.ts +99 -35
  534. package/src/runtime/routes/conversation-routes.ts +97 -11
  535. package/src/runtime/routes/documents-routes.ts +25 -86
  536. package/src/runtime/routes/group-routes.ts +5 -0
  537. package/src/runtime/routes/inbound-conversation.ts +28 -8
  538. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  539. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  540. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  541. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  542. package/src/runtime/routes/index.ts +8 -0
  543. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  544. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  545. package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
  546. package/src/runtime/routes/integrations/a2a.ts +235 -0
  547. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  548. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  549. package/src/runtime/routes/integrations/twilio.ts +6 -13
  550. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  551. package/src/runtime/routes/notification-routes.ts +1 -1
  552. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  553. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  554. package/src/runtime/routes/question-routes.ts +259 -0
  555. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  556. package/src/runtime/routes/schedule-routes.ts +4 -7
  557. package/src/runtime/routes/subagents-routes.ts +98 -18
  558. package/src/runtime/routes/telemetry-routes.ts +27 -0
  559. package/src/runtime/routes/tts-routes.ts +27 -2
  560. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  561. package/src/runtime/routes/workspace-routes.ts +28 -0
  562. package/src/runtime/services/conversation-serializer.ts +39 -7
  563. package/src/runtime/sync/resource-sync-events.ts +93 -1
  564. package/src/schedule/schedule-store.ts +27 -2
  565. package/src/schedule/scheduler.ts +9 -1
  566. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  567. package/src/security/untrusted-content.ts +93 -8
  568. package/src/skills/catalog-files.ts +1 -1
  569. package/src/skills/catalog-install.ts +233 -116
  570. package/src/skills/clawhub.ts +70 -13
  571. package/src/skills/managed-store.ts +4 -119
  572. package/src/skills/skillssh-registry.ts +27 -48
  573. package/src/subagent/manager.ts +17 -7
  574. package/src/telemetry/types.ts +113 -1
  575. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  576. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  577. package/src/tools/apps/executors.ts +58 -7
  578. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  579. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  580. package/src/tools/browser/browser-execution.ts +15 -11
  581. package/src/tools/computer-use/definitions.ts +3 -3
  582. package/src/tools/credentials/vault.ts +1 -1
  583. package/src/tools/document/document-tool.ts +124 -1
  584. package/src/tools/filesystem/edit.ts +1 -1
  585. package/src/tools/filesystem/list.ts +1 -1
  586. package/src/tools/filesystem/read.ts +1 -1
  587. package/src/tools/filesystem/write.ts +5 -2
  588. package/src/tools/host-filesystem/transfer.ts +1 -1
  589. package/src/tools/host-terminal/host-shell.ts +1 -1
  590. package/src/tools/memory/register.ts +1 -9
  591. package/src/tools/permission-checker.ts +1 -1
  592. package/src/tools/registry.ts +17 -7
  593. package/src/tools/schedule/create.ts +2 -2
  594. package/src/tools/schema-transforms.ts +7 -2
  595. package/src/tools/side-effects.ts +1 -0
  596. package/src/tools/skills/delete-managed.ts +4 -4
  597. package/src/tools/skills/execute.ts +1 -1
  598. package/src/tools/skills/scaffold-managed.ts +3 -2
  599. package/src/tools/subagent/notify-parent.ts +1 -1
  600. package/src/tools/system/request-permission.ts +2 -2
  601. package/src/tools/terminal/safe-env.ts +60 -1
  602. package/src/tools/tool-manifest.ts +2 -0
  603. package/src/tools/types.ts +107 -21
  604. package/src/tools/ui-surface/definitions.ts +6 -5
  605. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  606. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  607. package/src/types/onboarding-context.ts +2 -0
  608. package/src/util/errors.ts +17 -0
  609. package/src/util/platform.ts +10 -0
  610. package/src/watcher/__tests__/engine.test.ts +22 -0
  611. package/src/watcher/engine.ts +6 -2
  612. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  613. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  614. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  615. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  616. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  617. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  618. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  619. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  620. package/src/workspace/migrations/registry.ts +10 -0
  621. package/src/workspace/migrations/runner.ts +39 -9
  622. package/src/workspace/migrations/types.ts +4 -0
  623. package/examples/plugins/echo/bun.lock +0 -25
  624. package/src/__tests__/context-window-manager.test.ts +0 -2481
  625. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  626. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  627. package/src/context/prompts/compact.md +0 -26
  628. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  629. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  630. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
@@ -1,8 +1,7 @@
1
1
  import type { DrizzleDb } from "../db-connection.js";
2
- import { migrateExtConvBindingsChannelChatUnique } from "./010-ext-conv-bindings-channel-chat-unique.js";
3
2
 
4
3
  /**
5
- * External conversation bindings table with indexes and unique constraint migration.
4
+ * External conversation bindings table with indexes.
6
5
  */
7
6
  export function createExternalConversationBindingsTables(
8
7
  database: DrizzleDb,
@@ -12,6 +11,7 @@ export function createExternalConversationBindingsTables(
12
11
  conversation_id TEXT PRIMARY KEY REFERENCES conversations(id) ON DELETE CASCADE,
13
12
  source_channel TEXT NOT NULL,
14
13
  external_chat_id TEXT NOT NULL,
14
+ external_thread_id TEXT,
15
15
  external_user_id TEXT,
16
16
  display_name TEXT,
17
17
  username TEXT,
@@ -25,9 +25,20 @@ export function createExternalConversationBindingsTables(
25
25
  database.run(
26
26
  /*sql*/ `CREATE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat ON external_conversation_bindings(source_channel, external_chat_id)`,
27
27
  );
28
+ database.run(
29
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_thread ON external_conversation_bindings(source_channel, external_chat_id, external_thread_id)`,
30
+ );
28
31
  database.run(
29
32
  /*sql*/ `CREATE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel ON external_conversation_bindings(source_channel)`,
30
33
  );
31
-
32
- migrateExtConvBindingsChannelChatUnique(database);
34
+ database.run(/*sql*/ `
35
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_no_thread_unique
36
+ ON external_conversation_bindings(source_channel, external_chat_id)
37
+ WHERE external_thread_id IS NULL
38
+ `);
39
+ database.run(/*sql*/ `
40
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_thread_unique
41
+ ON external_conversation_bindings(source_channel, external_chat_id, external_thread_id)
42
+ WHERE external_thread_id IS NOT NULL
43
+ `);
33
44
  }
@@ -1133,7 +1133,7 @@ describe("migrateDeletePrivateConversations", () => {
1133
1133
  1,
1134
1134
  'text',
1135
1135
  'eA==',
1136
- ${now}
1136
+ ${now - 60_000}
1137
1137
  );
1138
1138
  `);
1139
1139
 
@@ -1153,4 +1153,41 @@ describe("migrateDeletePrivateConversations", () => {
1153
1153
  countWhere(raw, "attachments", `id = 'conv-standard-attachment'`),
1154
1154
  ).toBe(1);
1155
1155
  });
1156
+
1157
+ test("preserves pre-staged uploads (unlinked attachments) created after migration starts", () => {
1158
+ const db = createTestDb();
1159
+ const raw = getSqliteFrom(db);
1160
+ const now = Date.now();
1161
+
1162
+ bootstrapTables(raw);
1163
+ seedConversation(raw, "conv-standard", "standard");
1164
+ // created_at in the future ensures it lands after the migration's start
1165
+ // snapshot regardless of clock resolution / test-runner scheduling.
1166
+ raw.exec(/*sql*/ `
1167
+ INSERT INTO attachments (
1168
+ id,
1169
+ original_filename,
1170
+ mime_type,
1171
+ size_bytes,
1172
+ kind,
1173
+ data_base64,
1174
+ created_at
1175
+ ) VALUES (
1176
+ 'pre-staged-upload',
1177
+ 'pending.txt',
1178
+ 'text/plain',
1179
+ 1,
1180
+ 'text',
1181
+ 'eA==',
1182
+ ${now + 60_000}
1183
+ );
1184
+ `);
1185
+
1186
+ migrateDeletePrivateConversations(db);
1187
+
1188
+ expect(countWhere(raw, "attachments", `id = 'pre-staged-upload'`)).toBe(1);
1189
+ expect(
1190
+ countWhere(raw, "attachments", `id = 'conv-standard-attachment'`),
1191
+ ).toBe(1);
1192
+ });
1156
1193
  });
@@ -12,6 +12,12 @@ const PRIVATE_GRAPH_NODE_IDS = /*sql*/ `
12
12
  `;
13
13
 
14
14
  export function migrateDeletePrivateConversations(database: DrizzleDb): void {
15
+ // Snapshot the migration's start time. The trailing orphan-attachment sweep
16
+ // uses this as an upper bound so it cleans up leaks from prior runs of this
17
+ // migration (those rows were created before this run started) without
18
+ // touching pre-staged uploads created during or after the migration.
19
+ const migrationStartTs = Date.now();
20
+
15
21
  database.run(/*sql*/ `
16
22
  DELETE FROM tool_invocations
17
23
  WHERE conversation_id IN (${PRIVATE_CONVERSATION_IDS})
@@ -204,6 +210,7 @@ export function migrateDeletePrivateConversations(database: DrizzleDb): void {
204
210
  FROM message_attachments ma
205
211
  WHERE ma.attachment_id = attachments.id
206
212
  )
213
+ AND created_at <= ${migrationStartTs}
207
214
  `);
208
215
  database.run(/*sql*/ `
209
216
  DELETE FROM memory_summaries
@@ -0,0 +1,78 @@
1
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
+
3
+ /**
4
+ * Add a provider thread anchor to external conversation bindings.
5
+ *
6
+ * Slack conversations are keyed by `(channel_id, thread_ts)`, while legacy
7
+ * channels still use only `(source_channel, external_chat_id)`. SQLite treats
8
+ * NULLs as distinct in unique indexes, so use two partial unique indexes:
9
+ * one for legacy/no-thread bindings and one for threaded bindings.
10
+ */
11
+ export function migrateExternalConversationBindingThreadId(
12
+ database: DrizzleDb,
13
+ ): void {
14
+ const raw = getSqliteFrom(database);
15
+
16
+ const tableExists = raw
17
+ .query(
18
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'external_conversation_bindings'`,
19
+ )
20
+ .get();
21
+ if (!tableExists) return;
22
+
23
+ const columns = raw
24
+ .query(`PRAGMA table_info(external_conversation_bindings)`)
25
+ .all() as Array<{ name: string }>;
26
+ if (!columns.some((column) => column.name === "external_thread_id")) {
27
+ raw.exec(
28
+ `ALTER TABLE external_conversation_bindings ADD COLUMN external_thread_id TEXT`,
29
+ );
30
+ }
31
+
32
+ try {
33
+ raw.exec("BEGIN");
34
+
35
+ raw.exec(`DROP INDEX IF EXISTS idx_ext_conv_bindings_channel_chat_unique`);
36
+
37
+ raw.exec(/*sql*/ `
38
+ DELETE FROM external_conversation_bindings
39
+ WHERE rowid NOT IN (
40
+ SELECT rowid FROM (
41
+ SELECT rowid,
42
+ ROW_NUMBER() OVER (
43
+ PARTITION BY source_channel, external_chat_id, COALESCE(external_thread_id, '')
44
+ ORDER BY updated_at DESC, created_at DESC, rowid DESC
45
+ ) AS rn
46
+ FROM external_conversation_bindings
47
+ )
48
+ WHERE rn = 1
49
+ )
50
+ `);
51
+
52
+ raw.exec(/*sql*/ `
53
+ CREATE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_thread
54
+ ON external_conversation_bindings(source_channel, external_chat_id, external_thread_id)
55
+ `);
56
+
57
+ raw.exec(/*sql*/ `
58
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_no_thread_unique
59
+ ON external_conversation_bindings(source_channel, external_chat_id)
60
+ WHERE external_thread_id IS NULL
61
+ `);
62
+
63
+ raw.exec(/*sql*/ `
64
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_thread_unique
65
+ ON external_conversation_bindings(source_channel, external_chat_id, external_thread_id)
66
+ WHERE external_thread_id IS NOT NULL
67
+ `);
68
+
69
+ raw.exec("COMMIT");
70
+ } catch (err) {
71
+ try {
72
+ raw.exec("ROLLBACK");
73
+ } catch {
74
+ /* no active transaction */
75
+ }
76
+ throw err;
77
+ }
78
+ }
@@ -0,0 +1,21 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+
3
+ /**
4
+ * Create the onboarding_events table for tracking pre-chat onboarding
5
+ * selections (tools, tasks, tone, Google connect status).
6
+ */
7
+ export function createOnboardingEventsTable(database: DrizzleDb): void {
8
+ database.run(/*sql*/ `
9
+ CREATE TABLE IF NOT EXISTS onboarding_events (
10
+ id TEXT PRIMARY KEY,
11
+ created_at INTEGER NOT NULL,
12
+ screen TEXT NOT NULL,
13
+ tools_json TEXT,
14
+ tasks_json TEXT,
15
+ tone TEXT,
16
+ google_connected INTEGER,
17
+ google_scopes_json TEXT,
18
+ ab_variant TEXT
19
+ )
20
+ `);
21
+ }
@@ -0,0 +1,240 @@
1
+ import {
2
+ readSlackMetadata,
3
+ type SlackMessageMetadata,
4
+ } from "../../messaging/providers/slack/message-metadata.js";
5
+ import {
6
+ parseExternalContentEnvelope,
7
+ unwrapExternalContentForDisplay,
8
+ } from "../../security/untrusted-content.js";
9
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
10
+ import { withCrashRecovery } from "./validate-migration-state.js";
11
+
12
+ const CHECKPOINT_KEY = "migration_normalize_slack_external_content_v1";
13
+ const BATCH_SIZE = 100;
14
+
15
+ interface CandidateMessageRow {
16
+ rowid: number;
17
+ id: string;
18
+ role: string;
19
+ content: string;
20
+ metadata: string;
21
+ }
22
+
23
+ interface NormalizedMessageRow {
24
+ content: string;
25
+ metadata: string;
26
+ }
27
+
28
+ export function migrateNormalizeSlackExternalContent(
29
+ database: DrizzleDb,
30
+ ): void {
31
+ withCrashRecovery(database, CHECKPOINT_KEY, () => {
32
+ const raw = getSqliteFrom(database);
33
+
34
+ const tableExists = raw
35
+ .query(
36
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'messages'`,
37
+ )
38
+ .get();
39
+ if (!tableExists) return;
40
+
41
+ let lastRowid = 0;
42
+
43
+ for (;;) {
44
+ const rows = raw
45
+ .query(
46
+ /*sql*/ `
47
+ SELECT rowid, id, role, content, metadata
48
+ FROM messages
49
+ WHERE rowid > ?
50
+ AND metadata LIKE '%"slackMeta"%'
51
+ AND (
52
+ content LIKE '%<external_content%'
53
+ OR metadata NOT LIKE '%"provenanceTrustClass"%'
54
+ )
55
+ ORDER BY rowid
56
+ LIMIT ?
57
+ `,
58
+ )
59
+ .all(lastRowid, BATCH_SIZE) as CandidateMessageRow[];
60
+
61
+ if (rows.length === 0) break;
62
+
63
+ for (const row of rows) {
64
+ lastRowid = row.rowid;
65
+ const normalized = normalizeSlackMessageRow(row);
66
+ if (!normalized) continue;
67
+
68
+ raw
69
+ .query(`UPDATE messages SET content = ?, metadata = ? WHERE id = ?`)
70
+ .run(normalized.content, normalized.metadata, row.id);
71
+ }
72
+ }
73
+ });
74
+ }
75
+
76
+ export function downNormalizeSlackExternalContent(_database: DrizzleDb): void {
77
+ // Irreversible by design: this migration discards redundant persisted
78
+ // wrappers and leaves runtime assembly responsible for model boundaries.
79
+ }
80
+
81
+ function normalizeSlackMessageRow(
82
+ row: CandidateMessageRow,
83
+ ): NormalizedMessageRow | null {
84
+ const parsed = parseSlackMetadataEnvelope(row.metadata);
85
+ if (!parsed) return null;
86
+
87
+ const normalizedContent = normalizeMessageContent(row.content);
88
+ if (normalizedContent !== null) {
89
+ const { metadata } = parsed;
90
+ if (
91
+ !Object.prototype.hasOwnProperty.call(metadata, "provenanceTrustClass")
92
+ ) {
93
+ metadata.provenanceTrustClass = "unknown";
94
+ }
95
+
96
+ return {
97
+ content: normalizedContent,
98
+ metadata: JSON.stringify(metadata),
99
+ };
100
+ }
101
+
102
+ if (isLegacyGuardianBackfillRow(row, parsed)) {
103
+ const { metadata } = parsed;
104
+ metadata.provenanceTrustClass = "guardian";
105
+ if (
106
+ !Object.prototype.hasOwnProperty.call(metadata, "provenanceSourceChannel")
107
+ ) {
108
+ metadata.provenanceSourceChannel = "slack";
109
+ }
110
+
111
+ return {
112
+ content: row.content,
113
+ metadata: JSON.stringify(metadata),
114
+ };
115
+ }
116
+
117
+ return null;
118
+ }
119
+
120
+ function parseSlackMetadataEnvelope(rawMetadata: string): {
121
+ metadata: Record<string, unknown>;
122
+ slackMeta: SlackMessageMetadata;
123
+ } | null {
124
+ let parsed: unknown;
125
+ try {
126
+ parsed = JSON.parse(rawMetadata);
127
+ } catch {
128
+ return null;
129
+ }
130
+
131
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
132
+ return null;
133
+ }
134
+
135
+ const metadata = parsed as Record<string, unknown>;
136
+ if (typeof metadata.slackMeta !== "string") return null;
137
+ const slackMeta = readSlackMetadata(metadata.slackMeta);
138
+ if (!slackMeta) return null;
139
+ return { metadata, slackMeta };
140
+ }
141
+
142
+ function normalizeMessageContent(content: string): string | null {
143
+ const wholeEnvelope = parseExternalContentEnvelope(content);
144
+ if (wholeEnvelope) {
145
+ return wholeEnvelope.content;
146
+ }
147
+
148
+ let parsed: unknown;
149
+ try {
150
+ parsed = JSON.parse(content);
151
+ } catch {
152
+ return null;
153
+ }
154
+
155
+ if (!Array.isArray(parsed)) {
156
+ return null;
157
+ }
158
+
159
+ let changed = false;
160
+ const normalizedBlocks = parsed.map((block) => {
161
+ if (block === null || typeof block !== "object" || Array.isArray(block)) {
162
+ return block;
163
+ }
164
+ const record = block as Record<string, unknown>;
165
+ if (record.type !== "text" || typeof record.text !== "string") {
166
+ return block;
167
+ }
168
+
169
+ const unwrapped = unwrapExternalContentForDisplay(record.text);
170
+ if (unwrapped === record.text) {
171
+ return block;
172
+ }
173
+
174
+ changed = true;
175
+ return { ...record, text: unwrapped };
176
+ });
177
+
178
+ return changed ? JSON.stringify(normalizedBlocks) : null;
179
+ }
180
+
181
+ function isLegacyGuardianBackfillRow(
182
+ row: CandidateMessageRow,
183
+ parsed: {
184
+ metadata: Record<string, unknown>;
185
+ slackMeta: SlackMessageMetadata;
186
+ },
187
+ ): boolean {
188
+ if (row.role !== "user") return false;
189
+ if (parsed.slackMeta.eventKind !== "message") return false;
190
+ if (
191
+ Object.prototype.hasOwnProperty.call(
192
+ parsed.metadata,
193
+ "provenanceTrustClass",
194
+ )
195
+ ) {
196
+ return false;
197
+ }
198
+
199
+ // Old live Slack turns were written with turn-channel metadata. Old
200
+ // backfill rows were written directly with only `slackMeta`, and the old
201
+ // backfill invariant was: non-guardian non-empty text was stored wrapped,
202
+ // while guardian-authored non-empty text was stored raw. Only stamp the
203
+ // non-empty raw-text case; attachment-only / empty rows stay conservative.
204
+ if (
205
+ Object.prototype.hasOwnProperty.call(parsed.metadata, "userMessageChannel")
206
+ ) {
207
+ return false;
208
+ }
209
+
210
+ return hasNonEmptyRawText(row.content);
211
+ }
212
+
213
+ function hasNonEmptyRawText(content: string): boolean {
214
+ if (parseExternalContentEnvelope(content)) return false;
215
+
216
+ let parsed: unknown;
217
+ try {
218
+ parsed = JSON.parse(content);
219
+ } catch {
220
+ return content.trim().length > 0;
221
+ }
222
+
223
+ if (typeof parsed === "string") {
224
+ return parsed.trim().length > 0 && !parseExternalContentEnvelope(parsed);
225
+ }
226
+
227
+ if (!Array.isArray(parsed)) return false;
228
+
229
+ return parsed.some((block) => {
230
+ if (block === null || typeof block !== "object" || Array.isArray(block)) {
231
+ return false;
232
+ }
233
+ const text = (block as Record<string, unknown>).text;
234
+ return (
235
+ typeof text === "string" &&
236
+ text.trim().length > 0 &&
237
+ !parseExternalContentEnvelope(text)
238
+ );
239
+ });
240
+ }
@@ -0,0 +1,28 @@
1
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
+
3
+ /**
4
+ * Adds `base_url` (nullable) and `models` (nullable, JSON-encoded array of
5
+ * model identifiers) columns to the `provider_connections` table.
6
+ *
7
+ * Required by openai-compatible connections, which carry a user-supplied
8
+ * endpoint and model list per row instead of inheriting them from the catalog.
9
+ * Idempotent — re-running is a no-op once the columns exist.
10
+ */
11
+ export function migrateProviderConnectionBaseUrlAndModels(
12
+ database: DrizzleDb,
13
+ ): void {
14
+ const raw = getSqliteFrom(database);
15
+
16
+ const columns = raw
17
+ .query(`PRAGMA table_info(provider_connections)`)
18
+ .all() as Array<{ name: string }>;
19
+ const columnNames = new Set(columns.map((c) => c.name));
20
+
21
+ if (!columnNames.has("base_url")) {
22
+ raw.exec(`ALTER TABLE provider_connections ADD COLUMN base_url TEXT`);
23
+ }
24
+
25
+ if (!columnNames.has("models")) {
26
+ raw.exec(`ALTER TABLE provider_connections ADD COLUMN models TEXT`);
27
+ }
28
+ }
@@ -0,0 +1,49 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+ import { tableHasColumn } from "./schema-introspection.js";
4
+ import { withCrashRecovery } from "./validate-migration-state.js";
5
+
6
+ const CHECKPOINT_KEY = "migration_a2a_tasks_v1";
7
+
8
+ /**
9
+ * Create the a2a_tasks table for tracking A2A request/response lifecycle.
10
+ *
11
+ * Each row represents one inbound A2A task, tracking its state machine
12
+ * progression through submitted -> working -> completed/failed/canceled/rejected.
13
+ */
14
+ export function migrateA2ATasks(database: DrizzleDb): void {
15
+ withCrashRecovery(database, CHECKPOINT_KEY, () => {
16
+ if (tableHasColumn(database, "a2a_tasks", "id")) {
17
+ return;
18
+ }
19
+ const raw = getSqliteFrom(database);
20
+ raw.exec(/*sql*/ `
21
+ CREATE TABLE IF NOT EXISTS a2a_tasks (
22
+ id TEXT PRIMARY KEY,
23
+ context_id TEXT,
24
+ conversation_id TEXT,
25
+ state TEXT NOT NULL DEFAULT 'submitted',
26
+ status_message TEXT,
27
+ request_message_json TEXT NOT NULL,
28
+ artifacts_json TEXT,
29
+ push_url TEXT,
30
+ sender_assistant_id TEXT NOT NULL,
31
+ created_at INTEGER NOT NULL,
32
+ updated_at INTEGER NOT NULL
33
+ )
34
+ `);
35
+ raw.exec(/*sql*/ `
36
+ CREATE INDEX IF NOT EXISTS idx_a2a_tasks_context
37
+ ON a2a_tasks (context_id)
38
+ `);
39
+ raw.exec(/*sql*/ `
40
+ CREATE INDEX IF NOT EXISTS idx_a2a_tasks_conversation
41
+ ON a2a_tasks (conversation_id)
42
+ `);
43
+ });
44
+ }
45
+
46
+ export function downA2ATasks(database: DrizzleDb): void {
47
+ const raw = getSqliteFrom(database);
48
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS a2a_tasks`);
49
+ }
@@ -0,0 +1,32 @@
1
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
+
3
+ /**
4
+ * Adds `agent_loop_exit_reason` (nullable TEXT) to the `llm_request_logs`
5
+ * table.
6
+ *
7
+ * The agent loop sets this column on its final log row via
8
+ * `setAgentLoopExitReasonOnLatestLog` once the `while (true)` body exits.
9
+ * Intermediate rows keep NULL — downstream tooling (notably the LLM
10
+ * Context Inspector) reads "row has non-null value" as "this is the final
11
+ * call of a complete agent-loop run". Encoding the run-end via row state
12
+ * keeps the schema additive: no new tables, no FK churn.
13
+ *
14
+ * Idempotent — re-running is a no-op once the column exists. Modeled on
15
+ * migration 250 (`provider-connection-base-url-and-models`).
16
+ */
17
+ export function migrateLlmRequestLogAgentLoopExitReason(
18
+ database: DrizzleDb,
19
+ ): void {
20
+ const raw = getSqliteFrom(database);
21
+
22
+ const columns = raw
23
+ .query(`PRAGMA table_info(llm_request_logs)`)
24
+ .all() as Array<{ name: string }>;
25
+ const columnNames = new Set(columns.map((c) => c.name));
26
+
27
+ if (!columnNames.has("agent_loop_exit_reason")) {
28
+ raw.exec(
29
+ `ALTER TABLE llm_request_logs ADD COLUMN agent_loop_exit_reason TEXT`,
30
+ );
31
+ }
32
+ }
@@ -208,6 +208,15 @@ export { migrateCreateProviderConnections } from "./243-provider-connections.js"
208
208
  export { migrateProviderConnectionStatusLabel } from "./244-provider-connection-status-label.js";
209
209
  export { migrateMemoryRetrospectiveState } from "./245-memory-retrospective-state.js";
210
210
  export { migrateBackfillProviderConnectionLabel } from "./246-backfill-provider-connection-label.js";
211
+ export { migrateExternalConversationBindingThreadId } from "./247-external-conversation-binding-thread-id.js";
212
+ export { createOnboardingEventsTable } from "./248-create-onboarding-events.js";
213
+ export {
214
+ downNormalizeSlackExternalContent,
215
+ migrateNormalizeSlackExternalContent,
216
+ } from "./249-normalize-slack-external-content.js";
217
+ export { migrateProviderConnectionBaseUrlAndModels } from "./250-provider-connection-base-url-and-models.js";
218
+ export { downA2ATasks, migrateA2ATasks } from "./251-a2a-tasks.js";
219
+ export { migrateLlmRequestLogAgentLoopExitReason } from "./252-llm-request-log-agent-loop-exit-reason.js";
211
220
  export {
212
221
  MIGRATION_REGISTRY,
213
222
  type MigrationRegistryEntry,
@@ -48,6 +48,8 @@ import { downMemoryV2ActivationLogs } from "./234-memory-v2-activation-logs.js";
48
48
  import { downSlackCompactionWatermark } from "./235-slack-compaction-watermark.js";
49
49
  import { downToolInvocationsMatchedRuleId } from "./236-tool-invocations-matched-rule-id.js";
50
50
  import { downHeartbeatRuns } from "./237-heartbeat-runs.js";
51
+ import { downNormalizeSlackExternalContent } from "./249-normalize-slack-external-content.js";
52
+ import { downA2ATasks } from "./251-a2a-tasks.js";
51
53
 
52
54
  export interface MigrationRegistryEntry {
53
55
  /** The checkpoint key written to memory_checkpoints on completion. */
@@ -412,6 +414,20 @@ export const MIGRATION_REGISTRY: MigrationRegistryEntry[] = [
412
414
  "Create heartbeat_runs table for tracking heartbeat execution lifecycle with CAS state transitions",
413
415
  down: downHeartbeatRuns,
414
416
  },
417
+ {
418
+ key: "migration_normalize_slack_external_content_v1",
419
+ version: 48,
420
+ description:
421
+ "Normalize legacy persisted Slack external_content wrappers back to raw message content",
422
+ down: downNormalizeSlackExternalContent,
423
+ },
424
+ {
425
+ key: "migration_a2a_tasks_v1",
426
+ version: 49,
427
+ description:
428
+ "Create a2a_tasks table for tracking A2A request/response lifecycle",
429
+ down: downA2ATasks,
430
+ },
415
431
  ];
416
432
 
417
433
  export function getMaxMigrationVersion(): number {