@vellumai/assistant 0.8.5 → 0.8.6

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 (544) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/bunfig.toml +6 -1
  4. package/docs/credential-execution-service.md +6 -6
  5. package/docs/plugins.md +4 -3
  6. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
  7. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
  8. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  9. package/openapi.yaml +1900 -166
  10. package/package.json +1 -1
  11. package/src/__tests__/actor-token-service.test.ts +3 -2
  12. package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
  13. package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
  14. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  15. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  16. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  17. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/approval-routes-http.test.ts +9 -13
  20. package/src/__tests__/assert-not-live-db.ts +79 -0
  21. package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
  22. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  23. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  24. package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
  25. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  26. package/src/__tests__/btw-routes.test.ts +3 -2
  27. package/src/__tests__/call-controller.test.ts +3 -2
  28. package/src/__tests__/channel-approval-routes.test.ts +3 -2
  29. package/src/__tests__/channel-guardian.test.ts +3 -2
  30. package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
  31. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  32. package/src/__tests__/channel-retry-sweep.test.ts +320 -3
  33. package/src/__tests__/checker.test.ts +12 -12
  34. package/src/__tests__/compaction-events.test.ts +1 -0
  35. package/src/__tests__/compaction-trail-store.test.ts +264 -0
  36. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  37. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  38. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  39. package/src/__tests__/computer-use-tools.test.ts +12 -14
  40. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  41. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  42. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  43. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  44. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  45. package/src/__tests__/config-schema.test.ts +10 -10
  46. package/src/__tests__/connection-model-compat.test.ts +83 -0
  47. package/src/__tests__/contacts-tools.test.ts +3 -2
  48. package/src/__tests__/context-token-estimator.test.ts +22 -0
  49. package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
  50. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  52. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
  54. package/src/__tests__/conversation-agent-loop.test.ts +488 -2
  55. package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
  56. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  57. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
  58. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  61. package/src/__tests__/conversation-error.test.ts +30 -0
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -8
  63. package/src/__tests__/conversation-fork-route.test.ts +3 -2
  64. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  65. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  66. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  67. package/src/__tests__/conversation-lifecycle.test.ts +1 -0
  68. package/src/__tests__/conversation-list-source.test.ts +3 -2
  69. package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
  70. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
  71. package/src/__tests__/conversation-pairing.test.ts +53 -0
  72. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
  73. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  74. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
  75. package/src/__tests__/conversation-queue.test.ts +333 -291
  76. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
  77. package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
  78. package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
  79. package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
  80. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  81. package/src/__tests__/conversation-slash-queue.test.ts +84 -32
  82. package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
  83. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
  85. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
  90. package/src/__tests__/conversation-sync-tags.test.ts +128 -12
  91. package/src/__tests__/conversation-title-service.test.ts +1 -0
  92. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
  93. package/src/__tests__/conversation-usage.test.ts +1 -0
  94. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  95. package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
  96. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
  97. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  98. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  99. package/src/__tests__/credential-execution-client.test.ts +72 -1
  100. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  101. package/src/__tests__/credential-health-service.test.ts +252 -3
  102. package/src/__tests__/credential-security-invariants.test.ts +5 -5
  103. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  104. package/src/__tests__/credential-vault.test.ts +5 -5
  105. package/src/__tests__/cross-provider-web-search.test.ts +56 -2
  106. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  107. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  108. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  109. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  110. package/src/__tests__/db-test-helpers.ts +58 -0
  111. package/src/__tests__/disk-pressure-guard.test.ts +58 -41
  112. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  113. package/src/__tests__/disk-pressure-routes.test.ts +0 -33
  114. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  115. package/src/__tests__/dm-persistence.test.ts +26 -40
  116. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  117. package/src/__tests__/document-find-replace.test.ts +3 -2
  118. package/src/__tests__/document-tool-security.test.ts +81 -2
  119. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  120. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  121. package/src/__tests__/encrypted-store.test.ts +11 -9
  122. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  123. package/src/__tests__/filing-service.test.ts +1 -0
  124. package/src/__tests__/first-greeting.test.ts +62 -12
  125. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  126. package/src/__tests__/gemini-provider.test.ts +26 -0
  127. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  128. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  129. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  130. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
  131. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  132. package/src/__tests__/heartbeat-service.test.ts +1 -0
  133. package/src/__tests__/helpers/mock-logger.ts +26 -0
  134. package/src/__tests__/host-bash-routes.test.ts +1 -0
  135. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  136. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  137. package/src/__tests__/host-shell-tool.test.ts +5 -4
  138. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  139. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  140. package/src/__tests__/http-user-message-parity.test.ts +29 -7
  141. package/src/__tests__/identity-intro-cache.test.ts +133 -22
  142. package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
  143. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  144. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  145. package/src/__tests__/injector-disk-pressure.test.ts +3 -17
  146. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  147. package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
  148. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  149. package/src/__tests__/llm-resolver.test.ts +331 -0
  150. package/src/__tests__/llm-schema.test.ts +1 -1
  151. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  152. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  153. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  154. package/src/__tests__/messaging-send-tool.test.ts +1 -0
  155. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  156. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  157. package/src/__tests__/model-intents.test.ts +3 -3
  158. package/src/__tests__/native-web-search.test.ts +30 -2
  159. package/src/__tests__/notification-deep-link.test.ts +62 -0
  160. package/src/__tests__/oauth-commands-routes.test.ts +37 -0
  161. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  162. package/src/__tests__/oauth-store.test.ts +3 -2
  163. package/src/__tests__/onboarding-template-contract.test.ts +3 -2
  164. package/src/__tests__/openai-provider.test.ts +8 -9
  165. package/src/__tests__/openai-responses-provider.test.ts +70 -10
  166. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  167. package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
  168. package/src/__tests__/persistence-pipeline.test.ts +139 -1
  169. package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
  170. package/src/__tests__/plugin-bootstrap.test.ts +9 -11
  171. package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
  172. package/src/__tests__/process-message-background-slack.test.ts +21 -16
  173. package/src/__tests__/process-message-display-content.test.ts +19 -22
  174. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  175. package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
  176. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  177. package/src/__tests__/recording-handler.test.ts +1 -0
  178. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  179. package/src/__tests__/registry.test.ts +82 -76
  180. package/src/__tests__/relay-server.test.ts +10 -10
  181. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  182. package/src/__tests__/schedule-store.test.ts +16 -1
  183. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  184. package/src/__tests__/secret-ingress-http.test.ts +5 -1
  185. package/src/__tests__/secure-keys.test.ts +3 -3
  186. package/src/__tests__/send-endpoint-busy.test.ts +81 -42
  187. package/src/__tests__/server-history-render.test.ts +4 -1
  188. package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
  189. package/src/__tests__/skill-feature-flags.test.ts +14 -16
  190. package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
  191. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  192. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  193. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  194. package/src/__tests__/slack-channel-config.test.ts +3 -3
  195. package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
  196. package/src/__tests__/subagent-disposal.test.ts +27 -8
  197. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  198. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  199. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  200. package/src/__tests__/subagent-notify-parent.test.ts +5 -4
  201. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  202. package/src/__tests__/subagent-tools.test.ts +2 -1
  203. package/src/__tests__/suggestion-routes.test.ts +1 -0
  204. package/src/__tests__/system-prompt.test.ts +38 -0
  205. package/src/__tests__/test-preload-verifier.ts +68 -0
  206. package/src/__tests__/test-preload.ts +32 -39
  207. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  208. package/src/__tests__/tool-executor.test.ts +55 -10
  209. package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
  210. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  211. package/src/__tests__/twilio-routes.test.ts +3 -2
  212. package/src/__tests__/validate-input.test.ts +381 -0
  213. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  214. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
  215. package/src/__tests__/voice-session-bridge.test.ts +37 -28
  216. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  217. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  218. package/src/acp/session-manager.ts +5 -6
  219. package/src/agent/loop.ts +80 -0
  220. package/src/api/README.md +124 -2
  221. package/src/api/constants/call-sites.ts +27 -0
  222. package/src/api/events/assistant-outbound-attachment.ts +51 -0
  223. package/src/api/events/assistant-text-delta.ts +32 -0
  224. package/src/api/events/assistant-turn-start.ts +33 -0
  225. package/src/api/events/document-comment-created.ts +48 -0
  226. package/src/api/events/document-comment-deleted.ts +24 -0
  227. package/src/api/events/document-comment-reopened.ts +25 -0
  228. package/src/api/events/document-comment-resolved.ts +27 -0
  229. package/src/api/events/generation-cancelled.ts +24 -0
  230. package/src/api/events/generation-handoff.ts +41 -0
  231. package/src/api/events/message-complete.ts +42 -0
  232. package/src/api/events/open-url.ts +30 -0
  233. package/src/{events → api/events}/relationship-state-updated.ts +3 -3
  234. package/src/api/events/tool-use-start.ts +32 -0
  235. package/src/api/index.ts +128 -3
  236. package/src/api/responses/llm-context-response.ts +39 -0
  237. package/src/api/responses/llm-request-log-entry.ts +93 -0
  238. package/src/api/responses/memory-recall-log.ts +65 -0
  239. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  240. package/src/background-wake/background-wake-routes.test.ts +687 -52
  241. package/src/background-wake/platform-client.test.ts +308 -0
  242. package/src/background-wake/platform-client.ts +167 -0
  243. package/src/background-wake/publisher.ts +91 -0
  244. package/src/background-wake/runtime-registry.ts +2 -2
  245. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  246. package/src/calls/guardian-dispatch.ts +1 -0
  247. package/src/calls/voice-session-bridge.ts +4 -4
  248. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  249. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  250. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  251. package/src/cli/commands/channels/index.ts +229 -0
  252. package/src/cli/commands/memory-v3-render.ts +147 -0
  253. package/src/cli/commands/memory-v3.ts +255 -4
  254. package/src/cli/commands/notifications.ts +365 -55
  255. package/src/cli/lib/open-browser.ts +7 -2
  256. package/src/cli/program.ts +2 -0
  257. package/src/config/assistant-feature-flags.ts +23 -42
  258. package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
  259. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  260. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  261. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  262. package/src/config/call-site-defaults.ts +1 -1
  263. package/src/config/feature-flag-cache.ts +86 -0
  264. package/src/config/feature-flag-registry.json +17 -17
  265. package/src/config/llm-context-resolution.ts +10 -1
  266. package/src/config/llm-resolver.ts +121 -15
  267. package/src/config/loader.ts +4 -5
  268. package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
  269. package/src/config/schemas/heartbeat.ts +1 -1
  270. package/src/config/schemas/llm.ts +90 -1
  271. package/src/config/schemas/memory-v2.ts +26 -0
  272. package/src/config/schemas/services.ts +6 -2
  273. package/src/config/seed-inference-profiles.ts +36 -16
  274. package/src/context/token-estimator.ts +10 -5
  275. package/src/credential-execution/executable-discovery.ts +40 -0
  276. package/src/credential-execution/process-manager.ts +6 -2
  277. package/src/credential-health/credential-health-service.ts +125 -40
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  279. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
  280. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  281. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  282. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  283. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  284. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  285. package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
  286. package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
  287. package/src/daemon/conversation-agent-loop.ts +132 -28
  288. package/src/daemon/conversation-error.ts +33 -5
  289. package/src/daemon/conversation-messaging.ts +84 -43
  290. package/src/daemon/conversation-process.ts +74 -37
  291. package/src/daemon/conversation-runtime-assembly.ts +29 -9
  292. package/src/daemon/conversation-skill-tools.ts +14 -30
  293. package/src/daemon/conversation-surfaces.ts +69 -34
  294. package/src/daemon/conversation-tool-setup.ts +33 -48
  295. package/src/daemon/conversation.ts +26 -46
  296. package/src/daemon/daemon-control.ts +1 -1
  297. package/src/daemon/daemon-skill-host.ts +9 -2
  298. package/src/daemon/disk-pressure-guard.ts +27 -29
  299. package/src/daemon/first-greeting.ts +31 -13
  300. package/src/daemon/handlers/shared.ts +6 -1
  301. package/src/daemon/lifecycle.ts +12 -12
  302. package/src/daemon/mcp-reload-service.ts +1 -1
  303. package/src/daemon/meet-manifest-loader.ts +10 -17
  304. package/src/daemon/message-types/conversations.ts +20 -22
  305. package/src/daemon/message-types/document-comments.ts +8 -44
  306. package/src/daemon/message-types/home.ts +2 -2
  307. package/src/daemon/message-types/integrations.ts +2 -7
  308. package/src/daemon/message-types/messages.ts +23 -38
  309. package/src/daemon/message-types/subagents.ts +6 -0
  310. package/src/daemon/process-message.ts +9 -9
  311. package/src/daemon/providers-setup.ts +1 -1
  312. package/src/daemon/server.ts +16 -0
  313. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  314. package/src/daemon/tool-setup-types.ts +0 -6
  315. package/src/daemon/wake-target-adapter.ts +10 -0
  316. package/src/documents/document-store.ts +38 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  318. package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
  319. package/src/heartbeat/heartbeat-service.ts +63 -0
  320. package/src/home/__tests__/feed-writer.test.ts +161 -0
  321. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  322. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  323. package/src/home/feed-writer.ts +146 -7
  324. package/src/home/suggested-prompts.ts +27 -145
  325. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  326. package/src/ipc/gateway-client.test.ts +4 -1
  327. package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
  328. package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
  329. package/src/ipc/skill-routes/memory.ts +4 -3
  330. package/src/ipc/skill-routes/registries.ts +28 -29
  331. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  332. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  333. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  334. package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
  335. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  336. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  337. package/src/memory/conversation-attention-store.ts +17 -3
  338. package/src/memory/conversation-crud.ts +352 -112
  339. package/src/memory/db-connection.ts +29 -19
  340. package/src/memory/db-init.ts +4 -0
  341. package/src/memory/db-singleton.ts +77 -0
  342. package/src/memory/delivery-channels.ts +82 -0
  343. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  344. package/src/memory/graph/retriever.test.ts +3 -3
  345. package/src/memory/job-handlers/embedding.test.ts +3 -2
  346. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  347. package/src/memory/jobs-worker.ts +12 -1
  348. package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
  349. package/src/memory/llm-request-log-source-local.ts +24 -0
  350. package/src/memory/llm-request-log-source.ts +31 -0
  351. package/src/memory/llm-request-log-store.ts +188 -3
  352. package/src/memory/memory-v2-activation-log-store.ts +95 -1
  353. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  354. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  355. package/src/memory/migrations/index.ts +2 -0
  356. package/src/memory/schema/conversations.ts +9 -1
  357. package/src/memory/schema/inference.ts +0 -1
  358. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  359. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  362. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
  363. package/src/memory/v2/harness/metrics.ts +5 -1
  364. package/src/memory/v2/harness/replay-input.ts +19 -3
  365. package/src/memory/v2/harness/runner.ts +6 -0
  366. package/src/memory/v2/harness/trace.ts +6 -0
  367. package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
  368. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
  369. package/src/memory/v3/__tests__/edges.test.ts +144 -1
  370. package/src/memory/v3/__tests__/filter.test.ts +48 -0
  371. package/src/memory/v3/__tests__/gate.test.ts +96 -33
  372. package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
  373. package/src/memory/v3/__tests__/loop.test.ts +250 -5
  374. package/src/memory/v3/__tests__/scouts.test.ts +49 -0
  375. package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
  376. package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
  377. package/src/memory/v3/__tests__/traversal.test.ts +39 -0
  378. package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
  379. package/src/memory/v3/__tests__/validate.test.ts +32 -0
  380. package/src/memory/v3/coretrieval-seed.ts +240 -0
  381. package/src/memory/v3/edges.ts +58 -21
  382. package/src/memory/v3/filter.ts +27 -22
  383. package/src/memory/v3/gate.ts +51 -36
  384. package/src/memory/v3/index-composition.ts +18 -5
  385. package/src/memory/v3/loop.ts +65 -17
  386. package/src/memory/v3/scouts.ts +15 -4
  387. package/src/memory/v3/shadow-diff.ts +287 -0
  388. package/src/memory/v3/shadow-middleware.ts +44 -2
  389. package/src/memory/v3/traversal.ts +6 -1
  390. package/src/memory/v3/tree-walk.ts +6 -1
  391. package/src/memory/v3/validate.ts +56 -33
  392. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  393. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  394. package/src/notifications/adapters/slack.ts +45 -11
  395. package/src/notifications/broadcaster.ts +114 -63
  396. package/src/notifications/conversation-pairing.ts +23 -3
  397. package/src/notifications/decisions-store.ts +32 -1
  398. package/src/notifications/deliveries-store.ts +45 -0
  399. package/src/notifications/edit-notification.ts +201 -0
  400. package/src/notifications/emit-signal.ts +11 -1
  401. package/src/notifications/signal.ts +10 -0
  402. package/src/notifications/types.ts +37 -0
  403. package/src/oauth/byo-connection.test.ts +67 -3
  404. package/src/oauth/byo-connection.ts +32 -5
  405. package/src/oauth/connect-orchestrator.ts +9 -0
  406. package/src/oauth/connection-resolver.test.ts +76 -0
  407. package/src/oauth/connection-resolver.ts +49 -10
  408. package/src/oauth/manual-token-connection.ts +51 -3
  409. package/src/oauth/seed-providers.ts +3 -0
  410. package/src/permissions/approval-policy.test.ts +19 -5
  411. package/src/permissions/approval-policy.ts +14 -3
  412. package/src/permissions/checker.ts +21 -8
  413. package/src/platform/client.test.ts +24 -1
  414. package/src/platform/client.ts +8 -0
  415. package/src/platform/feature-gate.ts +15 -0
  416. package/src/plugins/defaults/injectors.ts +2 -8
  417. package/src/plugins/defaults/persistence.ts +25 -6
  418. package/src/plugins/types.ts +57 -13
  419. package/src/proactive-artifact/job.test.ts +1 -0
  420. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  421. package/src/prompts/system-prompt.ts +38 -40
  422. package/src/prompts/template-detection.ts +10 -4
  423. package/src/prompts/templates/BOOTSTRAP.md +7 -11
  424. package/src/prompts/templates/IDENTITY.md +0 -2
  425. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  426. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  427. package/src/providers/call-site-routing.ts +33 -9
  428. package/src/providers/connection-model-compat.ts +23 -0
  429. package/src/providers/connection-resolution.ts +39 -20
  430. package/src/providers/fireworks/client.ts +1 -0
  431. package/src/providers/gemini/client.ts +24 -3
  432. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  433. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  434. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  435. package/src/providers/inference/auth.ts +0 -8
  436. package/src/providers/inference/connections.ts +3 -66
  437. package/src/providers/inference/resolve-auth.ts +2 -3
  438. package/src/providers/model-catalog.ts +35 -1
  439. package/src/providers/model-intents.ts +3 -3
  440. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  441. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
  442. package/src/providers/openai/chat-completions-provider.ts +110 -12
  443. package/src/providers/openai/codex-models.ts +2 -0
  444. package/src/providers/openai/responses-provider.ts +53 -53
  445. package/src/providers/openrouter/client.ts +13 -8
  446. package/src/providers/provider-send-message.ts +18 -9
  447. package/src/providers/registry.ts +48 -8
  448. package/src/providers/retry.ts +16 -4
  449. package/src/providers/search-provider-catalog.ts +17 -9
  450. package/src/providers/types.ts +9 -0
  451. package/src/runtime/__tests__/agent-wake.test.ts +1 -0
  452. package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
  453. package/src/runtime/access-request-helper.ts +1 -0
  454. package/src/runtime/auth/route-policy.ts +10 -0
  455. package/src/runtime/channel-readiness-service.ts +68 -0
  456. package/src/runtime/channel-reply-delivery.ts +23 -0
  457. package/src/runtime/channel-retry-sweep.ts +47 -14
  458. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  459. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  460. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  461. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
  462. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  463. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  464. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  465. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
  466. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
  467. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
  468. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  469. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  470. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  471. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  472. package/src/runtime/routes/app-management-routes.ts +111 -4
  473. package/src/runtime/routes/background-wake-routes.ts +188 -20
  474. package/src/runtime/routes/btw-routes.ts +4 -4
  475. package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
  476. package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
  477. package/src/runtime/routes/conversation-list-routes.ts +147 -0
  478. package/src/runtime/routes/conversation-management-routes.ts +39 -14
  479. package/src/runtime/routes/conversation-query-routes.ts +60 -10
  480. package/src/runtime/routes/conversation-routes.ts +186 -140
  481. package/src/runtime/routes/conversations-import-routes.ts +19 -6
  482. package/src/runtime/routes/documents-routes.ts +10 -1
  483. package/src/runtime/routes/group-routes.ts +11 -0
  484. package/src/runtime/routes/home-feed-routes.ts +129 -0
  485. package/src/runtime/routes/identity-intro-cache.ts +61 -16
  486. package/src/runtime/routes/identity-routes.ts +30 -9
  487. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
  488. package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
  489. package/src/runtime/routes/index.ts +2 -0
  490. package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
  491. package/src/runtime/routes/integrations/vercel.ts +15 -0
  492. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  493. package/src/runtime/routes/memory-v3-routes.ts +160 -2
  494. package/src/runtime/routes/migration-routes.ts +20 -13
  495. package/src/runtime/routes/notification-routes.ts +63 -1
  496. package/src/runtime/routes/oauth-commands-routes.ts +6 -1
  497. package/src/runtime/routes/surface-action-routes.ts +1 -38
  498. package/src/runtime/routes/surface-content-routes.ts +12 -5
  499. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  500. package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
  501. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
  502. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  503. package/src/runtime/sync/resource-sync-events.ts +1 -1
  504. package/src/runtime/tool-grant-request-helper.ts +1 -0
  505. package/src/schedule/schedule-store.ts +8 -1
  506. package/src/schedule/scheduler.ts +111 -15
  507. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  508. package/src/security/encrypted-store.ts +7 -16
  509. package/src/security/store-path-override.ts +61 -0
  510. package/src/signals/user-message.ts +5 -8
  511. package/src/skills/validate-input.ts +177 -0
  512. package/src/subagent/manager.ts +13 -13
  513. package/src/subagent/types.ts +6 -0
  514. package/src/tasks/tool-sanitizer.ts +2 -2
  515. package/src/tools/apps/definitions.ts +35 -21
  516. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  517. package/src/tools/computer-use/definitions.ts +268 -266
  518. package/src/tools/document/document-tool.ts +131 -8
  519. package/src/tools/execution-target.ts +2 -5
  520. package/src/tools/executor.ts +18 -55
  521. package/src/tools/host-filesystem/edit.test.ts +1 -0
  522. package/src/tools/host-filesystem/read.test.ts +1 -0
  523. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  524. package/src/tools/host-filesystem/write.test.ts +1 -0
  525. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  526. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  527. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  528. package/src/tools/network/managed-search-proxy.ts +183 -0
  529. package/src/tools/network/web-search.ts +199 -44
  530. package/src/tools/policy-context.ts +3 -1
  531. package/src/tools/registry.ts +146 -103
  532. package/src/tools/schedule/create.ts +1 -1
  533. package/src/tools/skills/skill-tool-factory.ts +17 -36
  534. package/src/tools/subagent/spawn.ts +3 -0
  535. package/src/tools/tool-approval-handler.ts +10 -4
  536. package/src/tools/tool-name-aliases.ts +72 -14
  537. package/src/tools/types.ts +17 -15
  538. package/src/tools/ui-surface/definitions.ts +98 -86
  539. package/src/types/onboarding-context.ts +6 -0
  540. package/src/usage/attribution.ts +32 -1
  541. package/src/util/browser.ts +7 -2
  542. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  543. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  544. package/src/workspace/migrations/registry.ts +4 -0
