@vellumai/assistant 0.11.8 → 0.11.9-staging.1

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 (318) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/Dockerfile +8 -48
  3. package/docker-entrypoint.sh +5 -1
  4. package/docker-kata-apt-env.sh +3 -0
  5. package/docker-kata-apt-shims.sh +127 -0
  6. package/docker-kata-apt-wrapper.sh +45 -0
  7. package/docker-kata-chroot-exec.sh +35 -0
  8. package/docker-kata-pip.sh +8 -2
  9. package/docs/architecture/memory.md +8 -4
  10. package/docs/guardian-request-flow.md +18 -14
  11. package/docs/trusted-contact-access.md +1 -7
  12. package/node_modules/@vellumai/avatar-catalog/src/catalog.ts +7 -1
  13. package/node_modules/@vellumai/avatar-catalog/src/index.ts +1 -1
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +4 -1
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/platform-credential.ts +41 -0
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
  19. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +4 -1
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/platform-credential.ts +41 -0
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
  24. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +0 -18
  25. package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +110 -5
  26. package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +5 -33
  27. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +3 -0
  28. package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +100 -9
  29. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -2
  30. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +9 -0
  31. package/node_modules/@vellumai/service-contracts/package.json +4 -1
  32. package/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -0
  33. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  34. package/node_modules/@vellumai/service-contracts/src/platform-credential.ts +41 -0
  35. package/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
  36. package/openapi.yaml +193 -4
  37. package/package.json +2 -2
  38. package/src/__tests__/access-request-card-view.test.ts +6 -5
  39. package/src/__tests__/access-request-seed-content-blocks.test.ts +5 -2
  40. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +43 -82
  41. package/src/__tests__/always-loaded-tools-guard.test.ts +8 -2
  42. package/src/__tests__/attachment-stored-path-annotation.test.ts +80 -0
  43. package/src/__tests__/attachments-store.test.ts +27 -0
  44. package/src/__tests__/attachments.test.ts +43 -0
  45. package/src/__tests__/canned-reply-release.test.ts +48 -5
  46. package/src/__tests__/channel-delivery-store.test.ts +129 -18
  47. package/src/__tests__/channel-reply-delivery.test.ts +553 -93
  48. package/src/__tests__/channel-retry-sweep.test.ts +199 -0
  49. package/src/__tests__/client-os-metadata-persistence.test.ts +32 -3
  50. package/src/__tests__/conversation-error.test.ts +15 -0
  51. package/src/__tests__/conversation-load-history-repair.test.ts +118 -0
  52. package/src/__tests__/conversation-pairing.test.ts +141 -1
  53. package/src/__tests__/conversation-routes-disk-view.test.ts +28 -1
  54. package/src/__tests__/conversation-routes-slash-commands.test.ts +125 -0
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +79 -0
  56. package/src/__tests__/conversation-store-ephemeral.test.ts +323 -10
  57. package/src/__tests__/conversation-sync-tags.test.ts +2 -37
  58. package/src/__tests__/credential-health-service.test.ts +98 -10
  59. package/src/__tests__/delete-propagation.test.ts +469 -0
  60. package/src/__tests__/dm-persistence.test.ts +16 -0
  61. package/src/__tests__/docker-kata-apt-shims.test.ts +135 -0
  62. package/src/__tests__/forbidden-legacy-symbols.test.ts +12 -0
  63. package/src/__tests__/guardian-card-withdrawal.test.ts +0 -22
  64. package/src/__tests__/guardian-gateway-sim.ts +0 -23
  65. package/src/__tests__/guardian-question-mode.test.ts +108 -0
  66. package/src/__tests__/guardian-reply-router-answer-mode.test.ts +0 -2
  67. package/src/__tests__/guardian-routing-invariants.test.ts +26 -12
  68. package/src/__tests__/host-proxy-interface.test.ts +10 -0
  69. package/src/__tests__/inbound-slack-persistence.test.ts +16 -0
  70. package/src/__tests__/injector-v3-suppression.test.ts +43 -9
  71. package/src/__tests__/list-messages-page-latest.test.ts +127 -0
  72. package/src/__tests__/list-messages-system-card.test.ts +103 -0
  73. package/src/__tests__/media-resolve-image-validation.test.ts +77 -0
  74. package/src/__tests__/notification-decision-fallback.test.ts +199 -77
  75. package/src/__tests__/notification-decision-strategy.test.ts +198 -213
  76. package/src/__tests__/notification-discord-adapter.test.ts +25 -0
  77. package/src/__tests__/notification-slack-adapter.test.ts +133 -0
  78. package/src/__tests__/notification-telegram-adapter.test.ts +36 -0
  79. package/src/__tests__/openai-provider.test.ts +5 -5
  80. package/src/__tests__/outbound-slack-persistence.test.ts +85 -72
  81. package/src/__tests__/persist-user-message-set-processing-failure.test.ts +83 -65
  82. package/src/__tests__/platform-client-verify-credential.test.ts +100 -0
  83. package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -1
  84. package/src/__tests__/process-message-display-content.test.ts +20 -9
  85. package/src/__tests__/processing-acquire-fenced-guard.test.ts +56 -0
  86. package/src/__tests__/provider-meta-persistence.test.ts +67 -0
  87. package/src/__tests__/reaction-persistence.test.ts +22 -198
  88. package/src/__tests__/run-conversation-turn-persistence.test.ts +5 -2
  89. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +16 -0
  90. package/src/__tests__/skill-load-tool.test.ts +27 -0
  91. package/src/__tests__/skills.test.ts +34 -1
  92. package/src/__tests__/strip-memory-injections.test.ts +3 -4
  93. package/src/__tests__/terminal-tools.test.ts +9 -0
  94. package/src/__tests__/thread-backfill.test.ts +5 -3
  95. package/src/__tests__/unified-turn-context-location.test.ts +76 -0
  96. package/src/__tests__/voice-session-bridge.test.ts +18 -0
  97. package/src/__tests__/watch-retro-report-payload.test.ts +185 -0
  98. package/src/__tests__/watch-retro-tool-availability.test.ts +82 -0
  99. package/src/__tests__/workspace-migration-151-repair-renamed-fireworks-deepseek-pro-model-id.test.ts +235 -0
  100. package/src/__tests__/workspace-migration-152-repair-retired-fireworks-minimax-m2p7-model-id.test.ts +233 -0
  101. package/src/agent/attachments.ts +13 -2
  102. package/src/agent/loop.ts +3 -19
  103. package/src/api/README.md +9 -5
  104. package/src/api/index.ts +9 -8
  105. package/src/api/package.json +1 -0
  106. package/src/api/responses/conversation-message.ts +8 -0
  107. package/src/api/responses/home.ts +7 -18
  108. package/src/api/surfaces.ts +114 -7
  109. package/src/approvals/AGENTS.md +1 -1
  110. package/src/channels/__tests__/gateway-guardian-requests.test.ts +1 -23
  111. package/src/channels/__tests__/types.test.ts +22 -1
  112. package/src/channels/gateway-guardian-requests.ts +0 -22
  113. package/src/channels/types.ts +8 -6
  114. package/src/cli/__tests__/catalog-search-help.test.ts +12 -0
  115. package/src/cli/commands/channels/__tests__/channels.test.ts +26 -0
  116. package/src/cli/commands/channels/index.help.ts +15 -1
  117. package/src/cli/commands/channels/index.ts +6 -2
  118. package/src/cli/commands/db/__tests__/status.test.ts +22 -0
  119. package/src/cli/commands/db/index.help.ts +1 -1
  120. package/src/cli/commands/db/status.ts +172 -1
  121. package/src/cli/commands/platform/__tests__/connect.test.ts +29 -0
  122. package/src/cli/commands/platform/connect.ts +14 -5
  123. package/src/cli/commands/plugins.help.ts +6 -5
  124. package/src/cli/lib/__tests__/install-from-github.test.ts +0 -8
  125. package/src/cli/lib/__tests__/install-from-platform.test.ts +72 -0
  126. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +33 -0
  127. package/src/cli/lib/bundled-marketplace.json +14 -0
  128. package/src/cli/lib/install-from-github.ts +9 -5
  129. package/src/cli/lib/install-from-platform.ts +12 -1
  130. package/src/config/__tests__/assistant-initiated-threads-gate.test.ts +61 -0
  131. package/src/config/assistant-initiated-threads-gate.ts +50 -0
  132. package/src/config/bundled-skills/acp/SKILL.md +10 -3
  133. package/src/config/bundled-skills/schedule/SKILL.md +25 -11
  134. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +3 -1
  135. package/src/config/call-site-defaults.ts +4 -1
  136. package/src/config/feature-flag-registry.json +21 -4
  137. package/src/config/schemas/memory-v3.ts +4 -3
  138. package/src/context/strip-injections.ts +17 -56
  139. package/src/conversations/__tests__/message-consolidation.test.ts +39 -0
  140. package/src/conversations/message-consolidation.ts +4 -3
  141. package/src/credential-health/credential-health-service.ts +130 -0
  142. package/src/daemon/__tests__/conversation-tool-setup.test.ts +31 -0
  143. package/src/daemon/conversation-agent-loop-handlers.ts +55 -59
  144. package/src/daemon/conversation-error.ts +2 -0
  145. package/src/daemon/conversation-messaging.ts +195 -47
  146. package/src/daemon/conversation-process.ts +20 -5
  147. package/src/daemon/conversation-runtime-assembly.ts +48 -39
  148. package/src/daemon/conversation-store.ts +159 -11
  149. package/src/daemon/conversation-tool-setup.ts +9 -2
  150. package/src/daemon/conversation.ts +290 -19
  151. package/src/daemon/dictation-text-processing.ts +2 -7
  152. package/src/daemon/handlers/config-channels.ts +33 -3
  153. package/src/daemon/handlers/shared.ts +9 -0
  154. package/src/daemon/port-oversized-content.test.ts +117 -0
  155. package/src/daemon/port-oversized-content.ts +110 -0
  156. package/src/daemon/process-message.ts +87 -16
  157. package/src/daemon/reaction-record.test.ts +23 -11
  158. package/src/daemon/reaction-record.ts +6 -9
  159. package/src/documents/document-store.ts +1 -5
  160. package/src/home/conversation-starter-validation.ts +1 -5
  161. package/src/live-voice/__tests__/live-voice-photo.test.ts +910 -50
  162. package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +31 -25
  163. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +70 -1
  164. package/src/live-voice/live-voice-photo.ts +544 -61
  165. package/src/live-voice/live-voice-session.ts +6 -2
  166. package/src/live-voice/protocol.ts +20 -1
  167. package/src/messaging/provider-message-metadata.ts +40 -1
  168. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +10 -2
  169. package/src/messaging/providers/channel-transport.ts +28 -0
  170. package/src/messaging/providers/discord/send.ts +3 -2
  171. package/src/messaging/providers/slack/api.ts +11 -1
  172. package/src/messaging/providers/slack/message-metadata.test.ts +133 -0
  173. package/src/messaging/providers/slack/message-metadata.ts +141 -1
  174. package/src/messaging/providers/slack/send.test.ts +58 -0
  175. package/src/messaging/providers/slack/send.ts +4 -4
  176. package/src/messaging/providers/slack/transport.ts +28 -2
  177. package/src/messaging/providers/telegram-bot/send.test.ts +159 -0
  178. package/src/messaging/providers/telegram-bot/send.ts +93 -0
  179. package/src/messaging/providers/telegram-bot/transport.ts +71 -0
  180. package/src/messaging/reaction-envelopes.test.ts +73 -0
  181. package/src/messaging/reaction-envelopes.ts +33 -5
  182. package/src/messaging/read-provider-metadata.ts +4 -3
  183. package/src/monitoring/recovery/__tests__/stranded-delivery-events.test.ts +208 -0
  184. package/src/monitoring/recovery/db.ts +35 -0
  185. package/src/monitoring/recovery/orphaned-channel-events.ts +3 -22
  186. package/src/monitoring/recovery/run-recovery.ts +6 -2
  187. package/src/monitoring/recovery/stale-processing.ts +3 -20
  188. package/src/monitoring/recovery/stranded-delivery-events.ts +68 -0
  189. package/src/notifications/AGENTS.md +2 -2
  190. package/src/notifications/README.md +2 -2
  191. package/src/notifications/__tests__/assistant-reply-producer.test.ts +11 -9
  192. package/src/notifications/__tests__/broadcaster.test.ts +35 -4
  193. package/src/notifications/__tests__/notification-utils.test.ts +31 -0
  194. package/src/notifications/access-request-copy.ts +126 -114
  195. package/src/notifications/adapters/discord.ts +9 -5
  196. package/src/notifications/adapters/shared.ts +17 -4
  197. package/src/notifications/adapters/slack.ts +14 -4
  198. package/src/notifications/adapters/telegram.ts +8 -4
  199. package/src/notifications/approval-card-data.ts +5 -2
  200. package/src/notifications/assistant-reply-producer.ts +7 -7
  201. package/src/notifications/broadcaster.ts +57 -25
  202. package/src/notifications/conversation-pairing.ts +68 -2
  203. package/src/notifications/copy-composer.ts +13 -32
  204. package/src/notifications/decision-engine.ts +56 -237
  205. package/src/notifications/guardian-delivery-recorder.ts +5 -9
  206. package/src/notifications/guardian-feed-projection.ts +0 -15
  207. package/src/notifications/guardian-question-mode.ts +147 -6
  208. package/src/notifications/notification-utils.ts +117 -1
  209. package/src/permissions/confirmation-guardian-request.ts +2 -2
  210. package/src/permissions/prompter.ts +1 -1
  211. package/src/persistence/conversation-crud.ts +93 -14
  212. package/src/persistence/conversation-queries.ts +144 -17
  213. package/src/persistence/conversation-types.ts +39 -5
  214. package/src/persistence/delivery-crud.ts +224 -28
  215. package/src/persistence/delivery-status.ts +21 -2
  216. package/src/persistence/migrations/374-channel-inbound-message-id-index.ts +26 -0
  217. package/src/persistence/migrations/375-create-channel-outbound-posts.ts +52 -0
  218. package/src/persistence/migrations/__tests__/375-create-channel-outbound-posts.test.ts +84 -0
  219. package/src/persistence/schema/conversations.ts +71 -2
  220. package/src/persistence/schema-contract.test.ts +78 -0
  221. package/src/persistence/schema-contract.ts +96 -0
  222. package/src/persistence/steps.ts +4 -0
  223. package/src/platform/client.ts +55 -0
  224. package/src/plugins/__tests__/mcp-servers.test.ts +46 -0
  225. package/src/plugins/defaults/injector-order.ts +2 -1
  226. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +2 -2
  227. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +13 -15
  228. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +10 -0
  229. package/src/plugins/defaults/memory/injectors.ts +1 -1
  230. package/src/plugins/defaults/memory/memory-marker.ts +17 -7
  231. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +1 -5
  232. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +54 -41
  233. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +3 -3
  234. package/src/plugins/defaults/memory/v3/injector.ts +14 -15
  235. package/src/plugins/defaults/memory/v3/types.ts +15 -4
  236. package/src/plugins/defaults/turn-context/injectors.ts +3 -0
  237. package/src/plugins/defaults/turn-context/unified-turn-context.ts +24 -0
  238. package/src/plugins/mcp-servers.ts +14 -2
  239. package/src/providers/__tests__/dispatch-connection-routing.test.ts +50 -0
  240. package/src/providers/__tests__/registry-native-web-search.test.ts +49 -2
  241. package/src/providers/anthropic/client.ts +21 -24
  242. package/src/providers/call-site-routing.ts +5 -4
  243. package/src/providers/connection-resolution.ts +29 -1
  244. package/src/providers/content-block-size.test.ts +82 -0
  245. package/src/providers/content-block-size.ts +99 -0
  246. package/src/providers/file-block-text.test.ts +64 -0
  247. package/src/providers/file-block-text.ts +28 -0
  248. package/src/providers/gemini/client.ts +12 -7
  249. package/src/providers/inference/auth.ts +6 -6
  250. package/src/providers/media-resolve.ts +14 -0
  251. package/src/providers/model-catalog.ts +31 -34
  252. package/src/providers/openai/chat-completions-provider.ts +9 -25
  253. package/src/providers/openai/responses-provider.ts +11 -18
  254. package/src/providers/registry.ts +10 -7
  255. package/src/providers/routing-identity.ts +2 -1
  256. package/src/providers/types.ts +1 -4
  257. package/src/providers/vellum-model-routing.ts +3 -2
  258. package/src/runtime/AGENTS.md +40 -15
  259. package/src/runtime/approval-message-composer.ts +1 -6
  260. package/src/runtime/assistant-event-hub.ts +2 -39
  261. package/src/runtime/channel-approval-types.ts +1 -5
  262. package/src/runtime/channel-reply-delivery.ts +182 -115
  263. package/src/runtime/{slack-reply-session.test.ts → channel-reply-session.test.ts} +294 -156
  264. package/src/runtime/{slack-reply-session.ts → channel-reply-session.ts} +107 -86
  265. package/src/runtime/channel-retry-sweep.ts +102 -1
  266. package/src/runtime/finalize-event-delivery.ts +4 -4
  267. package/src/runtime/guardian-action-message-composer.ts +1 -4
  268. package/src/runtime/guardian-reply-router.ts +4 -75
  269. package/src/runtime/http-router.ts +1 -5
  270. package/src/runtime/question-request-guardian-bridge.ts +2 -3
  271. package/src/runtime/routes/__tests__/conversation-list-assistant-section.test.ts +359 -0
  272. package/src/runtime/routes/__tests__/dictation-command-mode.test.ts +120 -0
  273. package/src/runtime/routes/__tests__/sight-frame-routes.test.ts +487 -0
  274. package/src/runtime/routes/acp-routes.ts +1 -1
  275. package/src/runtime/routes/canned-reply-release.ts +19 -9
  276. package/src/runtime/routes/channel-route-shared.ts +0 -39
  277. package/src/runtime/routes/channel-verification-routes.ts +4 -1
  278. package/src/runtime/routes/conversation-list-routes.ts +44 -3
  279. package/src/runtime/routes/conversation-management-routes.ts +2 -1
  280. package/src/runtime/routes/conversation-routes.ts +138 -59
  281. package/src/runtime/routes/diagnostics-routes.ts +111 -60
  282. package/src/runtime/routes/guardian-action-routes.ts +10 -14
  283. package/src/runtime/routes/guardian-approval-interception.ts +0 -5
  284. package/src/runtime/routes/inbound-message-handler.ts +170 -48
  285. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +4 -3
  286. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +169 -8
  287. package/src/runtime/routes/inbound-stages/background-dispatch.ts +82 -14
  288. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +23 -48
  289. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +454 -94
  290. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +309 -102
  291. package/src/runtime/routes/index.ts +2 -0
  292. package/src/runtime/routes/platform-routes.ts +99 -10
  293. package/src/runtime/routes/sight-frame-routes.ts +136 -0
  294. package/src/runtime/sync/resource-sync-events.ts +14 -37
  295. package/src/runtime/sync/sync-publisher.test.ts +5 -4
  296. package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
  297. package/src/tools/client-os.ts +11 -1
  298. package/src/tools/host-filesystem/edit.ts +2 -2
  299. package/src/tools/host-filesystem/read.ts +2 -2
  300. package/src/tools/host-filesystem/transfer.ts +2 -2
  301. package/src/tools/host-filesystem/write.ts +2 -2
  302. package/src/tools/host-terminal/host-shell.ts +2 -2
  303. package/src/tools/skills/load.ts +1 -1
  304. package/src/tools/terminal/safe-env.ts +4 -0
  305. package/src/tools/tool-input-schemas.ts +2 -0
  306. package/src/tools/tool-manifest.ts +2 -0
  307. package/src/tools/ui-surface/definitions.ts +4 -3
  308. package/src/tools/watch/watch-retro-report.ts +205 -0
  309. package/src/watch/__tests__/watch-retro.test.ts +268 -40
  310. package/src/watch/watch-retro.ts +190 -77
  311. package/src/workspace/migrations/151-repair-renamed-fireworks-deepseek-pro-model-id.ts +195 -0
  312. package/src/workspace/migrations/152-repair-retired-fireworks-minimax-m2p7-model-id.ts +198 -0
  313. package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +0 -10
  314. package/src/workspace/migrations/registry.ts +4 -0
  315. package/docker-kata-pip-chroot.sh +0 -22
  316. package/src/__tests__/slack-reaction-approvals.test.ts +0 -97
  317. package/src/__tests__/slack-reaction-guardian-approval.test.ts +0 -307
  318. package/src/api/events/conversation-list-invalidated.ts +0 -38
