@vellumai/assistant 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (739) hide show
  1. package/ARCHITECTURE.md +48 -50
  2. package/Dockerfile +1 -0
  3. package/README.md +1 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
  5. package/bun.lock +26 -26
  6. package/docs/architecture/memory.md +5 -2
  7. package/docs/architecture/security.md +20 -0
  8. package/docs/plugins.md +7 -9
  9. package/knip.json +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  11. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +52 -5
  12. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  13. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  15. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  16. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  17. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  18. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  19. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  20. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  21. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  22. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  23. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  24. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  25. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  26. package/openapi.yaml +1020 -40
  27. package/package.json +6 -3
  28. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  29. package/src/__tests__/app-bundler.test.ts +170 -1
  30. package/src/__tests__/app-control-flow.test.ts +384 -0
  31. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  32. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  33. package/src/__tests__/app-executors.test.ts +30 -43
  34. package/src/__tests__/approval-routes-http.test.ts +23 -6
  35. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  36. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  37. package/src/__tests__/assistant-event-hub.test.ts +157 -2
  38. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -7
  39. package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
  40. package/src/__tests__/background-shell-host-bash.test.ts +14 -15
  41. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  42. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  43. package/src/__tests__/btw-routes.test.ts +13 -4
  44. package/src/__tests__/call-controller.test.ts +49 -1
  45. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  46. package/src/__tests__/call-domain.test.ts +0 -2
  47. package/src/__tests__/call-routes-http.test.ts +0 -2
  48. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  49. package/src/__tests__/channel-readiness-service.test.ts +62 -2
  50. package/src/__tests__/checker.test.ts +3 -4
  51. package/src/__tests__/config-loader-backfill.test.ts +461 -147
  52. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  53. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  54. package/src/__tests__/config-schema.test.ts +1 -0
  55. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  56. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +20 -11
  57. package/src/__tests__/config-watcher.test.ts +142 -71
  58. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  59. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  60. package/src/__tests__/context-search-fanout.test.ts +0 -1
  61. package/src/__tests__/context-search-memory-source.test.ts +3 -7
  62. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  63. package/src/__tests__/context-search-pkb-source.test.ts +0 -1
  64. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  65. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  66. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  67. package/src/__tests__/conversation-agent-loop.test.ts +454 -5
  68. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  69. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  70. package/src/__tests__/conversation-error.test.ts +150 -3
  71. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  72. package/src/__tests__/conversation-lifecycle.test.ts +36 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  74. package/src/__tests__/conversation-process-callsite.test.ts +43 -0
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  76. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
  77. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
  78. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  79. package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
  80. package/src/__tests__/conversation-slash-commands.test.ts +0 -4
  81. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  82. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  83. package/src/__tests__/conversation-store.test.ts +0 -18
  84. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  85. package/src/__tests__/conversation-surfaces-app-control.test.ts +328 -0
  86. package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
  87. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
  88. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  89. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  90. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  91. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  92. package/src/__tests__/credentials-cli.test.ts +12 -12
  93. package/src/__tests__/cu-unified-flow.test.ts +351 -23
  94. package/src/__tests__/daemon-credential-client.test.ts +101 -19
  95. package/src/__tests__/date-context.test.ts +164 -2
  96. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  97. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  98. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  99. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  100. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  101. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  102. package/src/__tests__/disk-usage.test.ts +150 -0
  103. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  104. package/src/__tests__/events-client-registration.test.ts +52 -0
  105. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  106. package/src/__tests__/file-write-tool.test.ts +4 -10
  107. package/src/__tests__/filing-service.test.ts +3 -4
  108. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  109. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  110. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  111. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
  112. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  113. package/src/__tests__/heartbeat-service.test.ts +968 -2
  114. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  115. package/src/__tests__/host-app-control-proxy.test.ts +772 -0
  116. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  117. package/src/__tests__/host-bash-proxy.test.ts +439 -47
  118. package/src/__tests__/host-bash-routes.test.ts +459 -0
  119. package/src/__tests__/host-browser-proxy.test.ts +24 -22
  120. package/src/__tests__/host-browser-routes.test.ts +39 -13
  121. package/src/__tests__/host-cu-proxy.test.ts +248 -52
  122. package/src/__tests__/host-cu-routes-targeted.test.ts +429 -0
  123. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  124. package/src/__tests__/host-file-proxy-targeted.test.ts +378 -0
  125. package/src/__tests__/host-file-proxy.test.ts +301 -45
  126. package/src/__tests__/host-file-read-tool.test.ts +17 -0
  127. package/src/__tests__/host-file-routes-targeted.test.ts +420 -0
  128. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  129. package/src/__tests__/host-proxy-base.test.ts +312 -0
  130. package/src/__tests__/host-shell-tool.test.ts +22 -4
  131. package/src/__tests__/host-transfer-proxy-targeted.test.ts +932 -0
  132. package/src/__tests__/host-transfer-proxy.test.ts +121 -22
  133. package/src/__tests__/host-transfer-routes-targeted.test.ts +662 -0
  134. package/src/__tests__/http-user-message-parity.test.ts +108 -1
  135. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  136. package/src/__tests__/identity-routes.test.ts +103 -1
  137. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  138. package/src/__tests__/injector-chain.test.ts +18 -6
  139. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  140. package/src/__tests__/inline-command-runner.test.ts +0 -1
  141. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
  142. package/src/__tests__/integration-status.test.ts +85 -5
  143. package/src/__tests__/intent-routing.test.ts +0 -1
  144. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  145. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  146. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  147. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  148. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  149. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  150. package/src/__tests__/mcp-cli.test.ts +338 -2
  151. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  152. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  153. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +108 -2
  155. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  156. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  157. package/src/__tests__/oauth-cli.test.ts +0 -2
  158. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  159. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  160. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  161. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  162. package/src/__tests__/openai-provider.test.ts +45 -8
  163. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  164. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  165. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
  166. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  167. package/src/__tests__/platform.test.ts +2 -1
  168. package/src/__tests__/playbook-execution.test.ts +0 -43
  169. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  170. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -25
  171. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  172. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  173. package/src/__tests__/provider-tool-name.test.ts +23 -0
  174. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  175. package/src/__tests__/relay-server.test.ts +15 -4
  176. package/src/__tests__/require-fresh-approval.test.ts +0 -1
  177. package/src/__tests__/retry-backoff.test.ts +87 -0
  178. package/src/__tests__/runtime-events-sse.test.ts +2 -2
  179. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  180. package/src/__tests__/schedule-retry.test.ts +715 -0
  181. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  182. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  183. package/src/__tests__/secret-ingress-http.test.ts +1 -1
  184. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  185. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  186. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  187. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  188. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  189. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  190. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  191. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  192. package/src/__tests__/slack-channel-config.test.ts +9 -14
  193. package/src/__tests__/suggestion-routes.test.ts +46 -0
  194. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  195. package/src/__tests__/system-prompt.test.ts +0 -1
  196. package/src/__tests__/telegram-config.test.ts +0 -1
  197. package/src/__tests__/test-preload.ts +8 -0
  198. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  199. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  200. package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
  201. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  202. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  203. package/src/__tests__/tool-executor.test.ts +0 -1
  204. package/src/__tests__/twilio-config.test.ts +3 -16
  205. package/src/__tests__/twilio-routes.test.ts +3 -5
  206. package/src/__tests__/twilio-validation.test.ts +93 -0
  207. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
  208. package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
  209. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  210. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  211. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  212. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  213. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  214. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  215. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  216. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
  217. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  218. package/src/approvals/guardian-decision-primitive.ts +13 -0
  219. package/src/approvals/guardian-request-resolvers.ts +16 -17
  220. package/src/backup/__tests__/paths.test.ts +0 -22
  221. package/src/backup/__tests__/restore.test.ts +51 -151
  222. package/src/backup/paths.ts +2 -18
  223. package/src/backup/restore.ts +107 -231
  224. package/src/backup/snapshot-lock.ts +2 -27
  225. package/src/bundler/app-bundler.ts +51 -3
  226. package/src/bundler/compiler-tools.ts +3 -2
  227. package/src/calls/call-conversation-messages.ts +46 -10
  228. package/src/calls/relay-server.ts +4 -44
  229. package/src/calls/twilio-config.ts +2 -17
  230. package/src/calls/twilio-rest.ts +33 -105
  231. package/src/calls/twilio-routes.ts +11 -12
  232. package/src/channels/types.ts +8 -7
  233. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  234. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  235. package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
  236. package/src/cli/commands/__tests__/webhooks.test.ts +0 -5
  237. package/src/cli/commands/backup.ts +6 -331
  238. package/src/cli/commands/bash.ts +35 -108
  239. package/src/cli/commands/clients.ts +36 -37
  240. package/src/cli/commands/contacts.ts +137 -25
  241. package/src/cli/commands/conversations.ts +2 -5
  242. package/src/cli/commands/credentials.ts +71 -7
  243. package/src/cli/commands/domain.ts +66 -15
  244. package/src/cli/commands/gateway.ts +183 -0
  245. package/src/cli/commands/keys.ts +9 -6
  246. package/src/cli/commands/mcp.ts +116 -156
  247. package/src/cli/commands/memory-v2.ts +303 -7
  248. package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
  249. package/src/cli/commands/oauth/connect.ts +127 -1
  250. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -4
  251. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -3
  252. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -3
  253. package/src/cli/commands/platform/__tests__/status.test.ts +116 -21
  254. package/src/cli/commands/platform/disconnect.ts +5 -4
  255. package/src/cli/commands/platform/index.ts +16 -25
  256. package/src/cli/commands/status.ts +57 -0
  257. package/src/cli/lib/daemon-credential-client.ts +110 -28
  258. package/src/cli/program.ts +6 -2
  259. package/src/config/assistant-feature-flags.ts +79 -12
  260. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  261. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  262. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  263. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  264. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  265. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  266. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  267. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  268. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  269. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  270. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  271. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  272. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  273. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  274. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  275. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  276. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  277. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  278. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  279. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  280. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  281. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  282. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  283. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  284. package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
  285. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  286. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  287. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +25 -4
  288. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  289. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  290. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  291. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  292. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  293. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  294. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  295. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  296. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  297. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  298. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  299. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  300. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  301. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  302. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  303. package/src/config/bundled-tool-registry.ts +21 -0
  304. package/src/config/env-registry.ts +0 -2
  305. package/src/config/env.ts +19 -20
  306. package/src/config/feature-flag-registry.json +47 -135
  307. package/src/config/loader.ts +197 -104
  308. package/src/config/sanitize-for-transfer.ts +2 -0
  309. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  310. package/src/config/schemas/__tests__/memory-v2.test.ts +17 -9
  311. package/src/config/schemas/call-site-catalog.ts +14 -0
  312. package/src/config/schemas/calls.ts +0 -9
  313. package/src/config/schemas/channels.ts +0 -5
  314. package/src/config/schemas/heartbeat.ts +64 -1
  315. package/src/config/schemas/ingress.ts +10 -6
  316. package/src/config/schemas/llm.ts +7 -10
  317. package/src/config/schemas/memory-lifecycle.ts +90 -24
  318. package/src/config/schemas/memory-v2.ts +121 -13
  319. package/src/config/schemas/platform.ts +49 -3
  320. package/src/config/schemas/services.ts +29 -15
  321. package/src/config/schemas/skills.ts +0 -6
  322. package/src/config/seed-inference-profiles.ts +230 -33
  323. package/src/contacts/contact-store.ts +0 -55
  324. package/src/contacts/contacts-write.ts +0 -27
  325. package/src/context/window-manager.ts +1 -2
  326. package/src/credential-execution/feature-gates.ts +10 -10
  327. package/src/credential-execution/process-manager.ts +12 -41
  328. package/src/daemon/__tests__/conversation-tool-setup.test.ts +187 -5
  329. package/src/daemon/assistant-attachments.ts +4 -4
  330. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  331. package/src/daemon/config-watcher.ts +89 -60
  332. package/src/daemon/conversation-agent-loop-handlers.ts +27 -3
  333. package/src/daemon/conversation-agent-loop.ts +202 -61
  334. package/src/daemon/conversation-error.ts +87 -15
  335. package/src/daemon/conversation-lifecycle.ts +9 -4
  336. package/src/daemon/conversation-process.ts +24 -11
  337. package/src/daemon/conversation-runtime-assembly.ts +28 -2
  338. package/src/daemon/conversation-store.ts +2 -2
  339. package/src/daemon/conversation-surfaces.ts +305 -4
  340. package/src/daemon/conversation-tool-setup.ts +66 -62
  341. package/src/daemon/conversation.ts +38 -24
  342. package/src/daemon/date-context.ts +71 -22
  343. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  344. package/src/daemon/disk-pressure-guard.ts +343 -0
  345. package/src/daemon/disk-pressure-policy.ts +163 -0
  346. package/src/daemon/doordash-steps.ts +1 -1
  347. package/src/daemon/handlers/shared.ts +4 -2
  348. package/src/daemon/handlers/skills.ts +3 -4
  349. package/src/daemon/host-app-control-proxy.ts +389 -0
  350. package/src/daemon/host-bash-proxy.ts +117 -82
  351. package/src/daemon/host-browser-proxy.ts +67 -82
  352. package/src/daemon/host-cu-proxy.ts +127 -86
  353. package/src/daemon/host-file-proxy.ts +129 -69
  354. package/src/daemon/host-proxy-base.ts +294 -0
  355. package/src/daemon/host-proxy-preactivation.ts +82 -0
  356. package/src/daemon/host-transfer-proxy.ts +338 -129
  357. package/src/daemon/lifecycle.ts +194 -145
  358. package/src/daemon/meet-host-supervisor.ts +4 -4
  359. package/src/daemon/meet-manifest-loader.ts +0 -1
  360. package/src/daemon/memory-v2-startup.ts +14 -4
  361. package/src/daemon/message-protocol.ts +6 -8
  362. package/src/daemon/message-types/contacts.ts +23 -1
  363. package/src/daemon/message-types/conversations.ts +15 -8
  364. package/src/daemon/message-types/disk-pressure.ts +9 -0
  365. package/src/daemon/message-types/host-app-control.ts +150 -0
  366. package/src/daemon/message-types/host-bash.ts +4 -0
  367. package/src/daemon/message-types/host-cu.ts +2 -0
  368. package/src/daemon/message-types/host-file.ts +4 -0
  369. package/src/daemon/message-types/host-transfer.ts +3 -0
  370. package/src/daemon/message-types/messages.ts +3 -0
  371. package/src/daemon/message-types/schedules.ts +8 -3
  372. package/src/daemon/message-types/skills.ts +2 -2
  373. package/src/daemon/process-message.ts +18 -1
  374. package/src/daemon/profiler-run-store.ts +5 -5
  375. package/src/daemon/shutdown-handlers.ts +0 -3
  376. package/src/daemon/tool-setup-types.ts +51 -0
  377. package/src/daemon/tool-side-effects.ts +1 -1
  378. package/src/documents/document-store.ts +85 -0
  379. package/src/events/tool-audit-listener.ts +2 -1
  380. package/src/filing/filing-service.ts +30 -5
  381. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +24 -23
  382. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +252 -0
  383. package/src/heartbeat/heartbeat-run-store.ts +249 -0
  384. package/src/heartbeat/heartbeat-service.ts +459 -54
  385. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  386. package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
  387. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  388. package/src/home/feed-scheduler.ts +18 -0
  389. package/src/home/post-connect-feed.ts +68 -0
  390. package/src/home/relationship-state-writer.ts +17 -92
  391. package/src/home/suggested-prompts.ts +46 -10
  392. package/src/inbound/platform-callback-registration.ts +8 -15
  393. package/src/inbound/public-ingress-urls.ts +32 -34
  394. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  395. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  396. package/src/ipc/assistant-server.ts +70 -3
  397. package/src/ipc/cli-client.ts +32 -1
  398. package/src/ipc/gateway-client.ts +37 -3
  399. package/src/live-voice/live-voice-archive.ts +4 -4
  400. package/src/live-voice/live-voice-metrics.ts +10 -10
  401. package/src/live-voice/protocol.ts +5 -7
  402. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  403. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  404. package/src/mcp/mcp-auth-state.ts +133 -0
  405. package/src/mcp/mcp-oauth-provider.ts +19 -0
  406. package/src/media/image-service.ts +1 -7
  407. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  408. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  409. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  412. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  413. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  414. package/src/memory/admin.ts +5 -9
  415. package/src/memory/anisotropy.test.ts +247 -0
  416. package/src/memory/anisotropy.ts +443 -0
  417. package/src/memory/auto-analysis-constants.ts +17 -0
  418. package/src/memory/auto-analysis-guard.ts +5 -15
  419. package/src/memory/canonical-guardian-store.ts +7 -7
  420. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  421. package/src/memory/context-search/agent-protocol.ts +6 -6
  422. package/src/memory/context-search/agent-runner.ts +51 -9
  423. package/src/memory/context-search/sources/conversations.ts +2 -11
  424. package/src/memory/context-search/sources/memory-v2.ts +22 -9
  425. package/src/memory/context-search/sources/memory.ts +0 -1
  426. package/src/memory/context-search/types.ts +0 -1
  427. package/src/memory/conversation-crud.ts +5 -13
  428. package/src/memory/conversation-key-store.ts +2 -15
  429. package/src/memory/db-init.ts +6 -0
  430. package/src/memory/embedding-backend.ts +9 -21
  431. package/src/memory/embedding-runtime-manager.ts +119 -5
  432. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +81 -25
  433. package/src/memory/graph/conversation-graph-memory.ts +43 -78
  434. package/src/memory/graph/extraction.ts +1 -3
  435. package/src/memory/graph/graph-search.test.ts +10 -67
  436. package/src/memory/graph/graph-search.ts +9 -20
  437. package/src/memory/graph/retriever.test.ts +6 -0
  438. package/src/memory/graph/retriever.ts +34 -10
  439. package/src/memory/graph/tools.ts +1 -1
  440. package/src/memory/indexer.ts +54 -45
  441. package/src/memory/job-handlers/backfill.ts +2 -11
  442. package/src/memory/job-handlers/cleanup.ts +43 -0
  443. package/src/memory/job-handlers/embedding.ts +6 -8
  444. package/src/memory/job-handlers/summarization.ts +2 -7
  445. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  446. package/src/memory/jobs/embed-concept-page.ts +28 -2
  447. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  448. package/src/memory/jobs-store.ts +114 -22
  449. package/src/memory/jobs-worker.ts +193 -106
  450. package/src/memory/memory-v2-activation-log-store.ts +33 -15
  451. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  452. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  453. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  454. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  455. package/src/memory/migrations/index.ts +6 -0
  456. package/src/memory/migrations/registry.ts +8 -0
  457. package/src/memory/pkb/pkb-search.test.ts +6 -0
  458. package/src/memory/pkb/pkb-search.ts +7 -0
  459. package/src/memory/qdrant-client.ts +49 -32
  460. package/src/memory/rerank-local.ts +374 -0
  461. package/src/memory/schema/infrastructure.ts +15 -0
  462. package/src/memory/search/semantic.ts +13 -67
  463. package/src/memory/sparse-tokenize.ts +49 -0
  464. package/src/memory/trace-event-store.ts +1 -17
  465. package/src/memory/v2/__tests__/activation.test.ts +387 -344
  466. package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
  467. package/src/memory/v2/__tests__/injection.test.ts +181 -169
  468. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  469. package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
  470. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  471. package/src/memory/v2/__tests__/sim.test.ts +154 -188
  472. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  473. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  474. package/src/memory/v2/__tests__/static-context.test.ts +76 -2
  475. package/src/memory/v2/activation.ts +213 -239
  476. package/src/memory/v2/consolidation-job.ts +65 -17
  477. package/src/memory/v2/constants.ts +7 -0
  478. package/src/memory/v2/injection.ts +123 -103
  479. package/src/memory/v2/prompts/consolidation.ts +348 -92
  480. package/src/memory/v2/qdrant.ts +198 -1
  481. package/src/memory/v2/reranker.ts +177 -0
  482. package/src/memory/v2/sim.ts +113 -77
  483. package/src/memory/v2/skill-content.ts +4 -3
  484. package/src/memory/v2/skill-store.ts +91 -53
  485. package/src/memory/v2/sparse-bm25.ts +245 -0
  486. package/src/memory/v2/static-context.ts +28 -5
  487. package/src/memory/v2/types.ts +10 -10
  488. package/src/messaging/providers/gmail/types.ts +0 -49
  489. package/src/messaging/providers/slack/adapter.ts +1 -31
  490. package/src/messaging/providers/slack/types.ts +0 -32
  491. package/src/notifications/README.md +10 -10
  492. package/src/notifications/broadcaster.ts +1 -1
  493. package/src/notifications/copy-composer.ts +13 -0
  494. package/src/notifications/guardian-question-mode.ts +5 -5
  495. package/src/notifications/signal.ts +4 -0
  496. package/src/oauth/AGENTS.md +3 -1
  497. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  498. package/src/oauth/connect-orchestrator.ts +6 -0
  499. package/src/oauth/connection-resolver.test.ts +66 -1
  500. package/src/oauth/connection-resolver.ts +55 -1
  501. package/src/oauth/credential-token-resolver.ts +1 -3
  502. package/src/oauth/manual-token-connection.ts +0 -4
  503. package/src/oauth/oauth-connect-state.ts +77 -0
  504. package/src/oauth/seed-providers.ts +58 -1
  505. package/src/outbound-proxy/index.ts +1 -37
  506. package/src/outbound-proxy/logging.ts +1 -1
  507. package/src/outbound-proxy/policy.ts +6 -5
  508. package/src/outbound-proxy/router.ts +2 -1
  509. package/src/permissions/approval-policy.test.ts +6 -275
  510. package/src/permissions/approval-policy.ts +0 -51
  511. package/src/permissions/checker.test.ts +0 -1
  512. package/src/permissions/checker.ts +3 -17
  513. package/src/permissions/gateway-threshold-reader.ts +2 -0
  514. package/src/permissions/prompter.ts +34 -1
  515. package/src/permissions/secret-prompter.ts +6 -2
  516. package/src/plugins/defaults/injectors.ts +35 -2
  517. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  518. package/src/plugins/types.ts +7 -0
  519. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  520. package/src/proactive-artifact/decision.test.ts +226 -0
  521. package/src/proactive-artifact/decision.ts +165 -0
  522. package/src/proactive-artifact/index.ts +7 -0
  523. package/src/proactive-artifact/job.test.ts +867 -0
  524. package/src/proactive-artifact/job.ts +352 -0
  525. package/src/proactive-artifact/message-copy.ts +41 -0
  526. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  527. package/src/proactive-artifact/trigger-state.ts +119 -0
  528. package/src/prompts/bootstrap-cleanup.ts +27 -0
  529. package/src/prompts/normalize-onboarding.ts +80 -0
  530. package/src/prompts/persona-resolver.ts +101 -9
  531. package/src/prompts/system-prompt.ts +23 -24
  532. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  533. package/src/prompts/templates/SOUL.md +13 -1
  534. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  535. package/src/providers/model-intents.ts +7 -0
  536. package/src/providers/openrouter/client.ts +8 -0
  537. package/src/providers/retry.ts +50 -0
  538. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  539. package/src/providers/types.ts +1 -0
  540. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  541. package/src/runtime/agent-wake.ts +238 -100
  542. package/src/runtime/assistant-event-hub.ts +151 -99
  543. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  544. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  545. package/src/runtime/auth/middleware.ts +0 -96
  546. package/src/runtime/auth/route-policy.ts +32 -0
  547. package/src/runtime/auth/same-actor.ts +216 -0
  548. package/src/runtime/btw-sidechain.ts +2 -3
  549. package/src/runtime/channel-invite-transport.ts +2 -48
  550. package/src/runtime/channel-invite-transports/email.ts +1 -1
  551. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  552. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  553. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  554. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  555. package/src/runtime/channel-invite-types.ts +54 -0
  556. package/src/runtime/channel-readiness-service.ts +32 -13
  557. package/src/runtime/channel-retry-sweep.ts +65 -1
  558. package/src/runtime/guardian-reply-router.ts +10 -0
  559. package/src/runtime/http-server.ts +3 -329
  560. package/src/runtime/http-types.ts +0 -5
  561. package/src/runtime/local-actor-identity.ts +52 -11
  562. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  563. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  564. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  565. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
  566. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  567. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  568. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  569. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  570. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  571. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
  572. package/src/runtime/migrations/migration-transport.ts +7 -7
  573. package/src/runtime/migrations/vbundle-builder.ts +327 -60
  574. package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
  575. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  576. package/src/runtime/migrations/vbundle-importer.ts +245 -68
  577. package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
  578. package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
  579. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  580. package/src/runtime/migrations/vbundle-validator.ts +114 -0
  581. package/src/runtime/pending-interactions.ts +43 -9
  582. package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
  583. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  584. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -5
  585. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  586. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  587. package/src/runtime/routes/approval-interception-types.ts +13 -0
  588. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  589. package/src/runtime/routes/backup-routes.ts +15 -38
  590. package/src/runtime/routes/btw-routes.ts +14 -37
  591. package/src/runtime/routes/client-routes.ts +21 -2
  592. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  593. package/src/runtime/routes/contact-routes.ts +0 -25
  594. package/src/runtime/routes/conversation-query-routes.ts +36 -1
  595. package/src/runtime/routes/conversation-routes.ts +65 -39
  596. package/src/runtime/routes/debug-bash-routes.ts +163 -0
  597. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  598. package/src/runtime/routes/document-pdf-renderer.ts +169 -0
  599. package/src/runtime/routes/documents-routes.ts +32 -75
  600. package/src/runtime/routes/errors.ts +19 -4
  601. package/src/runtime/routes/events-routes.ts +38 -0
  602. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  603. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  604. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  605. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  606. package/src/runtime/routes/host-bash-routes.ts +56 -6
  607. package/src/runtime/routes/host-browser-routes.ts +108 -13
  608. package/src/runtime/routes/host-cu-routes.ts +66 -9
  609. package/src/runtime/routes/host-file-routes.ts +54 -5
  610. package/src/runtime/routes/host-transfer-routes.ts +122 -19
  611. package/src/runtime/routes/http-adapter.ts +1 -0
  612. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  613. package/src/runtime/routes/identity-routes.ts +21 -180
  614. package/src/runtime/routes/inbound-message-handler.ts +78 -21
  615. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
  616. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  617. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
  618. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  619. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  620. package/src/runtime/routes/index.ts +14 -0
  621. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  622. package/src/runtime/routes/memory-item-routes.test.ts +41 -15
  623. package/src/runtime/routes/memory-item-routes.ts +10 -12
  624. package/src/runtime/routes/memory-v2-routes.ts +474 -1
  625. package/src/runtime/routes/migration-routes.ts +96 -0
  626. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  627. package/src/runtime/routes/schedule-routes.ts +7 -0
  628. package/src/runtime/verification-outbound-actions.ts +4 -4
  629. package/src/runtime/verification-templates.ts +4 -7
  630. package/src/schedule/integration-status.ts +66 -2
  631. package/src/schedule/recurrence-engine.ts +4 -1
  632. package/src/schedule/retry-backoff.ts +18 -0
  633. package/src/schedule/retry-policy.ts +82 -0
  634. package/src/schedule/run-script.ts +37 -5
  635. package/src/schedule/schedule-recovery.ts +64 -0
  636. package/src/schedule/schedule-store.ts +106 -2
  637. package/src/schedule/scheduler-types.ts +25 -0
  638. package/src/schedule/scheduler.ts +83 -39
  639. package/src/security/encrypted-store.ts +2 -0
  640. package/src/security/oauth-callback-registry.ts +8 -0
  641. package/src/security/secure-keys.ts +55 -0
  642. package/src/sequence/analytics.ts +5 -5
  643. package/src/sequence/engine.ts +1 -1
  644. package/src/skills/catalog-files.ts +2 -8
  645. package/src/skills/include-graph.ts +5 -5
  646. package/src/skills/remote-skill-policy.ts +10 -16
  647. package/src/skills/skill-file-provider.ts +1 -1
  648. package/src/skills/skill-file-types.ts +13 -0
  649. package/src/skills/skillssh-audit-types.ts +28 -0
  650. package/src/skills/skillssh-registry.ts +8 -21
  651. package/src/subagent/index.ts +1 -7
  652. package/src/subagent/manager.ts +1 -15
  653. package/src/tasks/task-runner.ts +0 -1
  654. package/src/tasks/task-store.ts +0 -3
  655. package/src/telemetry/types.ts +2 -0
  656. package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
  657. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  658. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  659. package/src/tools/apps/executors.ts +56 -69
  660. package/src/tools/background-tool-registry.ts +17 -3
  661. package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
  662. package/src/tools/browser/browser-execution.ts +2 -2
  663. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
  664. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  665. package/src/tools/browser/cdp-client/factory.ts +23 -24
  666. package/src/tools/browser/cdp-client/index.ts +1 -14
  667. package/src/tools/computer-use/definitions.ts +42 -20
  668. package/src/tools/executor.ts +2 -0
  669. package/src/tools/host-filesystem/edit.test.ts +151 -0
  670. package/src/tools/host-filesystem/edit.ts +68 -0
  671. package/src/tools/host-filesystem/read.test.ts +129 -0
  672. package/src/tools/host-filesystem/read.ts +68 -0
  673. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  674. package/src/tools/host-filesystem/transfer.ts +78 -3
  675. package/src/tools/host-filesystem/write.test.ts +134 -0
  676. package/src/tools/host-filesystem/write.ts +68 -0
  677. package/src/tools/host-terminal/host-shell.ts +66 -1
  678. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  679. package/src/tools/memory/register.test.ts +12 -9
  680. package/src/tools/memory/register.ts +1 -2
  681. package/src/tools/provider-tool-name.ts +28 -0
  682. package/src/tools/registry.ts +30 -9
  683. package/src/tools/schedule/create.ts +6 -0
  684. package/src/tools/schedule/list.ts +2 -0
  685. package/src/tools/schedule/update.ts +10 -0
  686. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  687. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  688. package/src/tools/skills/load.ts +0 -32
  689. package/src/tools/terminal/shell.ts +9 -1
  690. package/src/tools/tool-approval-handler.ts +32 -11
  691. package/src/tools/types.ts +28 -2
  692. package/src/tts/provider-catalog.ts +3 -5
  693. package/src/usage/pricing.ts +1 -1
  694. package/src/util/disk-usage.ts +138 -0
  695. package/src/util/platform.ts +21 -11
  696. package/src/util/process-liveness.ts +26 -0
  697. package/src/workspace/hatched-date.ts +86 -0
  698. package/src/workspace/heartbeat-service.ts +19 -0
  699. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  700. package/src/workspace/migrations/006-services-config.ts +8 -5
  701. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  702. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  703. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  704. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  705. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  706. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  707. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
  708. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  709. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
  710. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  711. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  712. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
  713. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  714. package/src/workspace/migrations/AGENTS.md +1 -1
  715. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  716. package/src/workspace/migrations/registry.ts +8 -0
  717. package/src/workspace/migrations/utils.ts +21 -0
  718. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  719. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
  720. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
  721. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
  722. package/src/__tests__/twilio-rest.test.ts +0 -34
  723. package/src/backup/__tests__/backup-key.test.ts +0 -152
  724. package/src/backup/__tests__/backup-worker.test.ts +0 -782
  725. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  726. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  727. package/src/backup/backup-key.ts +0 -137
  728. package/src/backup/backup-worker.ts +0 -472
  729. package/src/backup/offsite-writer.ts +0 -222
  730. package/src/backup/stream-crypt.ts +0 -263
  731. package/src/daemon/message-types/pairing.ts +0 -58
  732. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  733. package/src/memory/v2/skill-qdrant.ts +0 -395
  734. package/src/outbound-proxy/config.ts +0 -20
  735. package/src/outbound-proxy/health.ts +0 -18
  736. package/src/outbound-proxy/types.ts +0 -150
  737. package/src/runtime/capability-tokens.ts +0 -190
  738. package/src/signals/bash.ts +0 -198
  739. package/src/signals/mcp-reload.ts +0 -18