@@ -46,13 +46,17 @@ const ImageGenerationServiceSchema = BaseServiceSchema.extend({
46
46
  });
47
47
 
48
48
  const WebSearchServiceSchema = BaseServiceSchema.extend({
49
+ // Provider choice for app-executed search in Your Own mode, or the native
50
+ // hosted-search preference when set to `inference-provider-native`. In
51
+ // Managed mode, non-native inference providers can still use the platform
52
+ // managed search proxy through the app-executed `web_search` tool.
49
53
  provider: z
50
54
  .enum(VALID_WEB_SEARCH_PROVIDERS)
51
55
  .default("inference-provider-native"),
52
56
  });
53
57
 
54
58
  const GoogleOAuthServiceSchema = BaseServiceSchema.extend({
55
- mode: ServiceModeSchema.default("your-own"),
59
+ mode: ServiceModeSchema.default("managed"),
56
60
  });
57
61
 
58
62
  const OutlookOAuthServiceSchema = BaseServiceSchema.extend({
@@ -68,7 +72,7 @@ const GitHubOAuthServiceSchema = BaseServiceSchema.extend({
68
72
  });
69
73
 
70
74
  const NotionOAuthServiceSchema = BaseServiceSchema.extend({
71
- mode: ServiceModeSchema.default("your-own"),
75
+ mode: ServiceModeSchema.default("managed"),
72
76
  });
73
77
 
74
78
  const TwitterOAuthServiceSchema = BaseServiceSchema.extend({
@@ -1,7 +1,6 @@
1
1
  import type { DrizzleDb } from "../memory/db-connection.js";
2
2
  import {
3
3
  createConnection,
4
- disableManagedConnectionsForByokHatch,
5
4
  getConnection,
6
5
  MANAGED_CONNECTION_NAMES,
7
6
  PROVIDERS_REQUIRING_BASE_URL_AND_MODELS,
@@ -123,9 +122,18 @@ const USER_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
123
122
  },
124
123
  };
125
124
 
126
- export const MANAGED_PROFILE_NAMES = new Set(
127
- Object.keys(MANAGED_PROFILE_TEMPLATES),
128
- );
125
+ /**
126
+ * The "auto" profile key. When active, the daemon injects the
127
+ * `switch_inference_profile` tool and lets the model self-select a profile
128
+ * per query. No provider/model — the resolver falls through to the call-site
129
+ * default (balanced or custom-balanced for BYOK).
130
+ */
131
+ export const AUTO_PROFILE_KEY = "auto";
132
+
133
+ export const MANAGED_PROFILE_NAMES = new Set([
134
+ ...Object.keys(MANAGED_PROFILE_TEMPLATES),
135
+ AUTO_PROFILE_KEY,
136
+ ]);
129
137
 
130
138
  export type SeedInferenceProfilesOptions = {
131
139
  /**
@@ -263,21 +271,28 @@ export function seedInferenceProfiles(
263
271
  profiles[name] = next as ProfileEntry;
264
272
  }
265
273
 
274
+ // 1b. Auto profile — a metadata-only profile with no provider/model. When
275
+ // the user selects "Auto", the resolver falls through to the call-site
276
+ // default (balanced or custom-balanced), and the agent loop injects the
277
+ // switch_inference_profile tool so the model can self-select per query.
278
+ if (!preservedProfileNames.has(AUTO_PROFILE_KEY)) {
279
+ const previousAuto = readObject(profiles[AUTO_PROFILE_KEY]);
280
+ const autoEntry: Record<string, unknown> = {
281
+ source: "managed",
282
+ label: "Auto",
283
+ description:
284
+ "Automatically routes each query to the best profile — fast for simple questions, capable for complex ones",
285
+ };
286
+ if (previousAuto) {
287
+ if ("label" in previousAuto) autoEntry.label = previousAuto.label;
288
+ if ("status" in previousAuto) autoEntry.status = previousAuto.status;
289
+ }
290
+ profiles[AUTO_PROFILE_KEY] = autoEntry as ProfileEntry;
291
+ }
292
+
266
293
  // 2. User profiles — only at hatch time for off-platform installations.
267
294
  let userConnectionName: string | undefined;
268
295
  if (options.isHatch && !isPlatform) {
269
- // BYOK hatch: disable canonical managed connections so the picker doesn't
270
- // surface unusable platform-auth options on day one. If the hatch overlay
271
- // selected a managed profile, leave that connection active; the user has
272
- // already chosen managed inference. Runs only here, only at hatch —
273
- // `seedCanonicalConnections` leaves `status` alone on subsequent boots so
274
- // a post-hatch user re-enable persists.
275
- if (options.db) {
276
- disableManagedConnectionsForByokHatch(options.db, {
277
- excludeConnection: hatchSelectedManagedConnection,
278
- });
279
- }
280
-
281
296
  const hatchProvider = readString(readObject(llm.default)?.provider);
282
297
  if (
283
298
  hatchProvider &&
@@ -336,10 +351,15 @@ export function seedInferenceProfiles(
336
351
  }
337
352
 
338
353
  // Profile ordering — ensure all seeded profiles appear in the order array.
354
+ // "auto" is prepended so it appears first in the picker.
339
355
  const profileOrder = Array.isArray(llm.profileOrder)
340
356
  ? (llm.profileOrder as string[])
341
357
  : [];
342
358
  const orderSet = new Set(profileOrder);
359
+ if (!orderSet.has(AUTO_PROFILE_KEY)) {
360
+ profileOrder.unshift(AUTO_PROFILE_KEY);
361
+ orderSet.add(AUTO_PROFILE_KEY);
362
+ }
343
363
  for (const name of Object.keys(MANAGED_PROFILE_TEMPLATES)) {
344
364
  if (!orderSet.has(name)) {
345
365
  profileOrder.push(name);
@@ -154,11 +154,16 @@ function estimateGeminiImageTokens(width: number, height: number): number {
154
154
  ) {
155
155
  return GEMINI_IMAGE_TOKENS_PER_TILE;
156
156
  }
157
- // Gemini resizes images so the longest side is ≤3072px before tiling.
158
- const clampedWidth = Math.min(width, GEMINI_IMAGE_MAX_DIMENSION);
159
- const clampedHeight = Math.min(height, GEMINI_IMAGE_MAX_DIMENSION);
160
- const tilesWide = Math.ceil(clampedWidth / GEMINI_IMAGE_TILE_SIZE);
161
- const tilesHigh = Math.ceil(clampedHeight / GEMINI_IMAGE_TILE_SIZE);
157
+ // Gemini rescales both dimensions by a single aspect-preserving factor so
158
+ // the longest side is ≤3072px before tiling. Clamping each side
159
+ // independently would over-count tiles for extreme aspect ratios
160
+ // (e.g. 10000×1000 3072×307, not 3072×1000).
161
+ const scale = Math.min(
162
+ 1,
163
+ GEMINI_IMAGE_MAX_DIMENSION / Math.max(width, height),
164
+ );
165
+ const tilesWide = Math.ceil((width * scale) / GEMINI_IMAGE_TILE_SIZE);
166
+ const tilesHigh = Math.ceil((height * scale) / GEMINI_IMAGE_TILE_SIZE);
162
167
  return tilesWide * tilesHigh * GEMINI_IMAGE_TOKENS_PER_TILE;
163
168
  }
164
169
 
@@ -212,3 +212,43 @@ export function discoverCes(): DiscoveryResult {
212
212
  }
213
213
  return discoverLocalCes();
214
214
  }
215
+
216
+ /** How long to poll for the managed bootstrap socket before giving up. */
217
+ const MANAGED_DISCOVERY_TIMEOUT_MS = 3_000;
218
+
219
+ /** Delay between managed bootstrap-socket discovery attempts. */
220
+ const MANAGED_DISCOVERY_INTERVAL_MS = 100;
221
+
222
+ /**
223
+ * Discover CES, polling for the managed bootstrap socket with a short
224
+ * backoff before failing.
225
+ *
226
+ * The managed CES sidecar re-binds its bootstrap socket after each assistant
227
+ * session ends — it outlives any single assistant and accepts the next
228
+ * connection (see `credential-executor/src/managed-main.ts`). A reconnecting
229
+ * assistant (e.g. after a container restart) can therefore probe during the
230
+ * brief window before the socket is re-bound. A single existence check would
231
+ * race that window and incorrectly report the sidecar as unavailable; polling
232
+ * absorbs the gap.
233
+ *
234
+ * Local discovery is returned immediately — a missing binary will not appear
235
+ * by waiting, so there is nothing to poll for.
236
+ */
237
+ export async function discoverCesWithRetry({
238
+ timeoutMs = MANAGED_DISCOVERY_TIMEOUT_MS,
239
+ intervalMs = MANAGED_DISCOVERY_INTERVAL_MS,
240
+ }: { timeoutMs?: number; intervalMs?: number } = {}): Promise<DiscoveryResult> {
241
+ // Only the managed bootstrap socket is worth polling for; a missing local
242
+ // binary will not appear by waiting.
243
+ if (!getIsContainerized()) {
244
+ return discoverCes();
245
+ }
246
+
247
+ const deadline = Date.now() + timeoutMs;
248
+ let result = discoverCes();
249
+ while (result.mode === "unavailable" && Date.now() < deadline) {
250
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
251
+ result = discoverCes();
252
+ }
253
+ return result;
254
+ }
@@ -29,7 +29,7 @@ import { ensureBun } from "../util/bun-runtime.js";
29
29
  import { getLogger } from "../util/logger.js";
30
30
  import type { CesTransport } from "./client.js";
31
31
  import {
32
- discoverCes,
32
+ discoverCesWithRetry,
33
33
  discoverLocalCes,
34
34
  type DiscoveryResult,
35
35
  type LocalDiscoverySuccess,
@@ -118,7 +118,11 @@ export function createCesProcessManager(
118
118
  throw new Error("CES process manager is already running");
119
119
  }
120
120
 
121
- discoveryResult = await discoverCes();
121
+ // Poll for the managed bootstrap socket with a short backoff. The CES
122
+ // sidecar re-binds its socket after each assistant session ends, so a
123
+ // reconnecting assistant can briefly race the re-bind; a single probe
124
+ // would otherwise fall back to local discovery and fail in managed mode.
125
+ discoveryResult = await discoverCesWithRetry();
122
126
  if (discoveryResult.mode === "unavailable") {
123
127
  // The managed sidecar bootstrap socket is not present — this happens
124
128
  // when the instance pre-dates the socket volume mount (e.g. existing
@@ -7,8 +7,10 @@
7
7
  * - Scope coverage (grantedScopes vs provider defaultScopes)
8
8
  * - Liveness ping (for providers with a pingUrl)
9
9
  *
10
- * Designed to run during the heartbeat cycle. All checks are diagnostic
11
- * no token refresh or recovery is attempted.
10
+ * Designed to run during the heartbeat cycle. The BYO liveness ping is
11
+ * routed through `withValidToken`, so a stale-but-refreshable access token
12
+ * is refreshed transparently before the ping fires — this prevents the
13
+ * heartbeat from misreporting a refreshable connection as `"revoked"`.
12
14
  */
13
15
 
14
16
  import { isTokenExpired } from "@vellumai/credential-storage";
@@ -22,6 +24,10 @@ import {
22
24
  type OAuthConnectionRow,
23
25
  type OAuthProviderRow,
24
26
  } from "../oauth/oauth-store.js";
27
+ import {
28
+ TokenExpiredError,
29
+ withValidToken,
30
+ } from "../security/token-manager.js";
25
31
  import { getLogger } from "../util/logger.js";
26
32
 
27
33
  const log = getLogger("credential-health");
@@ -125,6 +131,45 @@ async function pingProvider(
125
131
  }
126
132
  }
127
133
 
134
+ /**
135
+ * Map a pingProvider result onto a CredentialHealthResult, or null when the
136
+ * ping succeeded. `authErrorContext` distinguishes whether a 401/403 came
137
+ * after a refresh attempt (refreshable connection) or directly from the
138
+ * stored token (manual-token / no-refresh connection) — the latter is the
139
+ * historical message wording.
140
+ */
141
+ type PingFailureContext = "after_refresh" | "no_refresh";
142
+
143
+ function pingResultToHealthFailure(
144
+ base: Omit<CredentialHealthResult, "status" | "details" | "canAutoRecover">,
145
+ provider: string,
146
+ connectionId: string,
147
+ pingResult: { ok: boolean; authError: boolean },
148
+ authErrorContext: PingFailureContext,
149
+ ): CredentialHealthResult | null {
150
+ if (pingResult.ok) return null;
151
+ if (pingResult.authError) {
152
+ const suffix =
153
+ authErrorContext === "after_refresh" ? " after a refresh attempt" : "";
154
+ return {
155
+ ...base,
156
+ status: "revoked",
157
+ details: `${provider} token was rejected (401/403)${suffix}. The token may have been revoked. Re-authorization required.`,
158
+ canAutoRecover: false,
159
+ };
160
+ }
161
+ log.debug(
162
+ { provider, connectionId },
163
+ "Credential ping failed with non-auth error",
164
+ );
165
+ return {
166
+ ...base,
167
+ status: "ping_failed",
168
+ details: `${provider} liveness check failed (non-auth error). This may be transient.`,
169
+ canAutoRecover: false,
170
+ };
171
+ }
172
+
128
173
  // ── Core check ────────────────────────────────────────────────────────
129
174
 
130
175
  interface CheckConnectionOpts {
@@ -192,14 +237,18 @@ async function checkConnection(
192
237
  const token = tokenResult.value;
193
238
 
194
239
  // 2. Check token expiry
195
- if (isTokenExpired(expiresAt)) {
240
+ //
241
+ // When the token is expired AND there is no refresh token, we have no path
242
+ // to recovery — short-circuit to "expired". When there IS a refresh token,
243
+ // we fall through so the ping (via withValidToken) can attempt a refresh
244
+ // and confirm whether the refresh token still works. Without that, we'd
245
+ // return "expiring" speculatively even when the refresh token was revoked.
246
+ if (isTokenExpired(expiresAt) && !hasRefreshToken) {
196
247
  return {
197
248
  ...base,
198
- status: hasRefreshToken ? "expiring" : "expired",
199
- details: hasRefreshToken
200
- ? `Token for ${provider} is expired but has a refresh token — auto-recovery may work.`
201
- : `Token for ${provider} is expired with no refresh token. Re-authorization required.`,
202
- canAutoRecover: hasRefreshToken,
249
+ status: "expired",
250
+ details: `Token for ${provider} is expired with no refresh token. Re-authorization required.`,
251
+ canAutoRecover: false,
203
252
  };
204
253
  }
205
254
 
@@ -234,36 +283,51 @@ async function checkConnection(
234
283
  }
235
284
 
236
285
  // 4. Liveness ping
286
+ //
287
+ // For refreshable connections we route through withValidToken so an
288
+ // expired-but-refreshable token gets refreshed before the ping fires.
289
+ // Without this wrapping, the ping would hit 401 on a stale token and the
290
+ // connection would be misreported as "revoked" — even though the next real
291
+ // API call (which goes through BYOOAuthConnection.request → withValidToken)
292
+ // would have refreshed and succeeded.
237
293
  if (pingUrl) {
238
- const pingResult = await pingProvider(
239
- token,
240
- pingUrl,
241
- pingMethod,
242
- pingHeaders,
243
- pingBody,
244
- );
245
- if (!pingResult.ok) {
246
- if (pingResult.authError) {
247
- return {
248
- ...base,
249
- status: "revoked",
250
- details: `${provider} token was rejected (401/403). The token may have been revoked. Re-authorization required.`,
251
- canAutoRecover: false,
252
- };
294
+ const runPing = (t: string) =>
295
+ pingProvider(t, pingUrl, pingMethod, pingHeaders, pingBody);
296
+
297
+ let pingResult: { ok: boolean; authError: boolean };
298
+ let authContext: PingFailureContext;
299
+
300
+ if (hasRefreshToken) {
301
+ try {
302
+ pingResult = await withValidToken(provider, runPing, { connectionId });
303
+ } catch (err) {
304
+ if (err instanceof TokenExpiredError) {
305
+ // Refresh itself failed (revoked refresh token, invalid_grant, etc.)
306
+ return {
307
+ ...base,
308
+ status: "revoked",
309
+ details: `${provider} token refresh failed. The refresh token may have been revoked. Re-authorization required.`,
310
+ canAutoRecover: false,
311
+ };
312
+ }
313
+ throw err;
253
314
  }
254
- // Non-auth ping failure — log but don't mark as critical.
255
- // Could be a transient API issue.
256
- log.debug(
257
- { provider, connectionId },
258
- "Credential ping failed with non-auth error",
259
- );
260
- return {
261
- ...base,
262
- status: "ping_failed",
263
- details: `${provider} liveness check failed (non-auth error). This may be transient.`,
264
- canAutoRecover: false,
265
- };
315
+ authContext = "after_refresh";
316
+ } else {
317
+ // Manual-token provider or an OAuth provider whose initial flow
318
+ // didn't return a refresh_token — nothing to refresh, ping directly.
319
+ pingResult = await runPing(token);
320
+ authContext = "no_refresh";
266
321
  }
322
+
323
+ const failure = pingResultToHealthFailure(
324
+ base,
325
+ provider,
326
+ connectionId,
327
+ pingResult,
328
+ authContext,
329
+ );
330
+ if (failure) return failure;
267
331
  }
268
332
 
269
333
  return {
@@ -434,10 +498,28 @@ async function checkManagedProvider(
434
498
  canAutoRecover: true,
435
499
  });
436
500
  } else if (pingResp.status === 401 || pingResp.status === 403) {
501
+ // 401/403 from the upstream provider after the platform proxy
502
+ // forwarded the request. From the daemon side we can't tell
503
+ // whether the proxy attempted (and failed) a refresh-before-
504
+ // forward or skipped refresh entirely and forwarded a stale token.
505
+ // Either way it's not definitively unrecoverable — the next ping
506
+ // cycle may succeed if the platform re-refreshes. Demote to
507
+ // ping_failed so we don't fire a user-facing reconnect alert on
508
+ // what may be a transient platform-side miss. Only platform-
509
+ // attested 424 (CredentialRequiredError below) signals genuine
510
+ // unrecoverable failure.
511
+ log.debug(
512
+ {
513
+ provider: providerRow.provider,
514
+ connectionId: conn.id,
515
+ status: pingResp.status,
516
+ },
517
+ "Managed credential ping returned 401/403 — treating as potentially transient",
518
+ );
437
519
  results.push({
438
520
  ...base,
439
- status: "revoked",
440
- details: `${providerRow.provider} managed token was rejected (${pingResp.status}). Reconnect on the Vellum platform.`,
521
+ status: "ping_failed",
522
+ details: `${providerRow.provider} managed liveness check returned ${pingResp.status} from the upstream provider. The Vellum platform may not have refreshed the token before forwarding; will retry next cycle.`,
441
523
  canAutoRecover: false,
442
524
  });
443
525
  } else {
@@ -450,8 +532,11 @@ async function checkManagedProvider(
450
532
  }
451
533
  } catch (err) {
452
534
  const msg = err instanceof Error ? err.message : String(err);
453
- // CredentialRequiredError means the platform can't materialize
454
- // the token treat as revoked.
535
+ // CredentialRequiredError corresponds to a 424 from the platform
536
+ // proxy — the platform attempted (and gave up on) refresh and is
537
+ // telling us the credential is genuinely unrecoverable. This is
538
+ // the only managed-path signal we trust enough to fire a user-
539
+ // facing reconnect alert.
455
540
  if (
456
541
  err &&
457
542
  typeof err === "object" &&
@@ -461,7 +546,7 @@ async function checkManagedProvider(
461
546
  results.push({
462
547
  ...base,
463
548
  status: "revoked",
464
- details: `${providerRow.provider} managed connection is no longer valid. Reconnect on the Vellum platform.`,
549
+ details: `${providerRow.provider} managed connection cannot be refreshed by the Vellum platform. Reconnect on the Vellum platform.`,
465
550
  canAutoRecover: false,
466
551
  });
467
552
  } else {
@@ -26,12 +26,9 @@
26
26
 
27
27
  import { beforeEach, describe, expect, mock, test } from "bun:test";
28
28
 
29
- mock.module("../../util/logger.js", () => ({
30
- getLogger: () =>
31
- new Proxy({} as Record<string, unknown>, {
32
- get: () => () => {},
33
- }),
34
- }));
29
+ import { createMockLoggerModule } from "../../__tests__/helpers/mock-logger.js";
30
+
31
+ mock.module("../../util/logger.js", () => createMockLoggerModule());
35
32
 
36
33
  // ---------------------------------------------------------------------------
37
34
  // Mock state — reset between tests.
@@ -44,10 +44,10 @@ mock.module("../../runtime/assistant-event.js", () => ({
44
44
  ...realEvent,
45
45
  // Pass-through so `focus` / `conversationId` can be asserted directly
46
46
  // on the captured event's `message` payload.
47
- buildAssistantEvent: (
48
- message: unknown,
49
- conversationId?: string,
50
- ) => ({ message, conversationId }),
47
+ buildAssistantEvent: (message: unknown, conversationId?: string) => ({
48
+ message,
49
+ conversationId,
50
+ }),
51
51
  }));
52
52
 
53
53
  // Stub DB helpers so the real `launchConversation` can run without a DB.
@@ -73,6 +73,7 @@ mock.module("../../memory/conversation-crud.js", () => ({
73
73
  ) => {
74
74
  updateTitleCalls.push({ conversationId, title });
75
75
  },
76
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
76
77
  }));
77
78
 
78
79
  // Stub conversation-store so the real `launchConversation` can hydrate
@@ -107,10 +108,7 @@ let processStartedPromise = new Promise<void>((resolve) => {
107
108
  const realProcessMessage = await import("../process-message.js");
108
109
  mock.module("../process-message.js", () => ({
109
110
  ...realProcessMessage,
110
- processMessageInBackground: (
111
- conversationId: string,
112
- content: string,
113
- ) => {
111
+ processMessageInBackground: (conversationId: string, content: string) => {
114
112
  processMessageCalls.push({ conversationId, content });
115
113
  markProcessStarted();
116
114
  return new Promise((resolve, reject) => {
@@ -177,8 +175,8 @@ function makeContext(
177
175
  surfaceActionRequestIds: new Set<string>(),
178
176
  currentTurnSurfaces: [],
179
177
  isProcessing: () => false,
180
- enqueueMessage: (content: string) => {
181
- enqueueCalls.push({ content });
178
+ enqueueMessage: (options) => {
179
+ enqueueCalls.push({ content: options.content });
182
180
  return { queued: false, requestId: "enq-req" };
183
181
  },
184
182
  getQueueDepth: () => 0,
@@ -256,7 +254,7 @@ describe("handleSurfaceAction — launch_conversation dispatch", () => {
256
254
 
257
255
  test("launches new conversation with inherited trust context and no chat message", async () => {
258
256
  nextKeyStoreResult = { conversationId: "conv-launched-1" };
259
-
257
+
260
258
  const originTrustContext: TrustContext = {
261
259
  sourceChannel: "vellum",
262
260
  trustClass: "guardian",
@@ -351,7 +349,7 @@ describe("handleSurfaceAction — launch_conversation dispatch", () => {
351
349
 
352
350
  test("omits originTrustContext when origin conversation has none", async () => {
353
351
  nextKeyStoreResult = { conversationId: "conv-launched-3" };
354
-
352
+
355
353
  // No `trustContext` on the origin context — simulating the
356
354
  // no-inherited-guardian path.
357
355
  const ctx = makeContext();
@@ -382,7 +380,7 @@ describe("handleSurfaceAction — launch_conversation dispatch", () => {
382
380
 
383
381
  test("handler returns before the seed turn resolves (fire-and-forget)", async () => {
384
382
  nextKeyStoreResult = { conversationId: "conv-nonblocking" };
385
-
383
+
386
384
  const ctx = makeContext();
387
385
  registerCardSurface(ctx, "surface-4");
388
386
 
@@ -413,7 +411,7 @@ describe("handleSurfaceAction — launch_conversation dispatch", () => {
413
411
 
414
412
  test("seed turn rejection is swallowed by the helper's .catch()", async () => {
415
413
  nextKeyStoreResult = { conversationId: "conv-seed-fails" };
416
-
414
+
417
415
  const ctx = makeContext();
418
416
  registerCardSurface(ctx, "surface-5");
419
417
 
@@ -449,7 +447,7 @@ describe("handleSurfaceAction — launch_conversation dispatch", () => {
449
447
  // message and triggering a full LLM round-trip on every click. The plan
450
448
  // claimed to eliminate that round-trip; this test enforces it.
451
449
  nextKeyStoreResult = { conversationId: "conv-pending-set" };
452
-
450
+
453
451
  const ctx = makeContext();
454
452
  registerCardSurface(ctx, "surface-pending");
455
453
  // Simulate `ui_show` having stamped a pending entry for this surface
@@ -29,7 +29,6 @@ function mcpTool(name: string): Tool {
29
29
  return {
30
30
  name,
31
31
  description: name,
32
- origin: "mcp",
33
32
  input_schema: def(name).input_schema,
34
33
  } as unknown as Tool;
35
34
  }
@@ -89,7 +88,7 @@ describe("createResolveToolsCallback — config.tools.exclude", () => {
89
88
  });
90
89
 
91
90
  test("excluded MCP tool is omitted from the resolved tool list", () => {
92
- registerMcpTools([
91
+ registerMcpTools("test-server", [
93
92
  mcpTool("mcp__server__navigate"),
94
93
  mcpTool("mcp__server__click"),
95
94
  ]);
@@ -95,6 +95,7 @@ mock.module("../../security/secure-keys.js", () => ({
95
95
 
96
96
  mock.module("../../memory/conversation-crud.js", () => ({
97
97
  addMessage: async () => ({ id: "msg-123" }),
98
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
98
99
  }));
99
100
 
100
101
  mock.module("../../runtime/agent-wake.js", () => ({
@@ -108,6 +109,7 @@ mock.module("../../runtime/assistant-event-hub.js", () => ({
108
109
  publish: publishSpy,
109
110
  subscribe: subscribeSpy,
110
111
  },
112
+ broadcastMessage: async () => {},
111
113
  }));
112
114
 
113
115
  mock.module("../../runtime/assistant-event.js", () => ({
@@ -163,14 +163,23 @@ describe("loadMeetManifestProxies", () => {
163
163
  const capturedRoutes: SkillRoute[] = [];
164
164
  const capturedHooks: string[] = [];
165
165
 
166
+ // Capture both the owner (first arg) and the provider (second arg) so
167
+ // we can assert the meet-join skill id flows through to the registry
168
+ // call. Ownership lives on the registry, not on the `Tool` object,
169
+ // so the only place to check it is on this argument.
170
+ const capturedOwners: Array<{ kind: string; id: string }> = [];
166
171
  await loadMeetManifestProxies(supervisor, {
167
172
  manifestPath,
168
- registerTools: (p) => capturedProviders.push(p),
173
+ registerTools: (owner, p) => {
174
+ capturedOwners.push(owner);
175
+ capturedProviders.push(p);
176
+ },
169
177
  registerRoute: (r) => capturedRoutes.push(r),
170
178
  registerShutdown: (name) => capturedHooks.push(name),
171
179
  });
172
180
 
173
181
  expect(capturedProviders).toHaveLength(1);
182
+ expect(capturedOwners).toEqual([{ kind: "skill", id: "meet-join" }]);
174
183
  const tools = capturedProviders[0]!();
175
184
  expect(tools).toHaveLength(1);
176
185
  const t = tools[0]!;
@@ -178,14 +187,10 @@ describe("loadMeetManifestProxies", () => {
178
187
  expect(t.description).toBe("Fixture demo tool");
179
188
  expect(t.category).toBe("meet");
180
189
  expect(t.defaultRiskLevel).toBe(RiskLevel.Medium);
181
- expect(t.executionMode).toBe("proxy");
182
- expect(t.origin).toBe("skill");
183
- expect(t.ownerSkillId).toBe("meet-join");
184
- expect(t.ownerSkillBundled).toBe(true);
185
- expect(t.ownerSkillVersionHash).toBe(FIXTURE_MANIFEST.sourceHash);
186
- expect(t.input_schema).toEqual(
187
- FIXTURE_MANIFEST.tools[0]!.input_schema,
188
- );
190
+ // Ownership now lives on the assistant-side registry's ownersByName map
191
+ // (recorded by registerSkillTools at the IPC boundary), not on the Tool
192
+ // object — the daemon-side projection doesn't carry the kind.
193
+ expect(t.input_schema).toEqual(FIXTURE_MANIFEST.tools[0]!.input_schema);
189
194
  });
190
195
 
191
196
  test("proxy tool execute dispatches through supervisor.dispatchTool and returns the result", async () => {
@@ -196,7 +201,11 @@ describe("loadMeetManifestProxies", () => {
196
201
 
197
202
  await loadMeetManifestProxies(stub.supervisor, {
198
203
  manifestPath,
199
- registerTools: (p) => captured.push(p),
204
+ // Mock signature mirrors `registerExternalTools(owner, provider)` —
205
+ // ownership is recorded by the registry-side argument, not by the
206
+ // `Tool` object. This test doesn't care which owner flows through;
207
+ // the round-trip dispatch behavior is what's under test.
208
+ registerTools: (_owner, p) => captured.push(p),
200
209
  registerRoute: () => undefined,
201
210
  registerShutdown: () => undefined,
202
211
  });
@@ -238,7 +247,11 @@ describe("loadMeetManifestProxies", () => {
238
247
 
239
248
  await loadMeetManifestProxies(stub.supervisor, {
240
249
  manifestPath,
241
- registerTools: (p) => captured.push(p),
250
+ // Mock signature mirrors `registerExternalTools(owner, provider)` —
251
+ // ownership is recorded by the registry-side argument, not by the
252
+ // `Tool` object. This test doesn't care which owner flows through;
253
+ // the round-trip dispatch behavior is what's under test.
254
+ registerTools: (_owner, p) => captured.push(p),
242
255
  registerRoute: () => undefined,
243
256
  registerShutdown: () => undefined,
244
257
  });
@@ -415,7 +428,7 @@ describe("loadMeetManifestProxies", () => {
415
428
  await expect(
416
429
  loadMeetManifestProxies(stub.supervisor, {
417
430
  manifestPath,
418
- registerTools: (p) => {
431
+ registerTools: (_owner, p) => {
419
432
  providerInvoked = true;
420
433
  p();
421
434
  },
@@ -42,6 +42,7 @@ mock.module("../../memory/conversation-crud.js", () => ({
42
42
  getMessageById: () => null,
43
43
  updateMessageContent: () => {},
44
44
  provenanceFromTrustContext: () => ({}),
45
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
45
46
  }));
46
47
 
47
48
  mock.module("../../memory/llm-request-log-store.js", () => ({