@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
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * When IS_PLATFORM=true and no config.json exists yet, loadConfig() must
3
- * write all eight managed-capable service modes as "managed" instead of the
4
- * schema default "your-own". When IS_PLATFORM is absent/false, or when
5
- * config.json already exists, the schema defaults and existing values are
6
- * preserved unchanged.
3
+ * write all managed-capable service modes as "managed" instead of the
4
+ * per-service schema default. When IS_PLATFORM is absent/false, or when
5
+ * config.json already exists, the Zod schema defaults and existing values
6
+ * are preserved unchanged — note that `google-oauth` and `notion-oauth`
7
+ * default to "managed" at the schema level (per JARVIS-966), while every
8
+ * other managed-capable service defaults to "your-own".
7
9
  */
8
10
 
9
11
  import {
@@ -59,7 +61,7 @@ afterAll(() => {
59
61
 
60
62
  import { invalidateConfigCache, loadConfig } from "../config/loader.js";
61
63
  import { applyContextDefaultsToRawConfig } from "../runtime/routes/conversation-query-routes.js";
62
- import { _setStorePath } from "../security/encrypted-store.js";
64
+ import { setStorePathForTesting } from "./encrypted-store-test-helpers.js";
63
65
 
64
66
  // ---------------------------------------------------------------------------
65
67
  // Helpers
@@ -91,6 +93,9 @@ function readConfig(): Record<string, unknown> {
91
93
  return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
92
94
  }
93
95
 
96
+ // When IS_PLATFORM=true, every managed-capable service defaults to "managed".
97
+ // Without IS_PLATFORM, services split by Zod schema default: google/notion-oauth
98
+ // resolve to "managed" (per JARVIS-966), the rest resolve to "your-own".
94
99
  const MANAGED_SERVICES = [
95
100
  "image-generation",
96
101
  "web-search",
@@ -101,6 +106,28 @@ const MANAGED_SERVICES = [
101
106
  "notion-oauth",
102
107
  ] as const;
103
108
 
109
+ /**
110
+ * Services whose Zod schema default is `"managed"` rather than `"your-own"`.
111
+ * For these, the fresh-write and in-memory paths produce `"managed"` even
112
+ * when IS_PLATFORM is false/unset — the schema default applies regardless of
113
+ * deployment context. See `assistant/src/config/schemas/services.ts` and
114
+ * JARVIS-966: managed mode is the optimization target for new users on
115
+ * managed-capable infra, since the BYO flow requires a Google Cloud /
116
+ * Notion integration setup that most users never complete.
117
+ */
118
+ const SCHEMA_MANAGED_DEFAULT_SERVICES = [
119
+ "google-oauth",
120
+ "notion-oauth",
121
+ ] as const;
122
+
123
+ const SCHEMA_YOUR_OWN_DEFAULT_SERVICES = [
124
+ "image-generation",
125
+ "web-search",
126
+ "outlook-oauth",
127
+ "linear-oauth",
128
+ "github-oauth",
129
+ ] as const;
130
+
104
131
  // ---------------------------------------------------------------------------
105
132
  // Tests
106
133
  // ---------------------------------------------------------------------------
@@ -110,12 +137,12 @@ describe("platform-managed config defaults", () => {
110
137
 
111
138
  beforeEach(() => {
112
139
  resetWorkspace();
113
- _setStorePath(join(WORKSPACE_DIR, "keys.enc"));
140
+ setStorePathForTesting(join(WORKSPACE_DIR, "keys.enc"));
114
141
  invalidateConfigCache();
115
142
  });
116
143
 
117
144
  afterEach(() => {
118
- _setStorePath(null);
145
+ setStorePathForTesting(null);
119
146
  invalidateConfigCache();
120
147
  // Restore env to its original value
121
148
  if (originalIsPlatform === undefined) {
@@ -137,9 +164,12 @@ describe("platform-managed config defaults", () => {
137
164
  for (const svc of MANAGED_SERVICES) {
138
165
  expect((services[svc] as { mode?: string })?.mode).toBe("managed");
139
166
  }
167
+ expect((services["web-search"] as { provider?: string })?.provider).toBe(
168
+ "inference-provider-native",
169
+ );
140
170
  });
141
171
 
142
- test("IS_PLATFORM=false, no config file → managed service modes default to 'your-own'", () => {
172
+ test("IS_PLATFORM=false, no config file → service modes follow schema defaults (your-own except google/notion-oauth which are managed)", () => {
143
173
  process.env.IS_PLATFORM = "false";
144
174
 
145
175
  loadConfig();
@@ -148,12 +178,15 @@ describe("platform-managed config defaults", () => {
148
178
  const written = readConfig() as { services?: Record<string, unknown> };
149
179
  expect(written.services).toBeDefined();
150
180
  const services = written.services!;
151
- for (const svc of MANAGED_SERVICES) {
181
+ for (const svc of SCHEMA_YOUR_OWN_DEFAULT_SERVICES) {
152
182
  expect((services[svc] as { mode?: string })?.mode).toBe("your-own");
153
183
  }
184
+ for (const svc of SCHEMA_MANAGED_DEFAULT_SERVICES) {
185
+ expect((services[svc] as { mode?: string })?.mode).toBe("managed");
186
+ }
154
187
  });
155
188
 
156
- test("IS_PLATFORM unset, no config file → managed service modes default to 'your-own'", () => {
189
+ test("IS_PLATFORM unset, no config file → service modes follow schema defaults (your-own except google/notion-oauth which are managed)", () => {
157
190
  delete process.env.IS_PLATFORM;
158
191
 
159
192
  loadConfig();
@@ -162,9 +195,12 @@ describe("platform-managed config defaults", () => {
162
195
  const written = readConfig() as { services?: Record<string, unknown> };
163
196
  expect(written.services).toBeDefined();
164
197
  const services = written.services!;
165
- for (const svc of MANAGED_SERVICES) {
198
+ for (const svc of SCHEMA_YOUR_OWN_DEFAULT_SERVICES) {
166
199
  expect((services[svc] as { mode?: string })?.mode).toBe("your-own");
167
200
  }
201
+ for (const svc of SCHEMA_MANAGED_DEFAULT_SERVICES) {
202
+ expect((services[svc] as { mode?: string })?.mode).toBe("managed");
203
+ }
168
204
  });
169
205
 
170
206
  test("IS_PLATFORM=true, config file already exists → existing service mode values are preserved", () => {
@@ -197,6 +233,37 @@ describe("platform-managed config defaults", () => {
197
233
  expect(config.services["image-generation"].mode).toBe("your-own");
198
234
  });
199
235
 
236
+ test("IS_PLATFORM=true, config file with explicit notion-oauth mode='your-own' → preserved (schema default is 'managed')", () => {
237
+ process.env.IS_PLATFORM = "true";
238
+
239
+ // notion-oauth's schema default is "managed" (per JARVIS-966), but an
240
+ // explicit user choice of "your-own" must win — the fill-defaults pass
241
+ // must never override an explicit value, even one that contradicts the
242
+ // schema default. Mirror of the image-generation "your-own preserved"
243
+ // test above, but for a service whose schema default is "managed".
244
+ writeFileSync(
245
+ CONFIG_PATH,
246
+ JSON.stringify(
247
+ {
248
+ services: {
249
+ "notion-oauth": { mode: "your-own" },
250
+ },
251
+ },
252
+ null,
253
+ 2,
254
+ ) + "\n",
255
+ );
256
+
257
+ const config = loadConfig();
258
+
259
+ const written = readConfig() as { services?: Record<string, unknown> };
260
+ expect(written.services).toBeDefined();
261
+ expect((written.services!["notion-oauth"] as { mode?: string })?.mode).toBe(
262
+ "your-own",
263
+ );
264
+ expect(config.services["notion-oauth"].mode).toBe("your-own");
265
+ });
266
+
200
267
  test("IS_PLATFORM=true, config file exists without a services key → in-memory config has all managed modes", () => {
201
268
  // Regression guard for the platform-managed boot order: by the time
202
269
  // `loadConfig()` runs, lifecycle steps such as `seedInferenceProfiles`
@@ -228,12 +295,8 @@ describe("platform-managed config defaults", () => {
228
295
  // missing service-mode fields.
229
296
  for (const svc of MANAGED_SERVICES) {
230
297
  expect(
231
- (
232
- config.services as unknown as Record<
233
- string,
234
- { mode: string }
235
- >
236
- )[svc]!.mode,
298
+ (config.services as unknown as Record<string, { mode: string }>)[svc]!
299
+ .mode,
237
300
  ).toBe("managed");
238
301
  }
239
302
 
@@ -277,9 +340,11 @@ describe("platform-managed config defaults", () => {
277
340
  expect(imageGen.provider).toBe("openai");
278
341
  });
279
342
 
280
- test("IS_PLATFORM=false, config file exists without services key → in-memory config keeps schema your-own defaults", () => {
343
+ test("IS_PLATFORM=false, config file exists without services key → in-memory config keeps schema defaults (your-own except google/notion-oauth which are managed)", () => {
281
344
  // Sanity guard: deployment-context defaults are a no-op when IS_PLATFORM
282
- // is not enabled, regardless of whether config.json existed.
345
+ // is not enabled, regardless of whether config.json existed. The Zod
346
+ // schema defaults still apply, so google-oauth and notion-oauth resolve
347
+ // to "managed" while the remaining services resolve to "your-own".
283
348
  process.env.IS_PLATFORM = "false";
284
349
 
285
350
  writeFileSync(
@@ -300,16 +365,18 @@ describe("platform-managed config defaults", () => {
300
365
 
301
366
  const config = loadConfig();
302
367
 
303
- for (const svc of MANAGED_SERVICES) {
368
+ for (const svc of SCHEMA_YOUR_OWN_DEFAULT_SERVICES) {
304
369
  expect(
305
- (
306
- config.services as unknown as Record<
307
- string,
308
- { mode: string }
309
- >
310
- )[svc]!.mode,
370
+ (config.services as unknown as Record<string, { mode: string }>)[svc]!
371
+ .mode,
311
372
  ).toBe("your-own");
312
373
  }
374
+ for (const svc of SCHEMA_MANAGED_DEFAULT_SERVICES) {
375
+ expect(
376
+ (config.services as unknown as Record<string, { mode: string }>)[svc]!
377
+ .mode,
378
+ ).toBe("managed");
379
+ }
313
380
  });
314
381
  });
315
382
 
@@ -28,7 +28,7 @@ import {
28
28
  invalidateConfigCache,
29
29
  loadConfig,
30
30
  } from "../config/loader.js";
31
- import { _setStorePath } from "../security/encrypted-store.js";
31
+ import { setStorePathForTesting } from "./encrypted-store-test-helpers.js";
32
32
 
33
33
  // ---------------------------------------------------------------------------
34
34
  // Helpers
@@ -71,12 +71,12 @@ function listQuarantinedFiles(): string[] {
71
71
  describe("config-quarantine UPDATES.md bulletin", () => {
72
72
  beforeEach(() => {
73
73
  resetWorkspace();
74
- _setStorePath(join(WORKSPACE_DIR, "keys.enc"));
74
+ setStorePathForTesting(join(WORKSPACE_DIR, "keys.enc"));
75
75
  invalidateConfigCache();
76
76
  });
77
77
 
78
78
  afterEach(() => {
79
- _setStorePath(null);
79
+ setStorePathForTesting(null);
80
80
  invalidateConfigCache();
81
81
  });
82
82
 
@@ -68,7 +68,6 @@ mock.module("../config/assistant-feature-flags.js", () => ({
68
68
  if (key === "managed-gemini-embeddings-enabled") return featureFlagEnabled;
69
69
  return true;
70
70
  },
71
- _setOverridesForTesting: () => {},
72
71
  clearFeatureFlagOverridesCache: () => {},
73
72
  initFeatureFlagOverrides: async () => {},
74
73
  getAssistantFeatureFlagDefaults: () => ({}),
@@ -81,7 +80,7 @@ afterAll(() => {
81
80
  });
82
81
 
83
82
  import { invalidateConfigCache, loadConfig } from "../config/loader.js";
84
- import { _setStorePath } from "../security/encrypted-store.js";
83
+ import { setStorePathForTesting } from "./encrypted-store-test-helpers.js";
85
84
 
86
85
  // ---------------------------------------------------------------------------
87
86
  // Helpers
@@ -117,7 +116,7 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
117
116
  }
118
117
  }
119
118
  ensureTestDir();
120
- _setStorePath(join(WORKSPACE_DIR, "keys.enc"));
119
+ setStorePathForTesting(join(WORKSPACE_DIR, "keys.enc"));
121
120
  invalidateConfigCache();
122
121
 
123
122
  // Reset mock state
@@ -127,7 +126,7 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
127
126
  });
128
127
 
129
128
  afterEach(() => {
130
- _setStorePath(null);
129
+ setStorePathForTesting(null);
131
130
  invalidateConfigCache();
132
131
 
133
132
  // Restore IS_PLATFORM
@@ -64,9 +64,9 @@ import {
64
64
  VALID_TTS_PROVIDERS as VALID_TTS_SERVICE_PROVIDERS,
65
65
  } from "../config/schemas/tts.js";
66
66
  import type { AssistantConfig } from "../config/types.js";
67
- import { _setStorePath } from "../security/encrypted-store.js";
68
67
  import { listCatalogProviderIds } from "../tts/provider-catalog.js";
69
68
  import { resolveTtsConfig } from "../tts/tts-config-resolver.js";
69
+ import { setStorePathForTesting } from "./encrypted-store-test-helpers.js";
70
70
 
71
71
  // ---------------------------------------------------------------------------
72
72
  // Helpers
@@ -87,7 +87,7 @@ describe("AssistantConfigSchema", () => {
87
87
  // llm.default.{provider,model}, auth routing via provider_connections.
88
88
  expect(result.services.inference).toEqual({});
89
89
  expect(result.llm.default.provider).toBe("anthropic");
90
- expect(result.llm.default.model).toBe("claude-opus-4-7");
90
+ expect(result.llm.default.model).toBe("claude-opus-4-8");
91
91
  expect(result.services["image-generation"].provider).toBe("gemini");
92
92
  expect(result.services["image-generation"].model).toBe(
93
93
  "gemini-3.1-flash-image-preview",
@@ -179,7 +179,7 @@ describe("AssistantConfigSchema", () => {
179
179
  expect(result.llm).toBeDefined();
180
180
  expect(result.llm.default).toEqual({
181
181
  provider: "anthropic",
182
- model: "claude-opus-4-7",
182
+ model: "claude-opus-4-8",
183
183
  maxTokens: 64000,
184
184
  effort: "max",
185
185
  speed: "standard",
@@ -318,7 +318,7 @@ describe("AssistantConfigSchema", () => {
318
318
  (result.services.inference as Record<string, unknown>).model,
319
319
  ).toBeUndefined();
320
320
  expect(result.llm.default.provider).toBe("anthropic");
321
- expect(result.llm.default.model).toBe("claude-opus-4-7");
321
+ expect(result.llm.default.model).toBe("claude-opus-4-8");
322
322
  });
323
323
 
324
324
  test("partial llm config (empty `llm: {}`) doesn't trigger full config reset", () => {
@@ -333,7 +333,7 @@ describe("AssistantConfigSchema", () => {
333
333
  });
334
334
  expect(result.llm.default.maxTokens).toBe(32000);
335
335
  expect(result.llm.default.provider).toBe("anthropic");
336
- expect(result.llm.default.model).toBe("claude-opus-4-7");
336
+ expect(result.llm.default.model).toBe("claude-opus-4-8");
337
337
  });
338
338
 
339
339
  test("llm.default with one missing field still parses (defaults applied)", () => {
@@ -2068,12 +2068,12 @@ describe("loadConfig with schema validation", () => {
2068
2068
  }
2069
2069
  }
2070
2070
  ensureTestDir();
2071
- _setStorePath(join(WORKSPACE_DIR, "keys.enc"));
2071
+ setStorePathForTesting(join(WORKSPACE_DIR, "keys.enc"));
2072
2072
  invalidateConfigCache();
2073
2073
  });
2074
2074
 
2075
2075
  afterEach(() => {
2076
- _setStorePath(null);
2076
+ setStorePathForTesting(null);
2077
2077
  invalidateConfigCache();
2078
2078
  });
2079
2079
 
@@ -2096,7 +2096,7 @@ describe("loadConfig with schema validation", () => {
2096
2096
  writeConfig({});
2097
2097
  const config = loadConfig();
2098
2098
  expect(config.llm.default.provider).toBe("anthropic");
2099
- expect(config.llm.default.model).toBe("claude-opus-4-7");
2099
+ expect(config.llm.default.model).toBe("claude-opus-4-8");
2100
2100
  expect(config.llm.default.maxTokens).toBe(64000);
2101
2101
  expect(config.llm.default.thinking).toEqual({
2102
2102
  enabled: true,
@@ -2350,12 +2350,12 @@ describe("Call entrypoint gating", () => {
2350
2350
  }
2351
2351
  }
2352
2352
  ensureTestDir();
2353
- _setStorePath(join(WORKSPACE_DIR, "keys.enc"));
2353
+ setStorePathForTesting(join(WORKSPACE_DIR, "keys.enc"));
2354
2354
  invalidateConfigCache();
2355
2355
  });
2356
2356
 
2357
2357
  afterEach(() => {
2358
- _setStorePath(null);
2358
+ setStorePathForTesting(null);
2359
2359
  invalidateConfigCache();
2360
2360
  });
2361
2361
 
@@ -0,0 +1,83 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ describeSubscriptionModelIncompatibility,
5
+ isConnectionCompatibleWithModel,
6
+ } from "../providers/connection-model-compat.js";
7
+
8
+ describe("isConnectionCompatibleWithModel", () => {
9
+ test("non-oauth_subscription connections are always compatible", () => {
10
+ expect(
11
+ isConnectionCompatibleWithModel(
12
+ { auth: { type: "api_key" } as never },
13
+ "o3",
14
+ ),
15
+ ).toBe(true);
16
+ });
17
+
18
+ test("oauth_subscription is compatible with Codex models", () => {
19
+ expect(
20
+ isConnectionCompatibleWithModel(
21
+ { auth: { type: "oauth_subscription" } as never },
22
+ "gpt-5.4",
23
+ ),
24
+ ).toBe(true);
25
+ });
26
+
27
+ test("oauth_subscription is incompatible with non-Codex models", () => {
28
+ expect(
29
+ isConnectionCompatibleWithModel(
30
+ { auth: { type: "oauth_subscription" } as never },
31
+ "o3",
32
+ ),
33
+ ).toBe(false);
34
+ });
35
+
36
+ test("undefined model is always compatible", () => {
37
+ expect(
38
+ isConnectionCompatibleWithModel(
39
+ { auth: { type: "oauth_subscription" } as never },
40
+ undefined,
41
+ ),
42
+ ).toBe(true);
43
+ });
44
+ });
45
+
46
+ describe("describeSubscriptionModelIncompatibility", () => {
47
+ const subscriptionConn = { auth: { type: "oauth_subscription" as const } as never };
48
+ const apiKeyConn = { auth: { type: "api_key" as const } as never };
49
+
50
+ test("returns message when all candidates are oauth_subscription and model is incompatible", () => {
51
+ const msg = describeSubscriptionModelIncompatibility(
52
+ [subscriptionConn],
53
+ "o3",
54
+ );
55
+ expect(msg).toContain("o3");
56
+ expect(msg).toContain("ChatGPT subscription");
57
+ });
58
+
59
+ test("returns null when no candidates", () => {
60
+ expect(describeSubscriptionModelIncompatibility([], "o3")).toBeNull();
61
+ });
62
+
63
+ test("returns null when model is undefined", () => {
64
+ expect(
65
+ describeSubscriptionModelIncompatibility([subscriptionConn], undefined),
66
+ ).toBeNull();
67
+ });
68
+
69
+ test("returns null when some candidates are compatible", () => {
70
+ expect(
71
+ describeSubscriptionModelIncompatibility(
72
+ [subscriptionConn, apiKeyConn],
73
+ "o3",
74
+ ),
75
+ ).toBeNull();
76
+ });
77
+
78
+ test("returns null when model is Codex-compatible", () => {
79
+ expect(
80
+ describeSubscriptionModelIncompatibility([subscriptionConn], "gpt-5.4"),
81
+ ).toBeNull();
82
+ });
83
+ });
@@ -65,11 +65,12 @@ import { executeContactMerge } from "../config/bundled-skills/contacts/tools/con
65
65
  import { executeContactSearch } from "../config/bundled-skills/contacts/tools/contact-search.js";
66
66
  import { upsertContact } from "../contacts/contact-store.js";
67
67
  import type { ContactWithChannels } from "../contacts/types.js";
68
- import { getDb, resetDb } from "../memory/db-connection.js";
68
+ import { getDb } from "../memory/db-connection.js";
69
69
  import { initializeDb } from "../memory/db-init.js";
70
70
  import { ROUTES } from "../runtime/routes/contact-routes.js";
71
71
  import { RouteError } from "../runtime/routes/errors.js";
72
72
  import type { ToolContext } from "../tools/types.js";
73
+ import { resetDbForTesting } from "./db-test-helpers.js";
73
74
 
74
75
  initializeDb();
75
76
 
@@ -127,7 +128,7 @@ beforeAll(() => {
127
128
 
128
129
  afterAll(() => {
129
130
  testServer?.stop(true);
130
- resetDb();
131
+ resetDbForTesting();
131
132
  });
132
133
 
133
134
  function getRawDb(): Database {
@@ -367,6 +367,28 @@ describe("token estimator", () => {
367
367
  expect(tokens).toBeLessThan(4_200);
368
368
  });
369
369
 
370
+ test("Gemini preserves aspect ratio when resizing to the 3072px cap", () => {
371
+ // Gemini rescales BOTH dimensions by a single factor so the longest side
372
+ // becomes 3072px. A 10000x1000 image → 3072x307, i.e. ceil(3072/768)=4
373
+ // tiles wide * ceil(307/768)=1 tile high = 4 tiles (~1,032 tokens).
374
+ // Clamping each side independently would yield 4*2=8 tiles, over-counting
375
+ // by 2x and risking spurious compaction.
376
+ const tokens = estimateContentBlockTokens(
377
+ {
378
+ type: "image",
379
+ source: {
380
+ type: "base64",
381
+ media_type: "image/png",
382
+ data: makePngBase64(10000, 1000),
383
+ },
384
+ },
385
+ { providerName: "gemini" },
386
+ );
387
+ // 4 tiles * 258 = 1,032 image tokens + 16 block overhead + 3 media type.
388
+ expect(tokens).toBeGreaterThanOrEqual(1_032);
389
+ expect(tokens).toBeLessThan(1_100);
390
+ });
391
+
370
392
  test("Gemini images ≤384px on both sides count as a single 258-token tile", () => {
371
393
  const tokens = estimateContentBlockTokens(
372
394
  {
@@ -138,6 +138,8 @@ mock.module("../memory/conversation-crud.js", () => ({
138
138
  updateConversationTitle: () => {},
139
139
  getMessageById: () => null,
140
140
  getLastUserTimestampBefore: () => 0,
141
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
142
+ updateMessageContent: mock(() => {}),
141
143
  }));
142
144
 
143
145
  mock.module("../memory/conversation-queries.js", () => ({
@@ -192,6 +194,9 @@ mock.module("../agent/loop.js", () => ({
192
194
  onEvent: (event: AgentEvent) => void,
193
195
  _signal?: AbortSignal,
194
196
  ): Promise<Message[]> {
197
+ // Prime the assistant row anchor — production code emits this from
198
+ // `AgentLoop.run` just before `provider.sendMessage`.
199
+ await onEvent({ type: "llm_call_started" });
195
200
  const history = [...messages];
196
201
 
197
202
  // Simulate provider response with 2 tool_use blocks
@@ -100,6 +100,7 @@ mock.module("../memory/conversation-crud.js", () => ({
100
100
  setConversationHistoryStrippedAt: () => {},
101
101
  updateConversationContextWindow: () => {},
102
102
  updateConversationSlackContextWatermark: () => {},
103
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
103
104
  }));
104
105
 
105
106
  import { runAgentLoopImpl } from "../daemon/conversation-agent-loop.js";
@@ -0,0 +1,55 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import type { AgentEvent } from "../agent/loop.js";
4
+ import {
5
+ createEventHandlerState,
6
+ type EventHandlerDeps,
7
+ handleMaxTokensReached,
8
+ } from "../daemon/conversation-agent-loop-handlers.js";
9
+ import type { ServerMessage } from "../daemon/message-protocol.js";
10
+
11
+ describe("max tokens reached handler", () => {
12
+ test("emits and stores an inline continuation card", () => {
13
+ const sent: ServerMessage[] = [];
14
+ const ctx = {
15
+ conversationId: "conv-1",
16
+ surfaceState: new Map(),
17
+ currentTurnSurfaces: [],
18
+ };
19
+ const deps = {
20
+ ctx,
21
+ onEvent: (msg: ServerMessage) => sent.push(msg),
22
+ reqId: "req-1",
23
+ isFirstMessage: false,
24
+ shouldGenerateTitle: false,
25
+ rlog: { warn: () => {} },
26
+ turnChannelContext: {
27
+ userMessageChannel: "vellum",
28
+ assistantMessageChannel: "vellum",
29
+ },
30
+ turnInterfaceContext: {
31
+ userMessageInterface: "web",
32
+ assistantMessageInterface: "web",
33
+ },
34
+ } as unknown as EventHandlerDeps;
35
+
36
+ handleMaxTokensReached(createEventHandlerState(), deps, {
37
+ type: "max_tokens_reached",
38
+ stopReason: "max_tokens",
39
+ } as Extract<AgentEvent, { type: "max_tokens_reached" }>);
40
+
41
+ const show = sent.find((msg) => msg.type === "ui_surface_show");
42
+ expect(show).toBeDefined();
43
+ if (!show || show.type !== "ui_surface_show") return;
44
+
45
+ expect(show.surfaceType).toBe("card");
46
+ expect((show.data as { title?: unknown }).title).toBe(
47
+ "Response limit reached",
48
+ );
49
+ expect(show.actions?.[0]?.id).toBe("relay_prompt");
50
+ expect(show.actions?.[0]?.data?.prompt).toContain("Continue");
51
+ expect(show.actions?.[0]?.data?._completeSurface).toBe(true);
52
+ expect(ctx.currentTurnSurfaces).toHaveLength(1);
53
+ expect(ctx.surfaceState.has(show.surfaceId)).toBe(true);
54
+ });
55
+ });
@@ -170,6 +170,7 @@ mock.module("../memory/conversation-crud.js", () => ({
170
170
  getMessageById: () => null,
171
171
  getLastUserTimestampBefore: () => 0,
172
172
  setLastNotifiedInferenceProfile: () => {},
173
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
173
174
  }));
174
175
 
175
176
  mock.module("../memory/conversation-disk-view.js", () => ({