@@ -60,7 +60,6 @@ const state = {
60
60
  embedCalls: [] as Array<{ inputs: unknown[] }>,
61
61
  sparseCalls: [] as string[],
62
62
  embedReturn: [[0.1, 0.2, 0.3]] as number[][],
63
- sparseReturn: { indices: [1, 2, 3], values: [0.5, 0.5, 0.5] },
64
63
  // Programmable Qdrant query response — one entry per `using` channel,
65
64
  // shifted in order so each test can stage dense + sparse results.
66
65
  queryResponses: {
@@ -71,16 +70,6 @@ const state = {
71
70
  points: Array<{ score?: number; payload: Record<string, unknown> }>;
72
71
  }>,
73
72
  },
74
- // Separate response queue for the dedicated skills collection so a test
75
- // querying both concept pages and skills doesn't have to interleave.
76
- skillQueryResponses: {
77
- dense: [] as Array<{
78
- points: Array<{ score?: number; payload: Record<string, unknown> }>;
79
- }>,
80
- sparse: [] as Array<{
81
- points: Array<{ score?: number; payload: Record<string, unknown> }>;
82
- }>,
83
- },
84
73
  queryCalls: [] as Array<{
85
74
  collection: string;
86
75
  using: string;
@@ -90,7 +79,7 @@ const state = {
90
79
  };
91
80
 
92
81
  // Re-export every real symbol from the embedding-backend module, overriding
93
- // only the two we control. Bun's `mock.module` replacement is process-wide,
82
+ // only the one we control. Bun's `mock.module` replacement is process-wide,
94
83
  // so a partial mock here would break sibling test files that import other
95
84
  // exports from the same module (`selectEmbeddingBackend`, etc.).
96
85
  const realEmbeddingBackend = await import("../../embedding-backend.js");
@@ -104,9 +93,23 @@ mock.module("../../embedding-backend.js", () => ({
104
93
  vectors: state.embedReturn,
105
94
  };
106
95
  },
107
- generateSparseEmbedding: (text: string) => {
96
+ }));
97
+
98
+ // `sim.ts` builds the query-side sparse vector via BM25's
99
+ // `generateBm25QueryEmbedding`. Wrap it to record the call text, then
100
+ // delegate to the real implementation so the resulting sparse vector is
101
+ // well-formed. Capture the function reference *before* registering the
102
+ // mock — ESM live bindings resolve through the namespace at call time, so
103
+ // `realSparseBm25.fn(...)` after `mock.module` would route into the
104
+ // mocked version and recurse.
105
+ const realSparseBm25 = await import("../sparse-bm25.js");
106
+ const realGenerateBm25QueryEmbedding =
107
+ realSparseBm25.generateBm25QueryEmbedding;
108
+ mock.module("../sparse-bm25.js", () => ({
109
+ ...realSparseBm25,
110
+ generateBm25QueryEmbedding: (text: string) => {
108
111
  state.sparseCalls.push(text);
109
- return state.sparseReturn;
112
+ return realGenerateBm25QueryEmbedding(text);
110
113
  },
111
114
  }));
112
115
 
@@ -134,11 +137,7 @@ class MockQdrantClient {
134
137
  filter: params.filter,
135
138
  });
