@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
@@ -9,6 +9,7 @@ import {
9
9
  getSkillRefCount,
10
10
  getSkillToolNames,
11
11
  getTool,
12
+ getToolOwner,
12
13
  initializeTools,
13
14
  registerSkillTools,
14
15
  registerTool,
@@ -40,12 +41,11 @@ function makeFakeTool(name: string): Tool {
40
41
  };
41
42
  }
42
43
 
43
- function makeSkillTool(name: string, ownerSkillId: string): Tool {
44
- return {
45
- ...makeFakeTool(name),
46
- origin: "skill" as const,
47
- ownerSkillId,
48
- };
44
+ function makeSkillTool(name: string): Tool {
45
+ // Ownership is recorded by the registry (via `registerSkillTools`), not by
46
+ // any field on the Tool itself. This helper is now a thin alias for
47
+ // makeFakeTool; the wrapper is kept so call sites stay readable.
48
+ return makeFakeTool(name);
49
49
  }
50
50
 
51
51
  describe("tool registry host tools", () => {
@@ -195,11 +195,11 @@ describe("baseline characterization: core app tool surface", () => {
195
195
 
196
196
  const tool = getTool("app_open");
197
197
  expect(tool).toBeDefined();
198
- expect(tool?.executionMode).toBe("proxy");
199
198
 
200
- // Proxy tools are excluded from getAllToolDefinitions() by design
199
+ // app_open is core-owned (no skill owner) so it flows through
200
+ // `getAllToolDefinitions()` like any other core tool.
201
201
  const definitionNames = getAllToolDefinitions().map((def) => def.name);
202
- expect(definitionNames).not.toContain("app_open");
202
+ expect(definitionNames).toContain("app_open");
203
203
  });
204
204
 
205
205
  test("bundled app-builder skill has TOOLS.json manifest", async () => {
@@ -217,33 +217,27 @@ describe("baseline characterization: core app tool surface", () => {
217
217
  });
218
218
  });
219
219
 
220
- describe("tool origin metadata", () => {
220
+ describe("tool ownership metadata", () => {
221
221
  beforeEach(() => {
222
222
  __resetRegistryForTesting();
223
223
  });
224
224
 
225
- test("registers a skill-origin tool and preserves metadata via getTool()", () => {
226
- const skillTool: Tool = {
227
- ...makeFakeTool("test-skill-origin-tool"),
228
- origin: "skill",
229
- ownerSkillId: "test-skill",
230
- };
231
-
232
- registerTool(skillTool);
225
+ test("registerTool does not record ownership (bare-install path)", () => {
226
+ registerTool(makeFakeTool("test-bare-tool"));
233
227
 
234
- const retrieved = getTool("test-skill-origin-tool");
235
- expect(retrieved).toBeDefined();
236
- expect(retrieved?.origin).toBe("skill");
237
- expect(retrieved?.ownerSkillId).toBe("test-skill");
228
+ expect(getTool("test-bare-tool")).toBeDefined();
229
+ // `registerTool` is the bare-install path used by tests + core
230
+ // bootstraps; it does not record ownership. Tools that need an owner
231
+ // must go through `registerSkillTools(skillId, ...)` or its sibling
232
+ // entry points so the registry populates `ownersByName`.
233
+ expect(getToolOwner("test-bare-tool")).toBeUndefined();
238
234
  });
239
235
 
240
- test("core tools default to no origin metadata (undefined)", async () => {
236
+ test("core tools have no owner", async () => {
241
237
  await initializeTools();
242
238
 
243
- const coreTool = getTool("host_file_read");
244
- expect(coreTool).toBeDefined();
245
- expect(coreTool?.origin).toBeUndefined();
246
- expect(coreTool?.ownerSkillId).toBeUndefined();
239
+ expect(getTool("host_file_read")).toBeDefined();
240
+ expect(getToolOwner("host_file_read")).toBeUndefined();
247
241
  });
248
242
  });
249
243
 
@@ -252,66 +246,67 @@ describe("dynamic skill tool registry", () => {
252
246
  __resetRegistryForTesting();
253
247
  });
254
248
 
255
- test("registers skill tools and retrieves them", () => {
256
- const tools = [
257
- makeSkillTool("sk_tool_a", "my-skill"),
258
- makeSkillTool("sk_tool_b", "my-skill"),
259
- ];
260
- registerSkillTools(tools);
249
+ test("registers skill tools and retrieves them with owner via getToolOwner", () => {
250
+ registerSkillTools("my-skill", [
251
+ makeSkillTool("sk_tool_a"),
252
+ makeSkillTool("sk_tool_b"),
253
+ ]);
261
254
 
262
255
  expect(getTool("sk_tool_a")).toBeDefined();
263
- expect(getTool("sk_tool_a")?.origin).toBe("skill");
264
- expect(getTool("sk_tool_a")?.ownerSkillId).toBe("my-skill");
256
+ expect(getToolOwner("sk_tool_a")).toEqual({
257
+ kind: "skill",
258
+ id: "my-skill",
259
+ });
265
260
 
266
261
  expect(getTool("sk_tool_b")).toBeDefined();
267
- expect(getTool("sk_tool_b")?.origin).toBe("skill");
262
+ expect(getToolOwner("sk_tool_b")).toEqual({
263
+ kind: "skill",
264
+ id: "my-skill",
265
+ });
268
266
  });
269
267
 
270
268
  test("skips skill tool that collides with a core tool without throwing", async () => {
271
269
  await initializeTools();
272
270
 
273
271
  // host_file_read is a core tool registered during init
274
- const colliding = makeSkillTool("host_file_read", "rogue-skill");
275
- const accepted = registerSkillTools([colliding]);
272
+ const accepted = registerSkillTools("rogue-skill", [
273
+ makeSkillTool("host_file_read"),
274
+ ]);
276
275
 
277
276
  // The colliding tool should be silently skipped
278
277
  expect(accepted).toHaveLength(0);
279
278
  // The core tool should still be in place (not overwritten)
280
- const retrieved = getTool("host_file_read");
281
- expect(retrieved?.origin).toBeUndefined(); // core tools have no origin
279
+ expect(getTool("host_file_read")).toBeDefined();
280
+ expect(getToolOwner("host_file_read")).toBeUndefined();
282
281
  });
283
282
 
284
283
  test("allows replacement within the same owning skill", () => {
285
- const original = makeSkillTool("sk_replaceable", "owner-skill");
286
- registerSkillTools([original]);
284
+ registerSkillTools("owner-skill", [makeSkillTool("sk_replaceable")]);
287
285
 
288
286
  const replacement: Tool = {
289
- ...makeSkillTool("sk_replaceable", "owner-skill"),
287
+ ...makeSkillTool("sk_replaceable"),
290
288
  description: "Updated description",
291
289
  };
292
290
  // Should not throw
293
- registerSkillTools([replacement]);
291
+ registerSkillTools("owner-skill", [replacement]);
294
292
 
295
293
  const retrieved = getTool("sk_replaceable");
296
294
  expect(retrieved?.description).toBe("Updated description");
297
295
  });
298
296
 
299
297
  test("rejects replacement from a different owning skill", () => {
300
- const original = makeSkillTool("sk_owned", "skill-alpha");
301
- registerSkillTools([original]);
298
+ registerSkillTools("skill-alpha", [makeSkillTool("sk_owned")]);
302
299
 
303
- const intruder = makeSkillTool("sk_owned", "skill-beta");
304
- expect(() => registerSkillTools([intruder])).toThrow(
305
- 'already registered by skill "skill-alpha"',
306
- );
300
+ expect(() =>
301
+ registerSkillTools("skill-beta", [makeSkillTool("sk_owned")]),
302
+ ).toThrow('already registered by skill "skill-alpha"');
307
303
  });
308
304
 
309
305
  test("unregisterSkillTools removes all tools for a skill", () => {
310
- const tools = [
311
- makeSkillTool("sk_rm_1", "removable-skill"),
312
- makeSkillTool("sk_rm_2", "removable-skill"),
313
- ];
314
- registerSkillTools(tools);
306
+ registerSkillTools("removable-skill", [
307
+ makeSkillTool("sk_rm_1"),
308
+ makeSkillTool("sk_rm_2"),
309
+ ]);
315
310
  expect(getTool("sk_rm_1")).toBeDefined();
316
311
  expect(getTool("sk_rm_2")).toBeDefined();
317
312
 
@@ -319,24 +314,32 @@ describe("dynamic skill tool registry", () => {
319
314
 
320
315
  expect(getTool("sk_rm_1")).toBeUndefined();
321
316
  expect(getTool("sk_rm_2")).toBeUndefined();
317
+ // Ownership map is cleared in lockstep with the tools map.
318
+ expect(getToolOwner("sk_rm_1")).toBeUndefined();
319
+ expect(getToolOwner("sk_rm_2")).toBeUndefined();
322
320
  });
323
321
 
324
322
  test("unregisterSkillTools does not affect tools from other skills", () => {
325
- registerSkillTools([makeSkillTool("sk_keep", "keep-skill")]);
326
- registerSkillTools([makeSkillTool("sk_remove", "nuke-skill")]);
323
+ registerSkillTools("keep-skill", [makeSkillTool("sk_keep")]);
324
+ registerSkillTools("nuke-skill", [makeSkillTool("sk_remove")]);
327
325
 
328
326
  unregisterSkillTools("nuke-skill");
329
327
 
330
328
  expect(getTool("sk_keep")).toBeDefined();
331
329
  expect(getTool("sk_remove")).toBeUndefined();
330
+ expect(getToolOwner("sk_keep")).toEqual({
331
+ kind: "skill",
332
+ id: "keep-skill",
333
+ });
334
+ expect(getToolOwner("sk_remove")).toBeUndefined();
332
335
  });
333
336
 
334
337
  test("getSkillToolNames returns only skill tool names", async () => {
335
338
  await initializeTools();
336
339
 
337
- registerSkillTools([
338
- makeSkillTool("sk_names_a", "names-skill"),
339
- makeSkillTool("sk_names_b", "names-skill"),
340
+ registerSkillTools("names-skill", [
341
+ makeSkillTool("sk_names_a"),
342
+ makeSkillTool("sk_names_b"),
340
343
  ]);
341
344
 
342
345
  const skillNames = getSkillToolNames();
@@ -350,19 +353,22 @@ describe("dynamic skill tool registry", () => {
350
353
  test("registerSkillTools skips core-colliding tools but registers the rest", async () => {
351
354
  await initializeTools();
352
355
 
353
- const tools = [
354
- makeSkillTool("sk_atomic_ok", "atomic-skill"),
355
- makeSkillTool("host_file_read", "atomic-skill"), // collides with core
356
- ];
357
-
358
- const accepted = registerSkillTools(tools);
356
+ const accepted = registerSkillTools("atomic-skill", [
357
+ makeSkillTool("sk_atomic_ok"),
358
+ makeSkillTool("host_file_read"), // collides with core
359
+ ]);
359
360
  // Only the non-colliding tool should be accepted
360
361
  expect(accepted).toHaveLength(1);
361
362
  expect(accepted[0].name).toBe("sk_atomic_ok");
362
- // The non-colliding tool should be registered
363
+ // The non-colliding tool should be registered with the correct owner
363
364
  expect(getTool("sk_atomic_ok")).toBeDefined();
364
- // The core tool should be untouched
365
- expect(getTool("host_file_read")?.origin).toBeUndefined();
365
+ expect(getToolOwner("sk_atomic_ok")).toEqual({
366
+ kind: "skill",
367
+ id: "atomic-skill",
368
+ });
369
+ // The core tool should be untouched (no owner recorded)
370
+ expect(getTool("host_file_read")).toBeDefined();
371
+ expect(getToolOwner("host_file_read")).toBeUndefined();
366
372
  });
367
373
  });
368
374
 
@@ -372,17 +378,17 @@ describe("skill tool reference counting", () => {
372
378
  });
373
379
 
374
380
  test("ref count increments on each registerSkillTools call", () => {
375
- registerSkillTools([makeSkillTool("rc_a", "rc-skill")]);
381
+ registerSkillTools("rc-skill", [makeSkillTool("rc_a")]);
376
382
  expect(getSkillRefCount("rc-skill")).toBe(1);
377
383
 
378
- // Second session registers the same skill (same ownerSkillId allows replacement)
379
- registerSkillTools([makeSkillTool("rc_a", "rc-skill")]);
384
+ // Second session registers the same skill (same owner id allows replacement)
385
+ registerSkillTools("rc-skill", [makeSkillTool("rc_a")]);
380
386
  expect(getSkillRefCount("rc-skill")).toBe(2);
381
387
  });
382
388
 
383
389
  test("unregister decrements ref count but keeps tools when count > 0", () => {
384
- registerSkillTools([makeSkillTool("rc_keep", "rc-multi")]);
385
- registerSkillTools([makeSkillTool("rc_keep", "rc-multi")]);
390
+ registerSkillTools("rc-multi", [makeSkillTool("rc_keep")]);
391
+ registerSkillTools("rc-multi", [makeSkillTool("rc_keep")]);
386
392
  expect(getSkillRefCount("rc-multi")).toBe(2);
387
393
 
388
394
  unregisterSkillTools("rc-multi");
@@ -392,8 +398,8 @@ describe("skill tool reference counting", () => {
392
398
  });
393
399
 
394
400
  test("tools are removed only when last reference is unregistered", () => {
395
- registerSkillTools([makeSkillTool("rc_last", "rc-final")]);
396
- registerSkillTools([makeSkillTool("rc_last", "rc-final")]);
401
+ registerSkillTools("rc-final", [makeSkillTool("rc_last")]);
402
+ registerSkillTools("rc-final", [makeSkillTool("rc_last")]);
397
403
 
398
404
  unregisterSkillTools("rc-final");
399
405
  expect(getTool("rc_last")).toBeDefined();
@@ -225,7 +225,7 @@ import {
225
225
  createVerificationSession,
226
226
  } from "../memory/channel-verification-sessions.js";
227
227
  import { addMessage, getMessages } from "../memory/conversation-crud.js";
228
- import { getDb, resetDb } from "../memory/db-connection.js";
228
+ import { getDb } from "../memory/db-connection.js";
229
229
  import { initializeDb } from "../memory/db-init.js";
230
230
  import { createInvite } from "../memory/invite-store.js";
231
231
  import { resetTestTables } from "../memory/raw-query.js";
@@ -235,12 +235,13 @@ import {
235
235
  getGuardianBinding,
236
236
  } from "../runtime/channel-verification-service.js";
237
237
  import { generateVoiceCode, hashVoiceCode } from "../util/voice-code.js";
238
+ import { resetDbForTesting } from "./db-test-helpers.js";
238
239
  import { createGuardianBinding } from "./helpers/create-guardian-binding.js";
239
240
 
240
241
  initializeDb();
241
242
 
242
243
  afterAll(() => {
243
- resetDb();
244
+ resetDbForTesting();
244
245
  });
245
246
 
246
247
  // ── Mock WebSocket factory ──────────────────────────────────────────
@@ -423,16 +424,15 @@ describe("relay-server", () => {
423
424
  includeDefaultFallback: false,
424
425
  },
425
426
  isProcessing: () => false,
426
- persistUserMessage: async (
427
- content: string,
428
- _attachments: unknown[],
429
- requestId?: string,
430
- ) => {
431
- session.currentRequestId = requestId;
427
+ persistUserMessage: async (options: {
428
+ content: string;
429
+ requestId?: string;
430
+ }) => {
431
+ session.currentRequestId = options.requestId;
432
432
  const message = await addMessage(
433
433
  conversationId,
434
434
  "user",
435
- JSON.stringify([{ type: "text", text: content }]),
435
+ JSON.stringify([{ type: "text", text: options.content }]),
436
436
  {
437
437
  userMessageChannel: "phone",
438
438
  assistantMessageChannel: "phone",
@@ -440,7 +440,7 @@ describe("relay-server", () => {
440
440
  assistantMessageInterface: "phone",
441
441
  },
442
442
  );
443
- return message.id;
443
+ return { id: message.id, deduplicated: false };
444
444
  },
445
445
  setChannelCapabilities: () => {},
446
446
  setAssistantId: () => {},
@@ -51,17 +51,18 @@ import {
51
51
  } from "../memory/attachments-store.js";
52
52
  import * as conversationStore from "../memory/conversation-crud.js";
53
53
  import { getOrCreateConversation } from "../memory/conversation-key-store.js";
54
- import { getDb, resetDb } from "../memory/db-connection.js";
54
+ import { getDb } from "../memory/db-connection.js";
55
55
  import { initializeDb } from "../memory/db-init.js";
56
56
  import * as deliveryChannels from "../memory/delivery-channels.js";
57
57
  import { resetTestTables } from "../memory/raw-query.js";
58
58
  import { RuntimeHttpServer } from "../runtime/http-server.js";
59
59
  import * as pendingInteractions from "../runtime/pending-interactions.js";
60
+ import { resetDbForTesting } from "./db-test-helpers.js";
60
61
 
61
62
  initializeDb();
62
63
 
63
64
  afterAll(() => {
64
- resetDb();
65
+ resetDbForTesting();
65
66
  });
66
67
 
67
68
  const TEST_TOKEN = "test-bearer-token-attach";
@@ -9,6 +9,17 @@ mock.module("../util/logger.js", () => ({
9
9
  truncateForLog: (value: string) => value,
10
10
  }));
11
11
 
12
+ // Stub the background-wake publisher so these store-level unit tests stay
13
+ // hermetic. `notifySchedulesChanged()` fires a debounced background-wake
14
+ // refresh on every mutation; left real, that 250ms timer performs a
15
+ // synchronous DB read plus a platform-client lookup which compete with the
16
+ // serialized async event delivery and can push a `sync_changed` past the
17
+ // wait deadline on loaded runners. Background-wake behavior is covered by the
18
+ // background-wake tests.
19
+ mock.module("../background-wake/publisher.js", () => ({
20
+ refreshBackgroundWakeIntent: () => {},
21
+ }));
22
+
12
23
  import { SYNC_TAGS } from "../daemon/message-types/sync.js";
13
24
  import { getDb } from "../memory/db-connection.js";
14
25
  import { initializeDb } from "../memory/db-init.js";
@@ -38,7 +49,11 @@ function getRawDb(): import("bun:sqlite").Database {
38
49
  }
39
50
 
40
51
  async function waitFor(predicate: () => boolean): Promise<void> {
41
- const deadline = Date.now() + 500;
52
+ // Event delivery is async (serialized through the hub's broadcast chain),
53
+ // so poll until it lands. The deadline is generous to absorb scheduling
54
+ // jitter on loaded CI runners — the happy path returns the moment the
55
+ // predicate holds, so a larger budget costs nothing when events flow.
56
+ const deadline = Date.now() + 2000;
42
57
  while (Date.now() < deadline) {
43
58
  if (predicate()) return;
44
59
  await new Promise((resolve) => setTimeout(resolve, 5));
@@ -210,12 +210,57 @@ describe("scheduler conversation reuse", () => {
210
210
  expect(runs2[0].conversationId).toBe(firstConversationId);
211
211
  });
212
212
 
213
+ test("recurring schedule defaults to reuseConversation=true", async () => {
214
+ /**
215
+ * When no explicit reuseConversation is provided, recurring schedules
216
+ * default to true — subsequent runs reuse the same conversation.
217
+ */
218
+
219
+ // GIVEN a recurring schedule with no explicit reuseConversation
220
+ const rruleExpr = buildEveryMinuteRrule();
221
+ const schedule = createSchedule({
222
+ name: "Default Reuse Test",
223
+ cronExpression: rruleExpr,
224
+ message: "Default reuse message",
225
+ syntax: "rrule",
226
+ expression: rruleExpr,
227
+ // no explicit reuseConversation — should default to true for recurring
228
+ });
229
+
230
+ // WHEN the schedule fires for the first time
231
+ forceScheduleDue(schedule.id);
232
+
233
+ const processMessage = async (conversationId: string, message: string) => {
234
+ processedMessages.push({ conversationId, message });
235
+ };
236
+
237
+ const scheduler1 = startScheduler(processMessage, () => {});
238
+ await new Promise((resolve) => setTimeout(resolve, 500));
239
+ scheduler1.stop();
240
+
241
+ expect(processedMessages).toHaveLength(1);
242
+ const firstConversationId = processedMessages[0].conversationId;
243
+ expect(firstConversationId).toBeTruthy();
244
+
245
+ // WHEN the schedule fires for the second time
246
+ forceScheduleDue(schedule.id);
247
+ processedMessages.length = 0;
248
+
249
+ const scheduler2 = startScheduler(processMessage, () => {});
250
+ await new Promise((resolve) => setTimeout(resolve, 500));
251
+ scheduler2.stop();
252
+
253
+ // THEN the same conversation is reused
254
+ expect(processedMessages).toHaveLength(1);
255
+ expect(processedMessages[0].conversationId).toBe(firstConversationId);
256
+ });
257
+
213
258
  test("recurring schedule with reuseConversation=false creates new conversation each run", async () => {
214
259
  /**
215
- * Default behavior: each run creates a brand-new conversation.
260
+ * When explicitly opted out, each run creates a brand-new conversation.
216
261
  */
217
262
 
218
- // GIVEN a recurring schedule with reuseConversation disabled (default)
263
+ // GIVEN a recurring schedule with reuseConversation explicitly disabled
219
264
  const rruleExpr = buildEveryMinuteRrule();
220
265
  const schedule = createSchedule({
221
266
  name: "No Reuse Test",
@@ -223,7 +268,7 @@ describe("scheduler conversation reuse", () => {
223
268
  message: "New conv each run",
224
269
  syntax: "rrule",
225
270
  expression: rruleExpr,
226
- // reuseConversation defaults to false
271
+ reuseConversation: false, // explicitly opt out of conversation reuse
227
272
  });
228
273
 
229
274
  // WHEN the schedule fires for the first time
@@ -106,6 +106,7 @@ mock.module("../memory/conversation-crud.js", () => ({
106
106
  provenanceFromTrustContext: () => undefined,
107
107
  setConversationOriginChannelIfUnset: () => {},
108
108
  setConversationOriginInterfaceIfUnset: () => {},
109
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
109
110
  }));
110
111
 
111
112
  mock.module("../runtime/local-actor-identity.js", () => ({
@@ -187,7 +188,10 @@ function makeRequest(
187
188
  });
188
189
  }
189
190
 
190
- const persistUserMessageMock = mock(async () => "persisted-id");
191
+ const persistUserMessageMock = mock(async () => ({
192
+ id: "persisted-id",
193
+ deduplicated: false,
194
+ }));
191
195
  const runAgentLoopMock = mock(async () => undefined);
192
196
 
193
197
  function makeSendMessageDeps() {
@@ -25,7 +25,6 @@ mock.module("../util/logger.js", () => ({
25
25
 
26
26
  import type { CesClient } from "../credential-execution/client.js";
27
27
  import * as encryptedStore from "../security/encrypted-store.js";
28
- import { _setStorePath } from "../security/encrypted-store.js";
29
28
  import {
30
29
  _resetBackend,
31
30
  deleteSecureKeyAsync,
@@ -36,6 +35,7 @@ import {
36
35
  setCesReconnect,
37
36
  setSecureKeyAsync,
38
37
  } from "../security/secure-keys.js";
38
+ import { setStorePathForTesting } from "./encrypted-store-test-helpers.js";
39
39
 
40
40
  // ---------------------------------------------------------------------------
41
41
  // Use a temp directory for encrypted store tests
@@ -59,11 +59,11 @@ describe("secure-keys", () => {
59
59
  rmSync(TEST_DIR, { recursive: true });
60
60
  }
61
61
  mkdirSync(TEST_DIR, { recursive: true });
62
- _setStorePath(STORE_PATH);
62
+ setStorePathForTesting(STORE_PATH);
63
63
  });
64
64
 
65
65
  afterEach(() => {
66
- _setStorePath(null);
66
+ setStorePathForTesting(null);
67
67
  _resetBackend();
68
68
  delete process.env.VELLUM_DEV;
69
69
  delete process.env.VELLUM_DESKTOP_APP;