@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
@@ -67,6 +67,43 @@ export interface FetchManagedCatalogResult {
67
67
  error?: string;
68
68
  }
69
69
 
70
+ // ---------------------------------------------------------------------------
71
+ // Managed connection cache — provides a synchronous view of platform-managed
72
+ // connections for use in the system prompt (which is built synchronously).
73
+ // Refresh is triggered at daemon startup and periodically via setInterval.
74
+ // ---------------------------------------------------------------------------
75
+
76
+ export interface CachedManagedConnection {
77
+ provider: string;
78
+ accountInfo: string | null;
79
+ }
80
+
81
+ let cachedConnections: CachedManagedConnection[] = [];
82
+
83
+ /**
84
+ * Return the last successfully fetched managed connections.
85
+ * Returns an empty array before the first successful refresh.
86
+ */
87
+ export function getCachedManagedConnections(): CachedManagedConnection[] {
88
+ return cachedConnections;
89
+ }
90
+
91
+ /**
92
+ * Fetch managed connections from the platform and update the in-memory cache.
93
+ * Best-effort: errors are logged and the cache retains its previous value.
94
+ */
95
+ export async function refreshManagedConnectionCache(): Promise<void> {
96
+ const result = await fetchManagedCatalog();
97
+ if (result.ok) {
98
+ cachedConnections = result.descriptors
99
+ .filter((d) => d.status === "active" || d.status === "ACTIVE")
100
+ .map((d) => ({
101
+ provider: d.provider,
102
+ accountInfo: d.accountInfo,
103
+ }));
104
+ }
105
+ }
106
+
70
107
  /**
71
108
  * Fetch the managed credential catalog from the platform.
72
109
  *
@@ -13,11 +13,14 @@
13
13
 
14
14
  import { isTokenExpired } from "@vellumai/credential-storage";
15
15
 
16
+ import type { Services } from "../config/schemas/services.js";
16
17
  import { getConnectionAccessTokenResult } from "../oauth/credential-token-resolver.js";
17
18
  import {
18
19
  getProvider,
19
20
  listActiveConnectionsByProvider,
20
21
  listProviders,
22
+ type OAuthConnectionRow,
23
+ type OAuthProviderRow,
21
24
  } from "../oauth/oauth-store.js";
22
25
  import { getLogger } from "../util/logger.js";
23
26
 
@@ -271,6 +274,207 @@ async function checkConnection(
271
274
  };
272
275
  }
273
276
 
277
+ // ── Managed provider checks ──────────────────────────────────────────
278
+
279
+ /**
280
+ * Check whether a provider is configured in managed mode.
281
+ * Uses dynamic imports to avoid circular dependencies (same pattern as
282
+ * `integration-status.ts`).
283
+ */
284
+ async function isManagedProvider(
285
+ providerRow: OAuthProviderRow,
286
+ ): Promise<boolean> {
287
+ const managedKey = providerRow.managedServiceConfigKey;
288
+ if (!managedKey) return false;
289
+
290
+ try {
291
+ const { ServicesSchema, getServiceMode } =
292
+ await import("../config/schemas/services.js");
293
+ if (!(managedKey in ServicesSchema.shape)) return false;
294
+
295
+ const { getConfig } = await import("../config/loader.js");
296
+ const services: Services = getConfig().services;
297
+ return getServiceMode(services, managedKey as keyof Services) === "managed";
298
+ } catch {
299
+ return false;
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Fetch active managed connections from the platform and ping each one.
305
+ * Returns health results for managed connections, or an empty array if
306
+ * the platform is unreachable or the provider is not managed.
307
+ */
308
+ async function checkManagedProvider(
309
+ providerRow: OAuthProviderRow,
310
+ ): Promise<CredentialHealthResult[]> {
311
+ const results: CredentialHealthResult[] = [];
312
+
313
+ try {
314
+ const { VellumPlatformClient } = await import("../platform/client.js");
315
+ const client = await VellumPlatformClient.create();
316
+ if (!client?.platformAssistantId) return results;
317
+
318
+ const params = new URLSearchParams();
319
+ params.set("provider", providerRow.provider);
320
+ params.set("status", "ACTIVE");
321
+
322
+ const path = `/v1/assistants/${encodeURIComponent(client.platformAssistantId)}/oauth/connections/?${params.toString()}`;
323
+ const response = await client.fetch(path);
324
+
325
+ if (!response.ok) {
326
+ log.warn(
327
+ { status: response.status, provider: providerRow.provider },
328
+ "Failed to list managed connections for health check",
329
+ );
330
+ return results;
331
+ }
332
+
333
+ const body = (await response.json()) as unknown;
334
+ const connections = (
335
+ Array.isArray(body)
336
+ ? body
337
+ : ((body as Record<string, unknown>).results ?? [])
338
+ ) as Array<{ id: string; account_label?: string }>;
339
+
340
+ if (connections.length === 0) {
341
+ // No active managed connections — report as missing so the
342
+ // heartbeat can notify the user.
343
+ results.push({
344
+ connectionId: `managed:${providerRow.provider}`,
345
+ provider: providerRow.provider,
346
+ accountInfo: null,
347
+ status: "missing_token",
348
+ details: `No active managed connection for ${providerRow.provider}. Reconnect on the Vellum platform.`,
349
+ missingScopes: [],
350
+ canAutoRecover: false,
351
+ });
352
+ return results;
353
+ }
354
+
355
+ // Ping each managed connection via the platform proxy
356
+ for (const conn of connections) {
357
+ const base: Omit<
358
+ CredentialHealthResult,
359
+ "status" | "details" | "canAutoRecover"
360
+ > = {
361
+ connectionId: conn.id,
362
+ provider: providerRow.provider,
363
+ accountInfo: conn.account_label ?? null,
364
+ missingScopes: [],
365
+ };
366
+
367
+ if (!providerRow.pingUrl) {
368
+ // No ping URL configured — assume healthy if connection exists
369
+ results.push({
370
+ ...base,
371
+ status: "healthy",
372
+ details: `${providerRow.provider} managed connection is active (no ping URL configured).`,
373
+ canAutoRecover: true,
374
+ });
375
+ continue;
376
+ }
377
+
378
+ // Ping via platform proxy
379
+ try {
380
+ const { PlatformOAuthConnection } =
381
+ await import("../oauth/platform-connection.js");
382
+ const platformConn = new PlatformOAuthConnection({
383
+ id: conn.id,
384
+ provider: providerRow.provider,
385
+ externalId: providerRow.provider,
386
+ accountInfo: conn.account_label ?? null,
387
+ client,
388
+ connectionId: conn.id,
389
+ baseUrl: undefined,
390
+ });
391
+
392
+ // Decompose the absolute pingUrl into base URL + relative path.
393
+ // OAuthConnectionRequest.path is documented as relative, but
394
+ // provider definitions store full absolute URLs.
395
+ const parsedPingUrl = new URL(providerRow.pingUrl);
396
+ const pingBaseUrl = `${parsedPingUrl.protocol}//${parsedPingUrl.host}`;
397
+ const pingPath = parsedPingUrl.pathname + parsedPingUrl.search;
398
+
399
+ const parsedHeaders = safeJsonParse<Record<string, string>>(
400
+ providerRow.pingHeaders,
401
+ {},
402
+ );
403
+ const parsedBody = safeJsonParse<unknown>(providerRow.pingBody, null);
404
+
405
+ const pingResp = await platformConn.request({
406
+ method: providerRow.pingMethod ?? "GET",
407
+ path: pingPath,
408
+ baseUrl: pingBaseUrl,
409
+ ...(Object.keys(parsedHeaders).length > 0
410
+ ? { headers: parsedHeaders }
411
+ : {}),
412
+ ...(parsedBody != null ? { body: parsedBody } : {}),
413
+ signal: AbortSignal.timeout(PING_TIMEOUT_MS),
414
+ });
415
+
416
+ if (pingResp.status >= 200 && pingResp.status < 300) {
417
+ results.push({
418
+ ...base,
419
+ status: "healthy",
420
+ details: `${providerRow.provider} managed credential is healthy.`,
421
+ canAutoRecover: true,
422
+ });
423
+ } else if (pingResp.status === 401 || pingResp.status === 403) {
424
+ results.push({
425
+ ...base,
426
+ status: "revoked",
427
+ details: `${providerRow.provider} managed token was rejected (${pingResp.status}). Reconnect on the Vellum platform.`,
428
+ canAutoRecover: false,
429
+ });
430
+ } else {
431
+ results.push({
432
+ ...base,
433
+ status: "ping_failed",
434
+ details: `${providerRow.provider} managed liveness check returned ${pingResp.status}.`,
435
+ canAutoRecover: false,
436
+ });
437
+ }
438
+ } catch (err) {
439
+ const msg = err instanceof Error ? err.message : String(err);
440
+ // CredentialRequiredError means the platform can't materialize
441
+ // the token — treat as revoked.
442
+ if (
443
+ err &&
444
+ typeof err === "object" &&
445
+ "name" in err &&
446
+ (err as { name: string }).name === "CredentialRequiredError"
447
+ ) {
448
+ results.push({
449
+ ...base,
450
+ status: "revoked",
451
+ details: `${providerRow.provider} managed connection is no longer valid. Reconnect on the Vellum platform.`,
452
+ canAutoRecover: false,
453
+ });
454
+ } else {
455
+ log.debug(
456
+ { provider: providerRow.provider, connectionId: conn.id, err: msg },
457
+ "Managed credential ping failed",
458
+ );
459
+ results.push({
460
+ ...base,
461
+ status: "ping_failed",
462
+ details: `${providerRow.provider} managed liveness check failed: ${msg}`,
463
+ canAutoRecover: false,
464
+ });
465
+ }
466
+ }
467
+ }
468
+ } catch (err) {
469
+ log.warn(
470
+ { err, provider: providerRow.provider },
471
+ "Failed to check managed provider health",
472
+ );
473
+ }
474
+
475
+ return results;
476
+ }
477
+
274
478
  // ── Public API ────────────────────────────────────────────────────────
275
479
 
276
480
  /**
@@ -279,6 +483,9 @@ async function checkConnection(
279
483
  * Iterates every registered provider, looks up active connections, and
280
484
  * validates each one. Returns a structured report with overall results
281
485
  * and a filtered list of unhealthy credentials.
486
+ *
487
+ * Checks both BYO (local SQLite) and managed (platform-hosted)
488
+ * connections.
282
489
  */
283
490
  export async function checkAllCredentials(): Promise<CredentialHealthReport> {
284
491
  const checkedAt = Date.now();
@@ -292,6 +499,10 @@ export async function checkAllCredentials(): Promise<CredentialHealthReport> {
292
499
  return { checkedAt, results, unhealthy: [] };
293
500
  }
294
501
 
502
+ // Track which providers have BYO connections so we skip the managed
503
+ // check for them (they're already covered by the BYO path).
504
+ const byoProviders = new Set<string>();
505
+
295
506
  for (const providerRow of providers) {
296
507
  let connections;
297
508
  try {
@@ -304,6 +515,10 @@ export async function checkAllCredentials(): Promise<CredentialHealthReport> {
304
515
  continue;
305
516
  }
306
517
 
518
+ if (connections.length > 0) {
519
+ byoProviders.add(providerRow.provider);
520
+ }
521
+
307
522
  for (const conn of connections) {
308
523
  try {
309
524
  const result = await checkConnection({
@@ -329,6 +544,36 @@ export async function checkAllCredentials(): Promise<CredentialHealthReport> {
329
544
  }
330
545
  }
331
546
 
547
+ // Check managed connections. If a provider is currently in managed mode,
548
+ // evaluate it via the managed path even if stale BYO rows exist — the
549
+ // user may have switched from BYO to managed.
550
+ for (const providerRow of providers) {
551
+ if (!(await isManagedProvider(providerRow))) continue;
552
+
553
+ // If the provider is in managed mode and also has BYO connections,
554
+ // remove the stale BYO results — managed mode takes priority.
555
+ if (byoProviders.has(providerRow.provider)) {
556
+ const beforeLen = results.length;
557
+ const filtered = results.filter(
558
+ (r) => r.provider !== providerRow.provider,
559
+ );
560
+ if (filtered.length !== beforeLen) {
561
+ results.length = 0;
562
+ results.push(...filtered);
563
+ }
564
+ }
565
+
566
+ try {
567
+ const managedResults = await checkManagedProvider(providerRow);
568
+ results.push(...managedResults);
569
+ } catch (err) {
570
+ log.warn(
571
+ { err, provider: providerRow.provider },
572
+ "Failed to check managed provider health",
573
+ );
574
+ }
575
+ }
576
+
332
577
  const unhealthy = results.filter((r) => r.status !== "healthy");
333
578
  if (unhealthy.length > 0) {
334
579
  log.info(
@@ -351,34 +596,50 @@ export async function checkAllCredentials(): Promise<CredentialHealthReport> {
351
596
  * result for the most recent active connection, or null if no connection
352
597
  * exists.
353
598
  *
599
+ * Checks BYO connections first; if none exist, falls back to checking
600
+ * managed connections on the platform.
601
+ *
354
602
  * Used by the watcher engine for pre-poll gating.
355
603
  */
356
604
  export async function checkCredentialForProvider(
357
605
  provider: string,
358
606
  ): Promise<CredentialHealthResult | null> {
359
- let connections;
607
+ const providerRow = getProvider(provider);
608
+ if (!providerRow) return null;
609
+
610
+ // Check managed mode first — if the provider is currently configured for
611
+ // managed mode, evaluate via the platform regardless of stale BYO rows.
612
+ if (await isManagedProvider(providerRow)) {
613
+ const managedResults = await checkManagedProvider(providerRow);
614
+ if (managedResults.length > 0) return managedResults[0]!;
615
+ return null;
616
+ }
617
+
618
+ // Fall back to BYO (local) connection check.
619
+ let connections: OAuthConnectionRow[];
360
620
  try {
361
621
  connections = listActiveConnectionsByProvider(provider);
362
622
  } catch {
363
- return null;
623
+ connections = [];
364
624
  }
365
- if (connections.length === 0) return null;
366
625
 
367
- const conn = connections[0]!;
368
- const providerRow = getProvider(conn.provider);
369
- if (!providerRow) return null;
626
+ if (connections.length > 0) {
627
+ const conn = connections[0]!;
628
+
629
+ return checkConnection({
630
+ connectionId: conn.id,
631
+ provider: conn.provider,
632
+ accountInfo: conn.accountInfo,
633
+ expiresAt: conn.expiresAt,
634
+ hasRefreshToken: !!conn.hasRefreshToken,
635
+ grantedScopesRaw: conn.grantedScopes,
636
+ defaultScopesRaw: providerRow.defaultScopes,
637
+ pingUrl: providerRow.pingUrl,
638
+ pingMethod: providerRow.pingMethod,
639
+ pingHeaders: providerRow.pingHeaders,
640
+ pingBody: providerRow.pingBody,
641
+ });
642
+ }
370
643
 
371
- return checkConnection({
372
- connectionId: conn.id,
373
- provider: conn.provider,
374
- accountInfo: conn.accountInfo,
375
- expiresAt: conn.expiresAt,
376
- hasRefreshToken: !!conn.hasRefreshToken,
377
- grantedScopesRaw: conn.grantedScopes,
378
- defaultScopesRaw: providerRow.defaultScopes,
379
- pingUrl: providerRow.pingUrl,
380
- pingMethod: providerRow.pingMethod,
381
- pingHeaders: providerRow.pingHeaders,
382
- pingBody: providerRow.pingBody,
383
- });
644
+ return null;
384
645
  }
@@ -62,8 +62,11 @@ const autoAnalysisConversations = new Set<string>();
62
62
  // flip this to true.
63
63
  let v2Enabled = false;
64
64
 
65
+ const realLoader = await import("../../config/loader.js");
65
66
  mock.module("../../config/loader.js", () => ({
67
+ ...realLoader,
66
68
  getConfig: () => ({ memory: { v2: { enabled: v2Enabled } } }),
69
+ loadConfig: () => ({ memory: { v2: { enabled: v2Enabled } } }),
67
70
  }));
68
71
 
69
72
  mock.module("../../memory/auto-analysis-guard.js", () => ({
@@ -81,7 +84,10 @@ mock.module("../../memory/jobs-store.js", () => ({
81
84
  },
82
85
  }));
83
86
 
87
+ const realAutoAnalysisEnqueue =
88
+ await import("../../memory/auto-analysis-enqueue.js");
84
89
  mock.module("../../memory/auto-analysis-enqueue.js", () => ({
90
+ ...realAutoAnalysisEnqueue,
85
91
  enqueueAutoAnalysisIfEnabled: (args: {
86
92
  conversationId: string;
87
93
  trigger: "batch" | "idle" | "lifecycle";
@@ -91,7 +97,6 @@ mock.module("../../memory/auto-analysis-enqueue.js", () => ({
91
97
  },
92
98
  }));
93
99
 
94
- let memoryRetroEnabled = false;
95
100
  const memoryRetroCalls: Array<{
96
101
  conversationId: string;
97
102
  trigger: string;
@@ -102,7 +107,6 @@ mock.module("../../memory/memory-retrospective-enqueue.js", () => ({
102
107
  conversationId: string;
103
108
  trigger: string;
104
109
  }) => {
105
- if (!memoryRetroEnabled) return;
106
110
  memoryRetroCalls.push(args);
107
111
  },
108
112
  // Also export sibling functions other modules import from this file, so
@@ -114,15 +118,23 @@ mock.module("../../memory/memory-retrospective-enqueue.js", () => ({
114
118
 
115
119
  // Stub all side-effecting cleanup helpers that disposeConversation chains
116
120
  // into after the enqueue block. We assert on enqueue behavior only.
121
+ const realBrowserScreencast =
122
+ await import("../../tools/browser/browser-screencast.js");
117
123
  mock.module("../../tools/browser/browser-screencast.js", () => ({
124
+ ...realBrowserScreencast,
118
125
  unregisterConversationSender: () => {},
119
126
  }));
120
127
 
128
+ const realConversationNotifiers = await import("../conversation-notifiers.js");
121
129
  mock.module("../conversation-notifiers.js", () => ({
130
+ ...realConversationNotifiers,
122
131
  unregisterCallNotifiers: () => {},
123
132
  }));
124
133
 
134
+ const realConversationSkillTools =
135
+ await import("../conversation-skill-tools.js");
125
136
  mock.module("../conversation-skill-tools.js", () => ({
137
+ ...realConversationSkillTools,
126
138
  resetSkillToolProjection: () => {},
127
139
  }));
128
140
 
@@ -191,7 +203,6 @@ describe("disposeConversation — auto-analysis enqueue", () => {
191
203
  autoAnalyzeCalls.length = 0;
192
204
  memoryRetroCalls.length = 0;
193
205
  autoAnalyzeEnabled = true;
194
- memoryRetroEnabled = false;
195
206
  autoAnalysisConversations.clear();
196
207
  v2Enabled = false;
197
208
  });
@@ -376,13 +387,11 @@ describe("disposeConversation — memory-retrospective lifecycle safety net", ()
376
387
  autoAnalyzeCalls.length = 0;
377
388
  memoryRetroCalls.length = 0;
378
389
  autoAnalyzeEnabled = false;
379
- memoryRetroEnabled = false;
380
390
  autoAnalysisConversations.clear();
381
391
  v2Enabled = false;
382
392
  });
383
393
 
384
- test("guardian conversation + flag on — enqueues memory-retrospective with trigger 'lifecycle'", () => {
385
- memoryRetroEnabled = true;
394
+ test("guardian conversation — enqueues memory-retrospective with trigger 'lifecycle'", () => {
386
395
  const ctx = makeDisposeContext({
387
396
  conversationId: "conv-retro",
388
397
  trustClass: "guardian",
@@ -397,31 +406,37 @@ describe("disposeConversation — memory-retrospective lifecycle safety net", ()
397
406
  });
398
407
  });
399
408
 
400
- test("flag off — no memory-retrospective enqueue", () => {
401
- memoryRetroEnabled = false;
409
+ test("untrusted actor — no memory-retrospective enqueue", () => {
402
410
  const ctx = makeDisposeContext({
403
- conversationId: "conv-retro-off",
404
- trustClass: "guardian",
411
+ conversationId: "conv-retro-untrusted",
412
+ trustClass: "unknown",
405
413
  });
406
414
 
407
415
  disposeConversation(ctx);
408
416
 
417
+ // The outer trust-class guard in disposeConversation gates ALL three
418
+ // enqueues (graph_extract, auto-analyze, memory-retrospective). When
419
+ // the actor is untrusted, none of them fire.
409
420
  expect(memoryRetroCalls).toHaveLength(0);
421
+ expect(autoAnalyzeCalls).toHaveLength(0);
410
422
  });
411
423
 
412
- test("untrusted actor no memory-retrospective enqueue even when flag is on", () => {
413
- memoryRetroEnabled = true;
424
+ // Regression test: the retrospective lifecycle enqueue was previously
425
+ // outside the `!isAutoAnalysis` guard, so it fired even for auto-analysis
426
+ // conversations. Mirrors the indexer-time gate in `indexer.ts` and
427
+ // matches the existing graph_extract recursion-guard semantics.
428
+ test("auto-analysis conversation — does NOT enqueue memory-retrospective", () => {
429
+ autoAnalysisConversations.add("conv-auto-retro");
414
430
  const ctx = makeDisposeContext({
415
- conversationId: "conv-retro-untrusted",
416
- trustClass: "unknown",
431
+ conversationId: "conv-auto-retro",
432
+ trustClass: "guardian",
417
433
  });
418
434
 
419
435
  disposeConversation(ctx);
420
436
 
421
- // The outer trust-class guard in disposeConversation gates ALL three
422
- // enqueues (graph_extract, auto-analyze, memory-retrospective). When
423
- // the actor is untrusted, none of them fire.
424
437
  expect(memoryRetroCalls).toHaveLength(0);
425
- expect(autoAnalyzeCalls).toHaveLength(0);
438
+ // graph_extract is also recursion-guarded by the same `!isAutoAnalysis`
439
+ // block, so it should be skipped here too.
440
+ expect(memoryJobCalls).toHaveLength(0);
426
441
  });
427
442
  });
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Tests for the `config.tools.exclude` filter applied inside
3
+ * `createResolveToolsCallback`. Excluded tool names must not appear in the
4
+ * tool list resolved per turn, nor in the executor's `allowedToolNames`.
5
+ */
6
+
7
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
8
+
9
+ import * as configLoader from "../../config/loader.js";
10
+ import type { AssistantConfig } from "../../config/schema.js";
11
+ import type { ToolDefinition } from "../../providers/types.js";
12
+ import {
13
+ __clearRegistryForTesting,
14
+ registerMcpTools,
15
+ } from "../../tools/registry.js";
16
+ import type { Tool } from "../../tools/types.js";
17
+ import { createResolveToolsCallback } from "../conversation-tool-setup.js";
18
+
19
+ type SkillProjectionContext =
20
+ import("../conversation-tool-setup.js").SkillProjectionContext;
21
+ type SkillProjectionCache =
22
+ import("../conversation-skill-tools.js").SkillProjectionCache;
23
+
24
+ function def(name: string): ToolDefinition {
25
+ return { name, description: name, input_schema: { type: "object" } };
26
+ }
27
+
28
+ function mcpTool(name: string): Tool {
29
+ return {
30
+ name,
31
+ description: name,
32
+ origin: "mcp",
33
+ getDefinition: () => def(name),
34
+ } as unknown as Tool;
35
+ }
36
+
37
+ function makeCtx(
38
+ overrides: Partial<SkillProjectionContext> = {},
39
+ ): SkillProjectionContext {
40
+ return {
41
+ skillProjectionState: new Map(),
42
+ skillProjectionCache: { fingerprints: new Map() } as SkillProjectionCache,
43
+ coreToolNames: new Set<string>(),
44
+ toolsDisabledDepth: 0,
45
+ ...overrides,
46
+ };
47
+ }
48
+
49
+ function withExclude(exclude: string[]) {
50
+ const stub: Partial<AssistantConfig> = { tools: { exclude } };
51
+ return spyOn(configLoader, "getConfig").mockReturnValue(
52
+ stub as AssistantConfig,
53
+ );
54
+ }
55
+
56
+ let getConfigSpy: ReturnType<typeof withExclude> | undefined;
57
+
58
+ beforeEach(() => {
59
+ __clearRegistryForTesting();
60
+ });
61
+
62
+ afterEach(() => {
63
+ getConfigSpy?.mockRestore();
64
+ getConfigSpy = undefined;
65
+ __clearRegistryForTesting();
66
+ });
67
+
68
+ describe("createResolveToolsCallback — config.tools.exclude", () => {
69
+ test("excluded core tool is omitted from the resolved tool list", () => {
70
+ getConfigSpy = withExclude(["bash"]);
71
+ const resolver = createResolveToolsCallback(
72
+ [def("bash"), def("file_read")],
73
+ makeCtx(),
74
+ );
75
+ const result = resolver!([]);
76
+ expect(result.map((d) => d.name)).toEqual(["file_read"]);
77
+ });
78
+
79
+ test("excluded core tool is removed from ctx.allowedToolNames", () => {
80
+ getConfigSpy = withExclude(["bash"]);
81
+ const ctx = makeCtx();
82
+ const resolver = createResolveToolsCallback(
83
+ [def("bash"), def("file_read")],
84
+ ctx,
85
+ );
86
+ resolver!([]);
87
+ expect(ctx.allowedToolNames?.has("bash")).toBe(false);
88
+ expect(ctx.allowedToolNames?.has("file_read")).toBe(true);
89
+ });
90
+
91
+ test("excluded MCP tool is omitted from the resolved tool list", () => {
92
+ registerMcpTools([
93
+ mcpTool("mcp__server__navigate"),
94
+ mcpTool("mcp__server__click"),
95
+ ]);
96
+ getConfigSpy = withExclude(["mcp__server__navigate"]);
97
+ const resolver = createResolveToolsCallback(
98
+ [def("mcp__server__navigate"), def("mcp__server__click")],
99
+ makeCtx(),
100
+ );
101
+ const result = resolver!([]);
102
+ expect(result.map((d) => d.name)).toEqual(["mcp__server__click"]);
103
+ });
104
+
105
+ test("unknown name in exclude list is silently ignored", () => {
106
+ getConfigSpy = withExclude(["does_not_exist"]);
107
+ const resolver = createResolveToolsCallback([def("file_read")], makeCtx());
108
+ expect(() => resolver!([])).not.toThrow();
109
+ expect(resolver!([]).map((d) => d.name)).toEqual(["file_read"]);
110
+ });
111
+
112
+ test("empty exclude list leaves the tool set unchanged", () => {
113
+ getConfigSpy = withExclude([]);
114
+ const resolver = createResolveToolsCallback(
115
+ [def("bash"), def("file_read")],
116
+ makeCtx(),
117
+ );
118
+ expect(
119
+ resolver!([])
120
+ .map((d) => d.name)
121
+ .sort(),
122
+ ).toEqual(["bash", "file_read"]);
123
+ });
124
+
125
+ test("excluded tool stays excluded under disk-pressure cleanup mode", () => {
126
+ // `bash` is a cleanup-safe tool and would normally survive cleanup mode;
127
+ // the exclude filter must still suppress it.
128
+ getConfigSpy = withExclude(["bash"]);
129
+ const ctx = makeCtx({ diskPressureCleanupModeActive: true });
130
+ const resolver = createResolveToolsCallback(
131
+ [def("bash"), def("file_read")],
132
+ ctx,
133
+ );
134
+ const result = resolver!([]);
135
+ expect(result.map((d) => d.name)).not.toContain("bash");
136
+ expect(ctx.allowedToolNames?.has("bash")).toBe(false);
137
+ });
138
+ });