136
139
  const channel = params.using as "dense" | "sparse";
137
- const queue =
138
- name === "memory_v2_skills"
139
- ? state.skillQueryResponses[channel]
140
- : state.queryResponses[channel];
141
- return queue.shift() ?? { points: [] };
140
+ return state.queryResponses[channel].shift() ?? { points: [] };
142
141
  }
143
142
  }
144
143
 
@@ -146,9 +145,7 @@ mock.module("@qdrant/js-client-rest", () => ({
146
145
  QdrantClient: MockQdrantClient,
147
146
  }));
148
147
 
149
- const { simBatch, simSkillBatch, clamp01 } = await import("../sim.js");
150
- const { _resetMemoryV2SkillQdrantForTests } =
151
- await import("../skill-qdrant.js");
148
+ const { simBatch, clamp01, effectiveWeights } = await import("../sim.js");
152
149
  const { _resetMemoryV2QdrantForTests } = await import("../qdrant.js");
153
150
 
154
151
  // ---------------------------------------------------------------------------
@@ -159,18 +156,14 @@ function resetState(): void {
159
156
  state.embedCalls.length = 0;
160
157
  state.sparseCalls.length = 0;
161
158
  state.embedReturn = [[0.1, 0.2, 0.3]];
162
- state.sparseReturn = { indices: [1, 2, 3], values: [0.5, 0.5, 0.5] };
163
159
  state.queryResponses.dense.length = 0;
164
160
  state.queryResponses.sparse.length = 0;
165
- state.skillQueryResponses.dense.length = 0;
166
- state.skillQueryResponses.sparse.length = 0;
167
161
  state.queryCalls.length = 0;
168
162
  // Bun's `mock.module` persists across files in the same process, so the
169
- // qdrant modules' singletons may already hold a MockQdrantClient instance
170
- // from a sibling test file. Reset both readiness caches so each test in
171
- // this file gets a fresh `new QdrantClient()` resolved against our mock.
163
+ // qdrant module's singleton may already hold a MockQdrantClient instance
164
+ // from a sibling test file. Reset readiness so each test in this file
165
+ // gets a fresh `new QdrantClient()` resolved against our mock.
172
166
  _resetMemoryV2QdrantForTests();
173
- _resetMemoryV2SkillQdrantForTests();
174
167
  }
