@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
@@ -12,6 +12,8 @@
12
12
  */
13
13
  import {
14
14
  type FeedItem,
15
+ type FeedItemCategory,
16
+ type FeedItemDetailPanelKind,
15
17
  feedItemSchema,
16
18
  type FeedItemUrgency,
17
19
  } from "../home/feed-types.js";
@@ -23,6 +25,7 @@ import type { NotificationSignal } from "./signal.js";
23
25
  import type {
24
26
  NotificationDecision,
25
27
  NotificationDeliveryResult,
28
+ RenderedChannelCopy,
26
29
  } from "./types.js";
27
30
 
28
31
  const log = getLogger("home-feed-side-effect");
@@ -49,9 +52,30 @@ export async function writeHomeFeedItemForSignal(
49
52
  ): Promise<FeedItem | null> {
50
53
  if (!shouldMirrorToHomeFeed(signal)) return null;
51
54
 
52
- const renderedCopy = decision.renderedCopy.vellum;
53
- const payloadTitle = readPayloadString(signal.contextPayload, "title");
54
- const payloadBody = readPayloadString(signal.contextPayload, "body");
55
+ const renderedCopy =
56
+ decision.renderedCopy.vellum ??
57
+ firstSelectedRenderedCopy(decision.renderedCopy, decision.selectedChannels);
58
+ const payloadTitle =
59
+ readPayloadString(signal.contextPayload, "title") ??
60
+ readPayloadString(signal.contextPayload, "requestedTitle");
61
+ const payloadBody =
62
+ readPayloadString(signal.contextPayload, "body") ??
63
+ readPayloadString(signal.contextPayload, "requestedMessage");
64
+
65
+ // Source the title from the payload only. The LLM's `renderedCopy.title`
66
+ // often echoes the body when no explicit title was passed, which stutters
67
+ // against `summary` in the row. Leave undefined when absent; renderers
68
+ // fall back to `summary`.
69
+ const resolvedTitle = payloadTitle?.trim() || undefined;
70
+ const resolvedSummary =
71
+ renderedCopy?.body?.trim() || payloadBody?.trim() || "";
72
+ if (!resolvedSummary) {
73
+ log.warn(
74
+ { signalId: signal.signalId, sourceEventName: signal.sourceEventName },
75
+ "Home-feed write skipped: no summary available (would have fallen back to event name)",
76
+ );
77
+ return null;
78
+ }
55
79
 
56
80
  const conversationId = deliveryResults.find(
57
81
  (r) => r.channel === "vellum",
@@ -61,17 +85,31 @@ export async function writeHomeFeedItemForSignal(
61
85
  : undefined;
62
86
  const now = new Date().toISOString();
63
87
 
88
+ const category = deriveCategory(signal);
89
+ const panelKind = deriveDetailPanelKind(signal);
90
+ const metadata =
91
+ signal.contextPayload &&
92
+ typeof signal.contextPayload === "object" &&
93
+ !Array.isArray(signal.contextPayload)
94
+ ? (signal.contextPayload as Record<string, unknown>)
95
+ : undefined;
96
+
64
97
  const item: FeedItem = {
65
98
  id: `notif:${signal.signalId}`,
66
99
  type: "notification",
67
100
  priority: 50,
68
- title: renderedCopy?.title ?? payloadTitle ?? signal.sourceEventName,
69
- summary: renderedCopy?.body ?? payloadBody ?? signal.sourceEventName,
101
+ ...(resolvedTitle ? { title: resolvedTitle } : {}),
102
+ summary: resolvedSummary,
70
103
  timestamp: now,
71
104
  createdAt: now,
72
105
  status: "new",
106
+ category,
107
+ noteworthy: deriveNoteworthy(signal),
108
+ fromAssistant: signal.sourceChannel === "assistant_tool",
73
109
  ...(urgency ? { urgency } : {}),
74
110
  ...(conversationId ? { conversationId } : {}),
111
+ ...(panelKind ? { detailPanel: { kind: panelKind } } : {}),
112
+ ...(metadata ? { metadata } : {}),
75
113
  };
76
114
 
77
115
  try {
@@ -88,12 +126,60 @@ export async function writeHomeFeedItemForSignal(
88
126
  return item;
89
127
  }
90
128
 
129
+ // ── Category & detail-panel derivation ────────────────────────────────
130
+
131
+ const EVENT_CATEGORY_MAP: Record<string, FeedItemCategory> = {
132
+ "credential.health_alert": "security",
133
+ "activity.failed": "background",
134
+ "activity.complete": "background",
135
+ "watcher.notification": "system",
136
+ "schedule.notify": "scheduling",
137
+ "guardian.question": "security",
138
+ "guardian.channel_activation": "security",
139
+ "ingress.access_request": "security",
140
+ "ingress.escalation": "security",
141
+ };
142
+
143
+ function deriveCategory(signal: NotificationSignal): FeedItemCategory {
144
+ return EVENT_CATEGORY_MAP[signal.sourceEventName] ?? "system";
145
+ }
146
+
147
+ function deriveDetailPanelKind(
148
+ signal: NotificationSignal,
149
+ ): FeedItemDetailPanelKind | undefined {
150
+ if (signal.sourceEventName === "credential.health_alert") {
151
+ return "toolPermission";
152
+ }
153
+
154
+ if (signal.sourceEventName === "guardian.question") {
155
+ const payload = signal.contextPayload;
156
+ const kind =
157
+ payload && typeof payload === "object" && "requestKind" in payload
158
+ ? (payload as Record<string, unknown>).requestKind
159
+ : undefined;
160
+ if (kind === "tool_approval" || kind === "tool_grant_request") {
161
+ return "permissionChat";
162
+ }
163
+ }
164
+
165
+ return undefined;
166
+ }
167
+
91
168
  /**
92
169
  * `sourceContextId` is best-effort — it may not be a conversation id
93
170
  * (e.g. scheduler job id, watcher event id), so a lookup failure
94
171
  * falls through to "not a background conversation" rather than throwing.
172
+ *
173
+ * `assistant_tool` is the source channel used by the `notifications send`
174
+ * skill (and by background-job failure emits). These signals represent
175
+ * the assistant actively choosing to share, so we mirror them into the
176
+ * home feed without requiring a background-typed conversation or the
177
+ * `isAsyncBackground` hint — the documented (SKILL.md) CLI surface
178
+ * intentionally does not expose either; internal call sites that still set
179
+ * the hint keep working unchanged.
95
180
  */
96
181
  function shouldMirrorToHomeFeed(signal: NotificationSignal): boolean {
182
+ if (signal.sourceChannel === "assistant_tool") return true;
97
183
  if (signal.attentionHints.isAsyncBackground) return true;
98
184
  if (!signal.sourceContextId) return false;
99
185
  try {
@@ -109,3 +195,50 @@ function readPayloadString(payload: unknown, key: string): string | undefined {
109
195
  const value = (payload as Record<string, unknown>)[key];
110
196
  return typeof value === "string" ? value : undefined;
111
197
  }
198
+
199
+ /**
200
+ * Routing-intent enforcement can prune `selectedChannels` without also
201
+ * pruning `renderedCopy`, so iterating `renderedCopy` directly risks
202
+ * surfacing copy for a channel that was never delivered. Walk
203
+ * `selectedChannels` in order instead so the channel that actually shipped
204
+ * wins.
205
+ */
206
+ function firstSelectedRenderedCopy(
207
+ renderedCopy: NotificationDecision["renderedCopy"],
208
+ selectedChannels: NotificationDecision["selectedChannels"],
209
+ ): RenderedChannelCopy | undefined {
210
+ for (const channel of selectedChannels) {
211
+ const copy = renderedCopy[channel];
212
+ if (copy && (copy.title?.trim() || copy.body?.trim())) return copy;
213
+ }
214
+ return undefined;
215
+ }
216
+
217
+ // ── Noteworthy derivation ─────────────────────────────────────────────
218
+ //
219
+ // Clients split the feed into inbox-style (noteworthy) and activity-style
220
+ // (routine) surfaces. Assistant-initiated shares and a small allow-list of
221
+ // high-importance system events land in the inbox; routine background
222
+ // signals stay in activity.
223
+
224
+ const NOTEWORTHY_EVENT_NAMES: ReadonlySet<string> = new Set([
225
+ "guardian.question",
226
+ "guardian.channel_activation",
227
+ "ingress.access_request",
228
+ "ingress.escalation",
229
+ "credential.health_alert",
230
+ ]);
231
+
232
+ function deriveNoteworthy(signal: NotificationSignal): boolean {
233
+ // Background-job failures emit with `sourceChannel: "assistant_tool"`
234
+ // (see `runtime/background-job-runner.ts`), so the activity.failed rule
235
+ // must run BEFORE the assistant_tool short-circuit — otherwise every
236
+ // routine watcher/heartbeat failure would land in the Inbox instead of
237
+ // staying in the activity feed.
238
+ if (signal.sourceEventName === "activity.failed") {
239
+ return signal.attentionHints.urgency === "critical";
240
+ }
241
+ if (signal.sourceChannel === "assistant_tool") return true;
242
+ if (NOTEWORTHY_EVENT_NAMES.has(signal.sourceEventName)) return true;
243
+ return false;
244
+ }
@@ -4,6 +4,7 @@
4
4
  * decision engine route contextually.
5
5
  */
6
6
 
7
+ import type { ConversationCreateType } from "../memory/conversation-crud.js";
7
8
  import type { GuardianQuestionPayload } from "./guardian-question-mode.js";
8
9
 
9
10
  // ── Source channel registry ────────────────────────────────────────────
@@ -106,10 +107,6 @@ export const NOTIFICATION_SOURCE_EVENT_NAMES = [
106
107
  description:
107
108
  "OAuth credential health issue detected (expired, revoked, missing scopes)",
108
109
  },
109
- {
110
- id: "heartbeat.alert",
111
- description: "Heartbeat found something worth surfacing to the guardian",
112
- },
113
110
  ] as const;
114
111
 
115
112
  export type NotificationSourceEventName =
@@ -119,7 +116,7 @@ export type NotificationSourceEventName =
119
116
 
120
117
  export interface AttentionHints {
121
118
  requiresAction: boolean;
122
- urgency: "low" | "medium" | "high";
119
+ urgency: "low" | "medium" | "high" | "critical";
123
120
  deadlineAt?: number; // epoch ms
124
121
  isAsyncBackground: boolean;
125
122
  visibleInSourceNow: boolean;
@@ -214,5 +211,6 @@ export interface NotificationSignal<TEventName extends string = string> {
214
211
  groupId?: string;
215
212
  scheduleJobId?: string;
216
213
  source?: string;
214
+ conversationType?: ConversationCreateType;
217
215
  };
218
216
  }
@@ -7,6 +7,7 @@
7
7
 
8
8
  import type { ChannelPolicies } from "../channels/config.js";
9
9
  import type { ChannelId } from "../channels/types.js";
10
+ import type { AttentionHints } from "./signal.js";
10
11
 
11
12
  /**
12
13
  * Derived from the channel policy registry: only channels whose
@@ -81,6 +82,13 @@ export interface ChannelDeliveryPayload {
81
82
  deepLinkTarget?: Record<string, unknown>;
82
83
  /** Original signal context payload — available for channel-specific structured rendering. */
83
84
  contextPayload?: Record<string, unknown>;
85
+ /**
86
+ * Forwarded from the originating signal so adapters can make
87
+ * urgency-aware decisions (e.g. the vellum adapter suppresses the OS
88
+ * banner for non-urgent intents while still emitting the conversation
89
+ * pairing side effects).
90
+ */
91
+ urgency: AttentionHints["urgency"];
84
92
  }
85
93
 
86
94
  /** Interface that each channel adapter must implement. */
@@ -92,7 +92,7 @@ export async function resolveOAuthConnection(
92
92
  ? ` matching ${filters.join(" and ")}`
93
93
  : "";
94
94
  throw new Error(
95
- `No active OAuth connection found for "${provider}"${qualifier}. Connect the service first with \`assistant oauth connect ${provider}\`.`,
95
+ `No active OAuth connection found for "${provider}"${qualifier}. The ${provider} service needs to be connected before it can be used.`,
96
96
  );
97
97
  }
98
98
 
@@ -102,7 +102,7 @@ export async function resolveOAuthConnection(
102
102
  });
103
103
  if (!tokenResult.value) {
104
104
  throw new Error(
105
- `OAuth connection for "${provider}" exists but has no access token. Re-authorize with \`assistant oauth connect ${provider}\`.`,
105
+ `OAuth connection for "${provider}" exists but the access token is missing or expired. The ${provider} service needs to be reconnected.`,
106
106
  );
107
107
  }
108
108
 
@@ -223,18 +223,22 @@ async function resolvePlatformConnectionId(
223
223
 
224
224
  if (connections.length === 0) {
225
225
  throw new Error(
226
- `No active platform OAuth connection found for provider "${provider}"` +
226
+ `No active OAuth connection found for provider "${provider}"` +
227
227
  (account ? ` with account "${account}"` : "") +
228
- ". Connect the service on the Vellum platform first.",
228
+ `. The ${provider} service needs to be connected.`,
229
229
  );
230
230
  }
231
231
 
232
232
  if (connections.length > 1 && !account) {
233
+ const allAccounts = connections
234
+ .map((c) => c.account_label ?? c.id)
235
+ .join(", ");
233
236
  log.warn(
234
237
  {
235
238
  provider,
236
239
  count: connections.length,
237
240
  selectedId: connections[0].id,
241
+ allAccounts,
238
242
  },
239
243
  "Multiple active platform connections found; using the most recently created. " +
240
244
  "Pass an account option to select a specific connection.",
@@ -261,17 +261,57 @@ describe("PlatformOAuthConnection", () => {
261
261
  ).rejects.toThrow(CredentialRequiredError);
262
262
  });
263
263
 
264
- test("502 response throws ProviderUnreachableError", async () => {
264
+ test("502 response retries then throws ProviderUnreachableError", async () => {
265
+ let callCount = 0;
266
+ const client = makeMockClient(
267
+ mock(async () => {
268
+ callCount++;
269
+ return new Response("", { status: 502 });
270
+ }) as unknown as typeof globalThis.fetch,
271
+ );
272
+
273
+ const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
274
+ await expect(
275
+ conn.request({ method: "GET", path: "/test" }),
276
+ ).rejects.toThrow(ProviderUnreachableError);
277
+ // 1 initial + 3 retries = 4 total attempts
278
+ expect(callCount).toBe(4);
279
+ });
280
+
281
+ test("502 response includes detail from response body", async () => {
265
282
  const client = makeMockClient(
266
283
  mock(
267
- async () => new Response("", { status: 502 }),
284
+ async () => new Response("upstream timeout after 30s", { status: 502 }),
268
285
  ) as unknown as typeof globalThis.fetch,
269
286
  );
270
287
 
271
288
  const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
272
289
  await expect(
273
290
  conn.request({ method: "GET", path: "/test" }),
274
- ).rejects.toThrow(ProviderUnreachableError);
291
+ ).rejects.toThrow(/upstream timeout after 30s/);
292
+ });
293
+
294
+ test("502 recovers on retry", async () => {
295
+ let callCount = 0;
296
+ const client = makeMockClient(
297
+ mock(async () => {
298
+ callCount++;
299
+ if (callCount <= 2) {
300
+ return new Response("", { status: 502 });
301
+ }
302
+ return new Response(
303
+ JSON.stringify({ status: 200, headers: {}, body: { ok: true } }),
304
+ { status: 200 },
305
+ );
306
+ }) as unknown as typeof globalThis.fetch,
307
+ );
308
+
309
+ const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
310
+ const result = await conn.request({ method: "GET", path: "/test" });
311
+
312
+ expect(result.status).toBe(200);
313
+ expect(result.body).toEqual({ ok: true });
314
+ expect(callCount).toBe(3);
275
315
  });
276
316
 
277
317
  test("withToken throws clear error", async () => {
@@ -1,5 +1,6 @@
1
1
  import type { VellumPlatformClient } from "../platform/client.js";
2
2
  import { BackendError } from "../util/errors.js";
3
+ import { getLogger } from "../util/logger.js";
3
4
  import { getHttpRetryDelay, isRetryableStatus, sleep } from "../util/retry.js";
4
5
  import type {
5
6
  OAuthConnection,
@@ -7,17 +8,22 @@ import type {
7
8
  OAuthConnectionResponse,
8
9
  } from "./connection.js";
9
10
 
11
+ const log = getLogger("platform-oauth-connection");
10
12
  const MAX_RETRIES = 3;
11
13
 
12
14
  export class CredentialRequiredError extends BackendError {
13
- constructor(message = "Connection not set up on platform") {
15
+ constructor(
16
+ message = "OAuth credential for this provider has expired or been revoked. The service needs to be reconnected.",
17
+ ) {
14
18
  super(message);
15
19
  this.name = "CredentialRequiredError";
16
20
  }
17
21
  }
18
22
 
19
23
  export class ProviderUnreachableError extends BackendError {
20
- constructor(message = "Provider is unreachable") {
24
+ constructor(
25
+ message = "The external service provider is temporarily unreachable. This may be a transient issue — retry after a brief pause.",
26
+ ) {
21
27
  super(message);
22
28
  this.name = "ProviderUnreachableError";
23
29
  }
@@ -107,19 +113,26 @@ export class PlatformOAuthConnection implements OAuthConnection {
107
113
  throw new CredentialRequiredError();
108
114
  }
109
115
 
110
- if (response.status === 502) {
111
- throw new ProviderUnreachableError();
112
- }
113
-
114
116
  if (
115
117
  !response.ok &&
116
118
  isRetryableStatus(response.status) &&
117
119
  attempt < MAX_RETRIES
118
120
  ) {
121
+ log.warn(
122
+ { status: response.status, attempt, provider: "platform-proxy" },
123
+ `Retryable status ${response.status} from platform proxy (attempt ${attempt + 1}/${MAX_RETRIES + 1})`,
124
+ );
119
125
  await sleep(getHttpRetryDelay(response, attempt));
120
126
  continue;
121
127
  }
122
128
 
129
+ if (response.status === 502) {
130
+ const detail = await response.text().catch(() => "");
131
+ throw new ProviderUnreachableError(
132
+ `The external service provider is temporarily unreachable (HTTP 502).${detail ? ` Detail: ${detail}` : ""} This may be a transient issue — retry after a brief pause.`,
133
+ );
134
+ }
135
+
123
136
  if (!response.ok) {
124
137
  throw new BackendError(
125
138
  `Platform proxy returned unexpected status ${response.status}`,
@@ -77,7 +77,7 @@ export const PROVIDER_SEED_DATA: Record<
77
77
  pingUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
78
78
  baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
79
79
  displayLabel: "Google",
80
- description: "Gmail, Calendar, and Contacts",
80
+ description: "Gmail, Calendar, Drive, and Contacts",
81
81
  dashboardUrl: "https://console.cloud.google.com/apis/credentials",
82
82
  clientIdPlaceholder: "123456789.apps.googleusercontent.com",
83
83
  logoUrl: "https://cdn.simpleicons.org/google",
@@ -88,6 +88,7 @@ export const PROVIDER_SEED_DATA: Record<
88
88
  "https://www.googleapis.com/auth/gmail.settings.basic",
89
89
  "https://www.googleapis.com/auth/calendar.readonly",
90
90
  "https://www.googleapis.com/auth/calendar.events",
91
+ "https://www.googleapis.com/auth/drive",
91
92
  "https://www.googleapis.com/auth/userinfo.email",
92
93
  "https://www.googleapis.com/auth/contacts.readonly",
93
94
  ],
@@ -722,6 +723,14 @@ export const PROVIDER_SEED_DATA: Record<
722
723
  requiresClientSecret: false,
723
724
  logoUrl: "https://cdn.simpleicons.org/slack",
724
725
  defaultScopes: [],
726
+ injectionTemplates: [
727
+ {
728
+ hostPattern: "slack.com",
729
+ injectionType: "header",
730
+ headerName: "Authorization",
731
+ valuePrefix: "Bearer ",
732
+ },
733
+ ],
725
734
  },
726
735
 
727
736
  telegram: {
@@ -21,6 +21,7 @@ import {
21
21
  getProtectedDir,
22
22
  getWorkspaceDir,
23
23
  getWorkspaceHooksDir,
24
+ getWorkspacePluginsDir,
24
25
  } from "../util/platform.js";
25
26
  import {
26
27
  type ApprovalContext,
@@ -255,6 +256,7 @@ function buildFileContext(): FileContext {
255
256
  protectedDir: getProtectedDir(),
256
257
  deprecatedDir: getDeprecatedDir(),
257
258
  hooksDir: getWorkspaceHooksDir(),
259
+ pluginsDir: getWorkspacePluginsDir(),
258
260
  actorTokenSigningKeyPath: join(
259
261
  getProtectedDir(),
260
262
  "actor-token-signing-key",
@@ -52,6 +52,7 @@ export interface FileContext {
52
52
  protectedDir: string;
53
53
  deprecatedDir: string;
54
54
  hooksDir: string;
55
+ pluginsDir: string;
55
56
  actorTokenSigningKeyPath: string;
56
57
  skillSourceDirs: string[];
57
58
  }