@@ -85,9 +85,7 @@ describe("conversation sync tags", () => {
85
85
  // (`patchConversation` in `metadata-handlers.ts`) and the per-
86
86
  // conversation `sync_changed` metadata tag is included as a belt-and-
87
87
  // suspenders signal for sibling-tab consumers that missed the typed
88
- // event. The legacy `conversation_list_invalidated` broadcast is
89
- // scoped to `targetInterfaceId: "macos"` and therefore not visible to
90
- // this process-type subscriber.
88
+ // event. No `conversation_list_invalidated` broadcast is emitted.
91
89
  const conversation = createConversation("Old title");
92
90
  const route = findRoute(
93
91
  CONVERSATION_MANAGEMENT_ROUTES,
@@ -115,13 +113,6 @@ describe("conversation sync tags", () => {
115
113
  expect((received[1]!.message as { tags: string[] }).tags).not.toContain(
116
114
  SYNC_TAGS.conversationsList,
117
115
  );
118
- // The legacy invalidation broadcast is macOS-scoped and must not
119
- // reach this process-type subscriber.
120
- expect(
121
- received.some(
122
- (event) => event.message.type === "conversation_list_invalidated",
123
- ),
124
- ).toBe(false);
125
116
  });
126
117
 
127
118
  test("auto-title generation emits the typed title event and a metadata-only sync tag (no list umbrella)", async () => {
@@ -159,8 +150,7 @@ describe("conversation sync tags", () => {
159
150
  // fetched). The umbrella `conversationsList` tag is the signal for
160
151
  // that redrain. Per-conversation metadata is included so any future
161
152
  // single-row consumer can patch the freshly added row in place
162
- // without an extra GET. The legacy macOS-only invalidation broadcast
163
- // is not visible to process subscribers.
153
+ // without an extra GET.
164
154
  const route = findRoute(
165
155
  CONVERSATION_MANAGEMENT_ROUTES,
166
156
  "createConversation",
@@ -185,11 +175,6 @@ describe("conversation sync tags", () => {
185
175
  conversationMetadataSyncTag(conversationId!),
186
176
  ],
187
177
  });
188
- expect(
189
- received.some(
190
- (event) => event.message.type === "conversation_list_invalidated",
191
- ),
192
- ).toBe(false);
193
178
  });
194
179
 
195
180
  test("reorder emits a sync_changed with the umbrella tag and per-conversation metadata tags", async () => {
@@ -228,11 +213,6 @@ describe("conversation sync tags", () => {
228
213
  conversationMetadataSyncTag(second.id),
229
214
  ],
230
215
  });
231
- expect(
232
- received.some(
233
- (event) => event.message.type === "conversation_list_invalidated",
234
- ),
235
- ).toBe(false);
236
216
  });
237
217
 
238
218
  test("record seen emits only a per-conversation metadata sync tag (no list umbrella)", async () => {
@@ -245,11 +225,6 @@ describe("conversation sync tags", () => {
245
225
  // single `sync_changed` with only the per-conversation
246
226
  // `conversation:<id>:metadata` tag, which web consumes by GET-and-
247
227
  // patching the single cached row via `refreshConversationRow`.
248
- //
249
- // The legacy `conversation_list_invalidated` broadcast is still
250
- // emitted for macOS (which has no per-row patcher) but is scoped
251
- // to `targetInterfaceId: "macos"` and therefore not visible to
252
- // this process-type subscriber.
253
228
  const conversation = createConversation("Attention");
254
229
  projectAssistantMessage({
255
230
  conversationId: conversation.id,
@@ -283,11 +258,6 @@ describe("conversation sync tags", () => {
283
258
  expect((received[0]!.message as { tags: string[] }).tags).not.toContain(
284
259
  SYNC_TAGS.conversationsList,
285
260
  );
286
- expect(
287
- received.some(
288
- (event) => event.message.type === "conversation_list_invalidated",
289
- ),
290
- ).toBe(false);
291
261
  });
292
262
 
293
263
  test("mark unread emits only a per-conversation metadata sync tag (no list umbrella)", async () => {
@@ -327,11 +297,6 @@ describe("conversation sync tags", () => {
327
297
  expect((received[0]!.message as { tags: string[] }).tags).not.toContain(
328
298
  SYNC_TAGS.conversationsList,
329
299
  );
330
- expect(
331
- received.some(
332
- (event) => event.message.type === "conversation_list_invalidated",
333
- ),
334
- ).toBe(false);
335
300
  });
336
301
 
337
302
  test("addMessage('assistant') emits a metadata sync tag when attention state transitions", async () => {
@@ -53,6 +53,12 @@ let managedPingOutcome:
53
53
  | { kind: "credential_required" }
54
54
  | { kind: "throw"; message: string } = { kind: "status", status: 200 };
55
55
 
56
+ // Managed assistant-credential mock state. The verdict is what the platform
57
+ // answers about the stored assistant API key; `platformClientAvailable` covers
58
+ // the case where the client cannot be built at all.
59
+ let assistantKeyVerdict: "valid" | "rejected" | "unknown" = "valid";
60
+ let platformClientAvailable = true;
61
+
56
62
  // ── Module mocks ─────────────────────────────────────────────────────
57
63
 
58
64
  mock.module("../security/secure-keys.js", () => ({
@@ -123,14 +129,20 @@ mock.module("../security/token-manager.js", () => ({
123
129
  // seeded into the real workspace config via `seedGoogleOAuthMode` below.
124
130
  mock.module("../platform/client.js", () => ({
125
131
  VellumPlatformClient: {
126
- create: async () => ({
127
- platformAssistantId: "test-assistant",
128
- fetch: async (_path: string) => ({
129
- ok: managedListResponse.ok,
130
- status: managedListResponse.status,
131
- json: async () => managedListResponse.body ?? { results: [] },
132
- }),
133
- }),
132
+ create: async () => {
133
+ if (!platformClientAvailable) {
134
+ return null;
135
+ }
136
+ return {
137
+ platformAssistantId: "test-assistant",
138
+ fetch: async (_path: string) => ({
139
+ ok: managedListResponse.ok,
140
+ status: managedListResponse.status,
141
+ json: async () => managedListResponse.body ?? { results: [] },
142
+ }),
143
+ verifyCredential: async () => assistantKeyVerdict,
144
+ };
145
+ },
134
146
  },
135
147
  }));
136
148
 
@@ -164,8 +176,12 @@ mock.module("../oauth/platform-connection.js", () => ({
164
176
 
165
177
  import { setConfig } from "./helpers/set-config.js";
166
178
 
167
- const { checkAllCredentials, checkCredentialForProvider, _setFetchFn } =
168
- await import("../credential-health/credential-health-service.js");
179
+ const {
180
+ checkAllCredentials,
181
+ checkAssistantApiKey,
182
+ checkCredentialForProvider,
183
+ _setFetchFn,
184
+ } = await import("../credential-health/credential-health-service.js");
169
185
 
170
186
  /** Seed the real workspace config's `services.google-oauth.mode`. */
171
187
  function seedGoogleOAuthMode(mode: "managed" | "your-own"): void {
@@ -259,6 +275,8 @@ describe("credential-health-service", () => {
259
275
  seedGoogleOAuthMode("your-own");
260
276
  managedListResponse = { ok: true, status: 200, body: { results: [] } };
261
277
  managedPingOutcome = { kind: "status", status: 200 };
278
+ assistantKeyVerdict = "valid";
279
+ platformClientAvailable = true;
262
280
  });
263
281
 
264
282
  test("returns empty report when no providers exist", async () => {
@@ -763,4 +781,74 @@ describe("credential-health-service", () => {
763
781
  expect(result!.status).toBe("missing_token");
764
782
  });
765
783
  });
784
+
785
+ describe("managed assistant credential", () => {
786
+ const KEY = "credential/vellum/assistant_api_key";
787
+ const BASE_URL = "credential/vellum/platform_base_url";
788
+
789
+ // Checked directly rather than through `checkAllCredentials`: the managed
790
+ // credential is not enrolled in the heartbeat pass yet, because the alert
791
+ // that pass raises has nowhere to send the reader until the notification
792
+ // carries the repair.
793
+ const assistantKeyResult = checkAssistantApiKey;
794
+
795
+ test("is not reported when the workspace was never connected", async () => {
796
+ expect(await assistantKeyResult()).toBeNull();
797
+ });
798
+
799
+ test("is missing_token when a platform identity exists without a key", async () => {
800
+ secureKeyValues.set(BASE_URL, "https://platform.example");
801
+
802
+ const result = await assistantKeyResult();
803
+ expect(result!.status).toBe("missing_token");
804
+ expect(result!.canAutoRecover).toBe(true);
805
+ });
806
+
807
+ test("is healthy when the platform accepts the stored key", async () => {
808
+ secureKeyValues.set(BASE_URL, "https://platform.example");
809
+ secureKeyValues.set(KEY, "stored-key");
810
+ assistantKeyVerdict = "valid";
811
+
812
+ expect((await assistantKeyResult())!.status).toBe("healthy");
813
+ });
814
+
815
+ test("is revoked when the platform rejects the stored key", async () => {
816
+ secureKeyValues.set(BASE_URL, "https://platform.example");
817
+ secureKeyValues.set(KEY, "stored-key");
818
+ assistantKeyVerdict = "rejected";
819
+
820
+ const result = await assistantKeyResult();
821
+ expect(result!.status).toBe("revoked");
822
+ // Recovery needs a signed-in client to rotate the key, but no
823
+ // re-authorization from the user.
824
+ expect(result!.canAutoRecover).toBe(true);
825
+ });
826
+
827
+ // The anti-churn invariant: only a settled rejection may read as revoked,
828
+ // because `revoked` is what drives a rotation. An unsettled answer (server
829
+ // error, transport failure) must degrade to `unreachable`, which the
830
+ // heartbeat drops before notifying.
831
+ test("an unsettled platform answer is unreachable, never revoked", async () => {
832
+ secureKeyValues.set(BASE_URL, "https://platform.example");
833
+ secureKeyValues.set(KEY, "stored-key");
834
+ assistantKeyVerdict = "unknown";
835
+
836
+ expect((await assistantKeyResult())!.status).toBe("unreachable");
837
+ });
838
+
839
+ test("an unreadable credential store is unreachable, never revoked", async () => {
840
+ secureKeyValues.set(BASE_URL, "https://platform.example");
841
+ markUnreachable(KEY);
842
+
843
+ expect((await assistantKeyResult())!.status).toBe("unreachable");
844
+ });
845
+
846
+ test("is unreachable when the platform client cannot be built", async () => {
847
+ secureKeyValues.set(BASE_URL, "https://platform.example");
848
+ secureKeyValues.set(KEY, "stored-key");
849
+ platformClientAvailable = false;
850
+
851
+ expect((await assistantKeyResult())!.status).toBe("unreachable");
852
+ });
853
+ });
766
854
  });