175
168
 
176
169
  function configWithWeights(
@@ -216,6 +209,120 @@ afterEach(resetState);
216
209
  // clamp01
217
210
  // ---------------------------------------------------------------------------
218
211
 
212
+ // ---------------------------------------------------------------------------
213
+ // effectiveWeights — adaptive sparse weighting
214
+ // ---------------------------------------------------------------------------
215
+
216
+ describe("effectiveWeights", () => {
217
+ // The helper takes a generic config, but only reads
218
+ // `memory.v2.min_sparse_spread` / `full_sparse_spread`. Build a minimal
219
+ // shape so test cases can opt into custom thresholds vs the built-in
220
+ // defaults (0.2 / 0.5).
221
+ function configWithSpreadOverrides(
222
+ min?: number,
223
+ full?: number,
224
+ ): AssistantConfig {
225
+ return {
226
+ memory: { v2: { min_sparse_spread: min, full_sparse_spread: full } },
227
+ } as unknown as AssistantConfig;
228
+ }
229
+ const baseConfig = configWithSpreadOverrides();
230
+
231
+ test("returns base weights when sparse weight is zero", () => {
232
+ const result = effectiveWeights(
233
+ [{ sparseScore: 1 }, { sparseScore: 2 }],
234
+ 2,
235
+ 1.0,
236
+ 0.0,
237
+ baseConfig,
238
+ );
239
+ expect(result.dense).toBe(1.0);
240
+ expect(result.sparse).toBe(0);
241
+ });
242
+
243
+ test("returns base weights when fewer than 2 sparse-bearing hits", () => {
244
+ // Single sparse-bearing hit — spread is undefined.
245
+ const result = effectiveWeights(
246
+ [{ sparseScore: 5 }, {}],
247
+ 5,
248
+ 0.7,
249
+ 0.3,
250
+ baseConfig,
251
+ );
252
+ expect(result.dense).toBeCloseTo(0.7);
253
+ expect(result.sparse).toBeCloseTo(0.3);
254
+ expect(result.spread).toBe(0);
255
+ });
256
+
257
+ test("collapses sparse weight to 0 when spread is below min_sparse_spread", () => {
258
+ // Three hits with sparseNorm = {0.95, 0.97, 1.0} → spread 0.05 < 0.2.
259
+ const hits = [
260
+ { sparseScore: 9.5 },
261
+ { sparseScore: 9.7 },
262
+ { sparseScore: 10 },
263
+ ];
264
+ const result = effectiveWeights(hits, 10, 0.7, 0.3, baseConfig);
265
+ expect(result.spread).toBeCloseTo(0.05, 6);
266
+ expect(result.sparse).toBeCloseTo(0, 6);
267
+ // Dense compensates: gets the full sparse weight added back.
268
+ expect(result.dense).toBeCloseTo(1.0, 6);
269
+ });
270
+
271
+ test("preserves base weights when spread reaches full_sparse_spread", () => {
272
+ // sparseNorm = {0.5, 1.0} → spread 0.5 === default full threshold.
273
+ const hits = [{ sparseScore: 5 }, { sparseScore: 10 }];
274
+ const result = effectiveWeights(hits, 10, 0.7, 0.3, baseConfig);
275
+ expect(result.spread).toBeCloseTo(0.5, 6);
276
+ expect(result.sparse).toBeCloseTo(0.3, 6);
277
+ expect(result.dense).toBeCloseTo(0.7, 6);
278
+ });
279
+
280
+ test("interpolates linearly between min and full thresholds", () => {
281
+ // sparseNorm = {0.65, 1.0} → spread 0.35; midway between 0.2 and 0.5
282
+ // → factor = 0.5; effSparse = 0.5 * 0.3 = 0.15; effDense = 0.7 + 0.15.
283
+ const hits = [{ sparseScore: 6.5 }, { sparseScore: 10 }];
284
+ const result = effectiveWeights(hits, 10, 0.7, 0.3, baseConfig);
285
+ expect(result.spread).toBeCloseTo(0.35, 6);
286
+ expect(result.sparse).toBeCloseTo(0.15, 6);
287
+ expect(result.dense).toBeCloseTo(0.85, 6);
288
+ });
289
+
290
+ test("config overrides min and full thresholds", () => {
291
+ // Custom: min=0.0, full=1.0 — spread is now in [0, 1] linearly.
292
+ const config = configWithSpreadOverrides(0.0, 1.0);
293
+ const hits = [{ sparseScore: 8 }, { sparseScore: 10 }];
294
+ const result = effectiveWeights(hits, 10, 0.7, 0.3, config);
295
+ // spread 0.2; factor = 0.2; effSparse = 0.06.
296
+ expect(result.spread).toBeCloseTo(0.2, 6);
297
+ expect(result.sparse).toBeCloseTo(0.06, 6);
298
+ expect(result.dense).toBeCloseTo(0.94, 6);
299
+ });
300
+
301
+ test("falls back to base weights when full <= min (degenerate config)", () => {
302
+ const config = configWithSpreadOverrides(0.5, 0.3);
303
+ const hits = [{ sparseScore: 5 }, { sparseScore: 10 }];
304
+ const result = effectiveWeights(hits, 10, 0.7, 0.3, config);
305
+ expect(result.sparse).toBeCloseTo(0.3, 6);
306
+ expect(result.dense).toBeCloseTo(0.7, 6);
307
+ });
308
+
309
+ test("dense + sparse always equals baseDense + baseSparse", () => {
310
+ // Property check: total weight is preserved across the spread spectrum
311
+ // so `fused` stays interpretable as a [0, 1] similarity regardless of
312
+ // how aggressively sparse is collapsed.
313
+ const cases = [
314
+ [{ sparseScore: 1 }, { sparseScore: 1.05 }], // tiny spread
315
+ [{ sparseScore: 1 }, { sparseScore: 5 }], // mid spread
316
+ [{ sparseScore: 1 }, { sparseScore: 10 }], // full spread
317
+ ];
318
+ for (const hits of cases) {
319
+ const maxSparse = Math.max(...hits.map((h) => h.sparseScore));
320
+ const result = effectiveWeights(hits, maxSparse, 0.7, 0.3, baseConfig);
321
+ expect(result.dense + result.sparse).toBeCloseTo(1.0, 6);
322
+ }
323
+ });
324
+ });
325
+
219
326
  describe("clamp01", () => {
220
327
  test("passes values already in [0, 1] through unchanged", () => {
221
328
  expect(clamp01(0)).toBe(0);
@@ -247,6 +354,24 @@ describe("simBatch", () => {
247
354
  expect(state.queryCalls).toHaveLength(0);
248
355
  });
249
356
 
357
+ test("empty text returns empty map without touching backends", async () => {
358
+ // Turn 1 has no prior assistant message, so `computeOwnActivation` calls
359
+ // `simBatch("", slugs, config)`. Gemini rejects empty content with HTTP
360
+ // 400 — short-circuit here so the activation pipeline doesn't crash.
361
+ const config = configWithWeights(0.7, 0.3);
362
+
363
+ for (const text of ["", " ", "\n\n"]) {
364
+ state.embedCalls.length = 0;
365
+ state.sparseCalls.length = 0;
366
+ state.queryCalls.length = 0;
367
+ const out = await simBatch(text, ["alice-vscode"], config);
368
+ expect(out.size).toBe(0);
369
+ expect(state.embedCalls).toHaveLength(0);
370
+ expect(state.sparseCalls).toHaveLength(0);
371
+ expect(state.queryCalls).toHaveLength(0);
372
+ }
373
+ });
374
+
250
375
  test("identical text yields ~1.0 when both channels max out", async () => {
251
376
  const config = configWithWeights(0.7, 0.3);
252
377
  stageHybridResponse([
@@ -388,162 +513,3 @@ describe("simBatch", () => {
388
513
  }
389
514
  });
390
515
  });
391
-
392
- // ---------------------------------------------------------------------------
393
- // simSkillBatch
394
- // ---------------------------------------------------------------------------
395
-
396
- /**
397
- * Stage a single hybrid response on the dedicated skills queues. Mirrors
398
- * `stageHybridResponse` but uses `payload.id` (skills' Qdrant payload key)
399
- * instead of `payload.slug`.
400
- */
401
- function stageSkillHybridResponse(
402
- hits: Array<{ id: string; denseScore?: number; sparseScore?: number }>,
403
- ): void {
404
- state.skillQueryResponses.dense.push({
405
- points: hits
406
- .filter((h) => h.denseScore !== undefined)
407
- .map((h) => ({ score: h.denseScore, payload: { id: h.id } })),
408
- });
409
- state.skillQueryResponses.sparse.push({
410
- points: hits
411
- .filter((h) => h.sparseScore !== undefined)
412
- .map((h) => ({ score: h.sparseScore, payload: { id: h.id } })),
413
- });
414
- }
415
-
416
- describe("simSkillBatch", () => {
417
- test("empty id list returns empty map without touching backends", async () => {
418
- const config = configWithWeights(0.7, 0.3);
419
-
420
- const out = await simSkillBatch("anything", [], config);
421
-
422
- expect(out.size).toBe(0);
423
- expect(state.embedCalls).toHaveLength(0);
424
- expect(state.sparseCalls).toHaveLength(0);
425
- expect(state.queryCalls).toHaveLength(0);
426
- });
427
-
428
- test("queries the dedicated skills collection and forwards an id-IN filter", async () => {
429
- const config = configWithWeights(0.7, 0.3);
430
- stageSkillHybridResponse([]);
431
-
432
- await simSkillBatch(
433
- "query",
434
- ["example-skill-a", "example-skill-b"],
435
- config,
436
- );
437
-
438
- expect(state.queryCalls).toHaveLength(2);
439
- for (const call of state.queryCalls) {
440
- expect(call.collection).toBe("memory_v2_skills");
441
- // The candidate ids are forwarded as a Qdrant filter so Qdrant scores
442
- // exactly the candidate set, not its global top-K. Without this,
443
- // candidate ids absent from the global top-K silently score 0.
444
- expect(call.filter).toEqual({
445
- must: [
446
- { key: "id", match: { any: ["example-skill-a", "example-skill-b"] } },
447
- ],
448
- });
449
- // Limit equals the candidate count.
450
- expect(call.limit).toBe(2);
451
- }
452
- });
453
-
454
- test("fuses dense + sparse with the configured weight blend", async () => {
455
- const config = configWithWeights(0.4, 0.6);
456
- stageSkillHybridResponse([
457
- { id: "example-skill-a", denseScore: 0.5, sparseScore: 4 }, // sparse-norm 1.0
458
- { id: "example-skill-b", denseScore: 0.25, sparseScore: 2 }, // sparse-norm 0.5
459
- ]);
460
-
461
- const out = await simSkillBatch(
462
- "query",
463
- ["example-skill-a", "example-skill-b"],
464
- config,
465
- );
466
-
467
- // example-skill-a: 0.4 * 0.5 + 0.6 * 1.0 = 0.8
468
- // example-skill-b: 0.4 * 0.25 + 0.6 * 0.5 = 0.4
469
- expect(out.get("example-skill-a")).toBeCloseTo(0.8, 6);
470
- expect(out.get("example-skill-b")).toBeCloseTo(0.4, 6);
471
- });
472
-
473
- test("dense-only and sparse-only hits are handled symmetrically", async () => {
474
- const config = configWithWeights(0.7, 0.3);
475
- stageSkillHybridResponse([
476
- { id: "example-skill-a", denseScore: 0.5 /* sparse omitted */ },
477
- { id: "example-skill-b", sparseScore: 8 /* dense omitted */ },
478
- ]);
479
-
480
- const out = await simSkillBatch(
481
- "query",
482
- ["example-skill-a", "example-skill-b"],
483
- config,
484
- );
485
-
486
- // example-skill-a: 0.7 * 0.5 + 0.3 * 0 = 0.35
487
- // example-skill-b: 0.7 * 0 + 0.3 * 1.0 = 0.30 (sparse-norm = 8/8)
488
- expect(out.get("example-skill-a")).toBeCloseTo(0.35, 6);
489
- expect(out.get("example-skill-b")).toBeCloseTo(0.3, 6);
490
- });
491
-
492
- test("forwards candidate ids as the Qdrant restriction; only candidates in result", async () => {
493
- // The bug we're guarding against: when the skills collection has more
494
- // skills than `ids.length`, calling `hybridQuerySkills` without a filter
495
- // returns Qdrant's global top-K. Candidate ids absent from that top-K
496
- // would silently score 0. The fix is to forward the candidate ids as a
497
- // server-side restriction so Qdrant scores exactly the candidate set.
498
- const config = configWithWeights(0.7, 0.3);
499
- stageSkillHybridResponse([
500
- { id: "example-skill-a", denseScore: 0.5, sparseScore: 1 },
501
- // `example-skill-c` would never be returned in production once the
502
- // filter is applied; the post-filter in simSkillBatch defensively
503
- // drops it even if a stale payload slips through.
504
- { id: "example-skill-c", denseScore: 0.9, sparseScore: 1 },
505
- ]);
506
-
507
- const out = await simSkillBatch(
508
- "query",
509
- ["example-skill-a", "example-skill-b"],
510
- config,
511
- );
512
-
513
- // The Qdrant filter was forwarded — both channels carry the id-IN
514
- // restriction matching the caller's candidate set.
515
- expect(state.queryCalls).toHaveLength(2);
516
- for (const call of state.queryCalls) {
517
- expect(call.filter).toEqual({
518
- must: [
519
- { key: "id", match: { any: ["example-skill-a", "example-skill-b"] } },
520
- ],
521
- });
522
- }
523
- // Only candidate ids appear in the result map.
524
- expect(out.has("example-skill-a")).toBe(true);
525
- expect(out.has("example-skill-c")).toBe(false);
526
- });
527
-
528
- test("returned scores are clamped into [0, 1]", async () => {
529
- const config = configWithWeights(0.8, 0.5); // intentionally sums to > 1
530
- stageSkillHybridResponse([
531
- { id: "example-skill-a", denseScore: 1.0, sparseScore: 1 },
532
- ]);
533
-
534
- const out = await simSkillBatch("query", ["example-skill-a"], config);
535
-
536
- expect(out.get("example-skill-a")).toBe(1);
537
- });
538
-
539
- test("embeds the query text exactly once via dense + sparse backends", async () => {
540
- const config = configWithWeights(0.7, 0.3);
541
- stageSkillHybridResponse([]);
542
-
543
- await simSkillBatch("hello skill", ["example-skill-a"], config);
544
-
545
- expect(state.embedCalls).toHaveLength(1);
546
- expect(state.embedCalls[0].inputs).toEqual(["hello skill"]);
547
- expect(state.sparseCalls).toEqual(["hello skill"]);
548
- });
549
- });
@@ -1,12 +1,17 @@
1
1
  /**
2
2
  * Tests for `assistant/src/memory/v2/skill-store.ts`.
3
3
  *
4
- * Coverage matrix from PR 5 acceptance criteria:
4
+ * Coverage matrix:
5
5
  * - `seedV2SkillEntries` enumerates the catalog and calls
6
- * `upsertSkillEmbedding` for each enabled skill.
6
+ * `upsertConceptPageEmbedding` with `slug: "skills/<id>"` for each
7
+ * enabled skill in the unified `memory_v2_concept_pages` collection.
7
8
  * - It skips skills whose declared feature flag is disabled.
8
- * - It calls `pruneSkillsExcept` with the active id list.
9
- * - It populates the `entries` cache so `getSkillCapability` returns each entry.
9
+ * - It calls `pruneSlugsWithPrefixExcept("skills/", ...)` with the active
10
+ * id list as suffixes, so stale skill slugs in the unified collection
11
+ * get pruned without touching concept-page slugs.
12
+ * - It populates the `entries` cache so `getSkillCapability` returns each
13
+ * entry — accepting both bare ids (`"example-skill"`) and unified-collection
14
+ * slugs (`"skills/example-skill"`).
10
15
  * - It swallows errors from the embedding backend — the function resolves
11
16
  * and the cache is unchanged from prior state.
12
17
  *
@@ -29,6 +34,18 @@ mock.module("../../../util/logger.js", () => ({
29
34
  // Programmable test state — drives every mocked dependency below.
30
35
  // ---------------------------------------------------------------------------
31
36
 
37
+ interface UpsertCall {
38
+ slug: string;
39
+ dense: number[];
40
+ sparse: { indices: number[]; values: number[] };
41
+ updatedAt: number;
42
+ }
43
+
44
+ interface PruneCall {
45
+ prefix: string;
46
+ activeSuffixes: readonly string[];
47
+ }
48
+
32
49
  interface TestState {
33
50
  catalog: SkillSummary[];
34
51
  resolved: ResolvedSkill[];
@@ -38,14 +55,8 @@ interface TestState {
38
55
  embedThrows: Error | null;
39
56
  embedReturn: number[][];
40
57
  sparseReturn: { indices: number[]; values: number[] };
41
- upsertCalls: Array<{
42
- id: string;
43
- content: string;
44
- dense: number[];
45
- sparse: { indices: number[]; values: number[] };
46
- updatedAt: number;
47
- }>;
48
- pruneCalls: Array<readonly string[]>;
58
+ upsertCalls: UpsertCall[];
59
+ pruneCalls: PruneCall[];
49
60
  upsertThrows: Error | null;
50
61
  }
51
62
 
@@ -99,13 +110,16 @@ mock.module("../../embedding-backend.js", () => ({
99
110
  generateSparseEmbedding: () => state.sparseReturn,
100
111
  }));
101
112
 
102
- mock.module("../skill-qdrant.js", () => ({
103
- upsertSkillEmbedding: async (params: TestState["upsertCalls"][number]) => {
113
+ mock.module("../qdrant.js", () => ({
114
+ upsertConceptPageEmbedding: async (params: UpsertCall) => {
104
115
  if (state.upsertThrows) throw state.upsertThrows;
105
116
  state.upsertCalls.push(params);
106
117
  },
107
- pruneSkillsExcept: async (ids: readonly string[]) => {
108
- state.pruneCalls.push(ids);
118
+ pruneSlugsWithPrefixExcept: async (
119
+ prefix: string,
120
+ activeSuffixes: readonly string[],
121
+ ) => {
122
+ state.pruneCalls.push({ prefix, activeSuffixes });
109
123
  },
110
124
  }));
111
125
 
@@ -160,7 +174,7 @@ afterEach(resetState);
160
174
  // ---------------------------------------------------------------------------
161
175
 
162
176
  describe("seedV2SkillEntries", () => {
163
- test("enumerates the catalog and upserts one point per enabled skill", async () => {
177
+ test("upserts each enabled skill into the unified collection under skills/<id>", async () => {
164
178
  const skillA = makeSummary({
165
179
  id: "example-skill-a",
166
180
  displayName: "Skill A",
@@ -182,15 +196,16 @@ describe("seedV2SkillEntries", () => {
182
196
  await seedV2SkillEntries();
183
197
 
184
198
  expect(state.upsertCalls).toHaveLength(2);
185
- const ids = state.upsertCalls.map((c) => c.id).sort();
186
- expect(ids).toEqual(["example-skill-a", "example-skill-b"]);
187
-
188
- // Each upsert carries the per-skill dense + sparse + content payload.
189
- const callA = state.upsertCalls.find((c) => c.id === "example-skill-a")!;
199
+ const slugs = state.upsertCalls.map((c) => c.slug).sort();
200
+ expect(slugs).toEqual(["skills/example-skill-a", "skills/example-skill-b"]);
201
+
202
+ // Each upsert carries the per-skill dense + sparse + updatedAt payload,
203
+ // keyed under the unified `skills/<id>` slug.
204
+ const callA = state.upsertCalls.find(
205
+ (c) => c.slug === "skills/example-skill-a",
206
+ )!;
190
207
  expect(callA.dense).toEqual([0.1, 0.2, 0.3]);
191
208
  expect(callA.sparse).toEqual(state.sparseReturn);
192
- expect(callA.content).toContain("Skill A");
193
- expect(callA.content).toContain("(example-skill-a)");
194
209
  expect(callA.updatedAt).toBeGreaterThan(0);
195
210
  });
196
211
 
@@ -207,12 +222,11 @@ describe("seedV2SkillEntries", () => {
207
222
  await seedV2SkillEntries();
208
223
 
209
224
  expect(state.upsertCalls).toHaveLength(1);
210
- expect(state.upsertCalls[0].id).toBe("example-skill-a");
225
+ expect(state.upsertCalls[0].slug).toBe("skills/example-skill-a");
211
226
  });
212
227
 
213
228
  test("does not re-seed an installed-but-disabled skill from the remote catalog", async () => {
214
- // Regression for https://github.com/vellum-ai/vellum-assistant/pull/28635
215
- // (Codex P1): if `seenIds` is built only from enabled skills, a locally
229
+ // Regression: if `seenIds` is built only from enabled skills, a locally
216
230
  // installed-but-disabled skill falls through to the catalog loop and gets
217
231
  // embedded as if it were a discoverable uninstalled skill — contradicting
218
232
  // the user's explicit disablement.
@@ -223,8 +237,6 @@ describe("seedV2SkillEntries", () => {
223
237
  { summary: enabledSkill, state: "enabled" },
224
238
  { summary: disabledSkill, state: "disabled" },
225
239
  ];
226
- // The remote catalog also contains the disabled skill (same id) — the
227
- // seed function must NOT pull it back in via `getCatalog()`.
228
240
  state.fullCatalog = [
229
241
  {
230
242
  id: "example-skill-b",
@@ -237,7 +249,7 @@ describe("seedV2SkillEntries", () => {
237
249
  await seedV2SkillEntries();
238
250
 
239
251
  expect(state.upsertCalls).toHaveLength(1);
240
- expect(state.upsertCalls[0].id).toBe("example-skill-a");
252
+ expect(state.upsertCalls[0].slug).toBe("skills/example-skill-a");
241
253
  });
242
254
 
243
255
  test("seeds genuinely uninstalled catalog skills alongside enabled installed skills", async () => {
@@ -263,8 +275,11 @@ describe("seedV2SkillEntries", () => {
263
275
 
264
276
  await seedV2SkillEntries();
265
277
 
266
- const ids = state.upsertCalls.map((c) => c.id).sort();
267
- expect(ids).toEqual(["example-skill-a", "uninstalled-skill"]);
278
+ const slugs = state.upsertCalls.map((c) => c.slug).sort();
279
+ expect(slugs).toEqual([
280
+ "skills/example-skill-a",
281
+ "skills/uninstalled-skill",
282
+ ]);
268
283
  });
269
284
 
270
285
  test("skips skills whose declared feature flag is disabled", async () => {
@@ -284,10 +299,10 @@ describe("seedV2SkillEntries", () => {
284
299
  await seedV2SkillEntries();
285
300
 
286
301
  expect(state.upsertCalls).toHaveLength(1);
287
- expect(state.upsertCalls[0].id).toBe("example-skill-b");
302
+ expect(state.upsertCalls[0].slug).toBe("skills/example-skill-b");
288
303
  });
289
304
 
290
- test("calls pruneSkillsExcept with the active id list", async () => {
305
+ test("calls pruneSlugsWithPrefixExcept with the active id list and the skills/ prefix", async () => {
291
306
  const skillA = makeSummary({ id: "example-skill-a" });
292
307
  const skillB = makeSummary({ id: "example-skill-b" });
293
308
  state.catalog = [skillA, skillB];
@@ -309,13 +324,14 @@ describe("seedV2SkillEntries", () => {
309
324
  await seedV2SkillEntries();
310
325
 
311
326
  expect(state.pruneCalls).toHaveLength(1);
312
- expect([...state.pruneCalls[0]].sort()).toEqual([
327
+ expect(state.pruneCalls[0].prefix).toBe("skills/");
328
+ expect([...state.pruneCalls[0].activeSuffixes].sort()).toEqual([
313
329
  "example-skill-a",
314
330
  "example-skill-b",
315
331
  ]);
316
332
  });
317
333
 
318
- test("passes only the active (post-flag-filter) ids to pruneSkillsExcept", async () => {
334
+ test("passes only the active (post-flag-filter) ids to pruneSlugsWithPrefixExcept", async () => {
319
335
  const flagged = makeSummary({
320
336
  id: "example-skill-a",
321
337
  featureFlag: "off-flag",
@@ -327,8 +343,6 @@ describe("seedV2SkillEntries", () => {
327
343
  { summary: unflagged, state: "enabled" },
328
344
  ];
329
345
  state.flagsEnabled = { "off-flag": false };
330
- // Remote catalog must be non-empty so catalogAvailable is true and
331
- // pruning is not skipped.
332
346
  state.fullCatalog = [
333
347
  { id: "example-skill-a", name: "example-skill-a", description: "A" },
334
348
  { id: "example-skill-b", name: "example-skill-b", description: "B" },
@@ -338,44 +352,35 @@ describe("seedV2SkillEntries", () => {
338
352
  await seedV2SkillEntries();
339
353
 
340
354
  expect(state.pruneCalls).toHaveLength(1);
341
- expect([...state.pruneCalls[0]]).toEqual(["example-skill-b"]);
355
+ expect(state.pruneCalls[0].prefix).toBe("skills/");
356
+ expect([...state.pruneCalls[0].activeSuffixes]).toEqual([
357
+ "example-skill-b",
358
+ ]);
342
359
  });
343
360
 
344
- test("populates the entries cache so getSkillCapability returns each entry", async () => {
361
+ test("populates the entries cache so getSkillCapability resolves both bare id and unified slug", async () => {
345
362
  const skillA = makeSummary({
346
363
  id: "example-skill-a",
347
364
  displayName: "Skill A",
348
365
  });
349
- const skillB = makeSummary({
350
- id: "example-skill-b",
351
- displayName: "Skill B",
352
- });
353
- state.catalog = [skillA, skillB];
354
- state.resolved = [
355
- { summary: skillA, state: "enabled" },
356
- { summary: skillB, state: "enabled" },
357
- ];
358
- state.embedReturn = [
359
- [0.1, 0.2, 0.3],
360
- [0.4, 0.5, 0.6],
361
- ];
366
+ state.catalog = [skillA];
367
+ state.resolved = [{ summary: skillA, state: "enabled" }];
368
+ state.embedReturn = [[0.1, 0.2, 0.3]];
362
369
 
363
370
  expect(getSkillCapability("example-skill-a")).toBeNull();
364
371
 
365
372
  await seedV2SkillEntries();
366
373
 
367
- const entryA = getSkillCapability("example-skill-a");
368
- const entryB = getSkillCapability("example-skill-b");
369
- expect(entryA).not.toBeNull();
370
- expect(entryA?.id).toBe("example-skill-a");
371
- expect(entryA?.content).toContain("Skill A");
372
-
373
- expect(entryB).not.toBeNull();
374
- expect(entryB?.id).toBe("example-skill-b");
375
- expect(entryB?.content).toContain("Skill B");
374
+ // Bare id and unified-slug forms both resolve to the same entry.
375
+ const byId = getSkillCapability("example-skill-a");
376
+ const bySlug = getSkillCapability("skills/example-skill-a");
377
+ expect(byId).not.toBeNull();
378
+ expect(byId?.id).toBe("example-skill-a");
379
+ expect(byId?.content).toContain("Skill A");
380
+ expect(bySlug).toEqual(byId);
376
381
 
377
- // Unknown ids return null even when the cache is populated.
378
382
  expect(getSkillCapability("unknown-skill")).toBeNull();
383
+ expect(getSkillCapability("skills/unknown-skill")).toBeNull();
379
384
  });
380
385
 
381
386
  test("swallows errors from embedWithBackend and leaves prior cache intact", async () => {
@@ -426,9 +431,10 @@ describe("seedV2SkillEntries", () => {
426
431
  await seedV2SkillEntries();
427
432
 
428
433
  expect(state.upsertCalls).toHaveLength(1);
429
- expect(state.upsertCalls[0].id).toBe("remote-only");
434
+ expect(state.upsertCalls[0].slug).toBe("skills/remote-only");
430
435
  expect(state.pruneCalls).toHaveLength(1);
431
- expect([...state.pruneCalls[0]]).toEqual(["remote-only"]);
436
+ expect(state.pruneCalls[0].prefix).toBe("skills/");
437
+ expect([...state.pruneCalls[0].activeSuffixes]).toEqual(["remote-only"]);
432
438
  });
433
439
 
434
440
  test("skips pruning when catalog fetch returns empty (network failure guard)", async () => {