@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
@@ -62,6 +62,7 @@ export interface ConceptPageQueryResult {
62
62
 
63
63
  let _client: QdrantRestClient | null = null;
64
64
  let _collectionReady = false;
65
+ let _collectionReadyPromise: Promise<void> | null = null;
65
66
 
66
67
  /** Lazily create a Qdrant REST client bound to the resolved URL. */
67
68
  function getClient(): QdrantRestClient {
@@ -85,7 +86,15 @@ function getClient(): QdrantRestClient {
85
86
  */
86
87
  export async function ensureConceptPageCollection(): Promise<void> {
87
88
  if (_collectionReady) return;
89
+ if (_collectionReadyPromise) return _collectionReadyPromise;
88
90
 
91
+ _collectionReadyPromise = ensureConceptPageCollectionOnce().finally(() => {
92
+ _collectionReadyPromise = null;
93
+ });
94
+ return _collectionReadyPromise;
95
+ }
96
+
97
+ async function ensureConceptPageCollectionOnce(): Promise<void> {
89
98
  const client = getClient();
90
99
  const config = getConfig();
91
100
  const vectorSize = config.memory.qdrant.vectorSize;
@@ -215,6 +224,95 @@ export async function deleteConceptPageEmbedding(slug: string): Promise<void> {
215
224
  }
216
225
  }
217
226
 
227
+ /**
228
+ * Remove every point whose slug starts with the given prefix and whose
229
+ * remaining suffix is not in `activeSuffixes`. Used by the skill-seed flow to
230
+ * drop stale `skills/<id>` slugs after a skill is uninstalled or disabled,
231
+ * since skills now share the concept-page collection rather than living in a
232
+ * dedicated one.
233
+ *
234
+ * Idempotent: when the live `<prefix>*` slugs already match `activeSuffixes`,
235
+ * the function performs a single scroll and no deletes.
236
+ */
237
+ export async function pruneSlugsWithPrefixExcept(
238
+ prefix: string,
239
+ activeSuffixes: readonly string[],
240
+ ): Promise<void> {
241
+ await ensureConceptPageCollection();
242
+
243
+ const client = getClient();
244
+ const activeSet = new Set(activeSuffixes);
245
+
246
+ const doPrune = async (): Promise<void> => {
247
+ const stalePointIds: Array<string | number> = [];
248
+ let offset: string | number | undefined = undefined;
249
+ const maxIterations = 10_000;
250
+ const batchSize = 256;
251
+ for (let i = 0; i < maxIterations; i++) {
252
+ const result = await client.scroll(MEMORY_V2_COLLECTION, {
253
+ limit: batchSize,
254
+ with_payload: true,
255
+ with_vector: false,
256
+ ...(offset !== undefined ? { offset } : {}),
257
+ });
258
+ for (const point of result.points) {
259
+ const slug = (point.payload as { slug?: unknown } | null)?.slug;
260
+ if (typeof slug !== "string") continue;
261
+ if (!slug.startsWith(prefix)) continue;
262
+ const suffix = slug.slice(prefix.length);
263
+ if (!activeSet.has(suffix)) {
264
+ stalePointIds.push(point.id);
265
+ }
266
+ }
267
+ const next = result.next_page_offset;
268
+ if (next == null) break;
269
+ offset = typeof next === "string" ? next : (next as number);
270
+ }
271
+
272
+ if (stalePointIds.length === 0) return;
273
+
274
+ await client.delete(MEMORY_V2_COLLECTION, {
275
+ wait: true,
276
+ points: stalePointIds,
277
+ });
278
+ };
279
+
280
+ try {
281
+ await doPrune();
282
+ } catch (err) {
283
+ if (isCollectionMissing(err)) {
284
+ _collectionReady = false;
285
+ await ensureConceptPageCollection();
286
+ await doPrune();
287
+ return;
288
+ }
289
+ throw err;
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Best-effort delete of the legacy `memory_v2_skills` Qdrant collection. Skill
295
+ * embeddings now live alongside concept pages in `memory_v2_concept_pages`
296
+ * under the `skills/<id>` slug prefix, so the dedicated collection is dead
297
+ * weight on installs upgraded from the split-collection era. Fire-and-forget:
298
+ * on a fresh install (collection never existed) or a transient Qdrant
299
+ * unavailable, we log and move on.
300
+ */
301
+ export async function dropLegacySkillsCollection(): Promise<void> {
302
+ try {
303
+ const client = getClient();
304
+ const exists = await client.collectionExists("memory_v2_skills");
305
+ if (!exists.exists) return;
306
+ await client.deleteCollection("memory_v2_skills");
307
+ log.info("Deleted legacy memory_v2_skills Qdrant collection");
308
+ } catch (err) {
309
+ log.warn(
310
+ { err },
311
+ "Failed to drop legacy memory_v2_skills collection — non-fatal",
312
+ );
313
+ }
314
+ }
315
+
218
316
  /**
219
317
  * Run separate dense and sparse queries against the concept-page collection
220
318
  * and return per-channel scores per slug. Callers fuse these — typically via
@@ -236,6 +334,7 @@ export async function hybridQueryConceptPages(
236
334
  sparse: SparseEmbedding,
237
335
  limit: number,
238
336
  restrictToSlugs?: readonly string[],
337
+ options?: { skipSparse?: boolean },
239
338
  ): Promise<ConceptPageQueryResult[]> {
240
339
  if (restrictToSlugs && restrictToSlugs.length === 0) {
241
340
  // An empty restriction means "no candidates"; skip the round-trip.
@@ -249,6 +348,13 @@ export async function hybridQueryConceptPages(
249
348
  ? { must: [{ key: "slug", match: { any: [...restrictToSlugs] } }] }
250
349
  : undefined;
251
350
 
351
+ // When the caller weighted sparse to zero, skip the round-trip entirely.
352
+ // The downstream fuser (`fuseHit` in `sim.ts`) already treats a missing
353
+ // sparse score as a 0 contribution, so omitting the query is a pure
354
+ // optimization — and it's also the kill switch operators use to dodge a
355
+ // Qdrant 1.13.x sparse-index crash that we've reproduced in the wild.
356
+ const skipSparse = options?.skipSparse ?? false;
357
+
252
358
  const denseQuery = () =>
253
359
  client.query(MEMORY_V2_COLLECTION, {
254
360
  query: dense,
@@ -267,7 +373,14 @@ export async function hybridQueryConceptPages(
267
373
  });
268
374
 
269
375
  // Run both queries concurrently — they hit independent named vectors.
270
- const runQueries = async () => Promise.all([denseQuery(), sparseQuery()]);
376
+ // When sparse is gated off we still resolve a Promise so the destructuring
377
+ // below stays uniform; the empty `points: []` matches the shape of a
378
+ // no-hit Qdrant response.
379
+ const emptyResult = {
380
+ points: [] as Array<{ payload?: unknown; score?: number }>,
381
+ };
382
+ const runQueries = async () =>
383
+ Promise.all([denseQuery(), skipSparse ? emptyResult : sparseQuery()]);
271
384
 
272
385
  let denseResults;
273
386
  let sparseResults;
@@ -305,6 +418,89 @@ export async function hybridQueryConceptPages(
305
418
  return Array.from(merged.values());
306
419
  }
307
420
 
421
+ /**
422
+ * Page through the v2 concept-page collection and return up to `maxSamples`
423
+ * stored dense vectors. Used by the anisotropy-fit pipeline to compute a
424
+ * corpus mean + top-k principal components without re-embedding every page.
425
+ *
426
+ * Sparse vectors are skipped — anisotropy is a dense-embedding phenomenon, and
427
+ * pulling the sparse side would just inflate the response. Payload is also
428
+ * skipped because the fit doesn't need slug identity.
429
+ *
430
+ * Returns an empty array when the collection is empty or missing. Caller
431
+ * decides what to do (typically: surface a "no vectors to fit" error).
432
+ */
433
+ export async function sampleConceptPageDenseVectors(
434
+ maxSamples: number,
435
+ ): Promise<number[][]> {
436
+ if (maxSamples <= 0) return [];
437
+ await ensureConceptPageCollection();
438
+
439
+ const client = getClient();
440
+ const out: number[][] = [];
441
+ let offset: string | number | undefined = undefined;
442
+ // Same pagination guard pattern as the rest of the file — bounds the loop
443
+ // even if Qdrant somehow keeps handing back a non-null offset.
444
+ const maxIterations = 10_000;
445
+ const batchSize = Math.min(256, maxSamples);
446
+
447
+ for (let i = 0; i < maxIterations; i++) {
448
+ if (out.length >= maxSamples) break;
449
+ const remaining = maxSamples - out.length;
450
+ let result;
451
+ try {
452
+ result = await client.scroll(MEMORY_V2_COLLECTION, {
453
+ limit: Math.min(batchSize, remaining),
454
+ with_payload: false,
455
+ // Fetch only the dense named vector — sparse is irrelevant for
456
+ // anisotropy correction.
457
+ with_vector: ["dense"],
458
+ ...(offset !== undefined ? { offset } : {}),
459
+ });
460
+ } catch (err) {
461
+ if (isCollectionMissing(err)) {
462
+ _collectionReady = false;
463
+ return out;
464
+ }
465
+ throw err;
466
+ }
467
+
468
+ for (const point of result.points) {
469
+ const v = extractDenseVector(point.vector);
470
+ if (v) out.push(v);
471
+ if (out.length >= maxSamples) break;
472
+ }
473
+
474
+ const next = result.next_page_offset;
475
+ if (next == null) break;
476
+ offset = typeof next === "string" ? next : (next as number);
477
+ }
478
+
479
+ return out;
480
+ }
481
+
482
+ /**
483
+ * Pull the `dense` named-vector payload out of a Qdrant point. Defensively
484
+ * handles both the named-vector shape (`{ dense: [...] }`) and the legacy
485
+ * unnamed-vector shape (`number[]`) so older collection layouts don't trip
486
+ * the sampler. Returns `null` for shapes we don't recognise.
487
+ */
488
+ function extractDenseVector(vector: unknown): number[] | null {
489
+ if (Array.isArray(vector)) {
490
+ if (vector.every((n) => typeof n === "number")) {
491
+ return vector as number[];
492
+ }
493
+ return null;
494
+ }
495
+ if (vector && typeof vector === "object") {
496
+ const dense = (vector as { dense?: unknown }).dense;
497
+ if (Array.isArray(dense) && dense.every((n) => typeof n === "number")) {
498
+ return dense as number[];
499
+ }
500
+ }
501
+ return null;
502
+ }
503
+
308
504
  /**
309
505
  * Detect "collection not found" errors so callers can reset readiness and
310
506
  * retry after an external deletion (e.g. workspace reset).
@@ -339,4 +535,5 @@ function pointIdForSlug(slug: string): string {
339
535
  export function _resetMemoryV2QdrantForTests(): void {
340
536
  _client = null;
341
537
  _collectionReady = false;
538
+ _collectionReadyPromise = null;
342
539
  }
@@ -0,0 +1,177 @@
1
+ /** Memory v2 cross-encoder rerank — `(query, page-preview)` pairs scored by a local model. */
2
+
3
+ import { createHash } from "node:crypto";
4
+
5
+ import type { AssistantConfig } from "../../config/types.js";
6
+ import { getLogger } from "../../util/logger.js";
7
+ import { getWorkspaceDir } from "../../util/platform.js";
8
+ import { getOrCreateRerankBackend } from "../rerank-local.js";
9
+ import { readPage } from "./page-store.js";
10
+
11
+ const log = getLogger("memory-v2-reranker");
12
+
13
+ // ~512-token model context for bge-reranker-base; cap input to bound payload.
14
+ const PASSAGE_CHAR_CAP = 240;
15
+
16
+ interface CacheEntry {
17
+ scores: Map<string, number>;
18
+ ts: number;
19
+ }
20
+
21
+ const CACHE_TTL_MS = 2 * 60 * 1000;
22
+ const CACHE_MAX_ENTRIES = 64;
23
+ const cache = new Map<string, CacheEntry>();
24
+
25
+ function cacheKey(query: string, slugs: readonly string[]): string {
26
+ const sorted = [...slugs].sort().join("\0");
27
+ return createHash("sha256").update(`${query}\0${sorted}`).digest("hex");
28
+ }
29
+
30
+ function evictExpired(now: number): void {
31
+ for (const [k, v] of cache) {
32
+ if (now - v.ts > CACHE_TTL_MS) cache.delete(k);
33
+ }
34
+ if (cache.size > CACHE_MAX_ENTRIES) {
35
+ const toDrop = cache.size - CACHE_MAX_ENTRIES;
36
+ let i = 0;
37
+ for (const k of cache.keys()) {
38
+ if (i++ >= toDrop) break;
39
+ cache.delete(k);
40
+ }
41
+ }
42
+ }
43
+
44
+ function buildPassage(slug: string, body: string): string {
45
+ const trimmed = body.replace(/^\s+/, "");
46
+ const blank = trimmed.search(/\n\s*\n/);
47
+ const para = blank === -1 ? trimmed : trimmed.slice(0, blank);
48
+ const stripped = para.replace(/^#+\s.*\n/, "").trim();
49
+ const compact = stripped.replace(/\s+/g, " ").slice(0, PASSAGE_CHAR_CAP);
50
+ return `${slug}\n${compact}`;
51
+ }
52
+
53
+ /**
54
+ * Run the cross-encoder over each candidate's first-paragraph preview for
55
+ * one or more queries against the same candidate set. Returns one
56
+ * `Map<slug, score>` per query, in the same order as the `queries` array.
57
+ *
58
+ * Multi-query batching: the user-channel and assistant-channel queries share
59
+ * a candidate set per turn, so scoring them in a single tokenizer +
60
+ * forward-pass call avoids the ONNX-invocation overhead of two serialised
61
+ * worker round-trips. Cache hits short-circuit per-query independently —
62
+ * a whitespace-only query yields an empty Map without hitting the backend.
63
+ *
64
+ * Failures (worker down, page read errors) yield empty Maps so callers can
65
+ * fall back to pure fused scores. Per-batch normalisation and boost math
66
+ * live in `computeOwnActivation`.
67
+ */
68
+ export async function rerankCandidates(
69
+ queries: readonly string[],
70
+ candidates: readonly string[],
71
+ config: AssistantConfig,
72
+ ): Promise<Array<Map<string, number>>> {
73
+ if (queries.length === 0) return [];
74
+ if (candidates.length === 0) return queries.map(() => new Map());
75
+
76
+ const now = Date.now();
77
+ evictExpired(now);
78
+
79
+ const results: Array<Map<string, number> | null> = queries.map(() => null);
80
+ const uncachedIndices: number[] = [];
81
+ for (let i = 0; i < queries.length; i++) {
82
+ const q = queries[i];
83
+ if (q.trim().length === 0) {
84
+ results[i] = new Map();
85
+ continue;
86
+ }
87
+ const key = cacheKey(q, candidates);
88
+ const cached = cache.get(key);
89
+ if (cached) {
90
+ // Refresh insertion order so frequently-hit entries survive eviction.
91
+ cache.delete(key);
92
+ cache.set(key, { ...cached, ts: now });
93
+ results[i] = new Map(cached.scores);
94
+ } else {
95
+ uncachedIndices.push(i);
96
+ }
97
+ }
98
+
99
+ const finalize = (): Array<Map<string, number>> =>
100
+ results.map((r) => r ?? new Map());
101
+
102
+ if (uncachedIndices.length === 0) return finalize();
103
+
104
+ const workspaceDir = getWorkspaceDir();
105
+ const pages = await Promise.all(
106
+ candidates.map((slug) =>
107
+ readPage(workspaceDir, slug).catch((err) => {
108
+ log.debug({ err, slug }, "Reranker skipping page that failed to load");
109
+ return null;
110
+ }),
111
+ ),
112
+ );
113
+ const passages: string[] = [];
114
+ const slugsForPassages: string[] = [];
115
+ for (let i = 0; i < candidates.length; i++) {
116
+ const page = pages[i];
117
+ if (!page) continue;
118
+ passages.push(buildPassage(candidates[i], page.body));
119
+ slugsForPassages.push(candidates[i]);
120
+ }
121
+
122
+ if (passages.length === 0) {
123
+ for (const i of uncachedIndices) results[i] = new Map();
124
+ return finalize();
125
+ }
126
+
127
+ // One tokenizer + ONNX forward pass over every uncached query × passage
128
+ // pair. Pairs are laid out query-major: queries[uncached[0]] × passages,
129
+ // then queries[uncached[1]] × passages, etc.
130
+ const batchQueries: string[] = [];
131
+ const batchPassages: string[] = [];
132
+ for (const qi of uncachedIndices) {
133
+ const q = queries[qi];
134
+ for (const p of passages) {
135
+ batchQueries.push(q);
136
+ batchPassages.push(p);
137
+ }
138
+ }
139
+
140
+ const { model, dtype } = config.memory.v2.rerank;
141
+ let scores: number[];
142
+ try {
143
+ const backend = getOrCreateRerankBackend(model, dtype);
144
+ scores = await backend.score(batchQueries, batchPassages);
145
+ } catch (err) {
146
+ log.warn(
147
+ { err, model, n: batchPassages.length },
148
+ "Rerank backend failed; falling back to pure fused scores",
149
+ );
150
+ for (const i of uncachedIndices) results[i] = new Map();
151
+ return finalize();
152
+ }
153
+
154
+ for (let j = 0; j < uncachedIndices.length; j++) {
155
+ const qi = uncachedIndices[j];
156
+ const offset = j * passages.length;
157
+ const result = new Map<string, number>();
158
+ for (let i = 0; i < slugsForPassages.length; i++) {
159
+ const s = scores[offset + i];
160
+ if (typeof s !== "number" || Number.isNaN(s)) continue;
161
+ // sigmoid output should already be in [0, 1]; clamp defensively.
162
+ result.set(slugsForPassages[i], Math.max(0, Math.min(1, s)));
163
+ }
164
+ results[qi] = result;
165
+ cache.set(cacheKey(queries[qi], candidates), {
166
+ scores: new Map(result),
167
+ ts: now,
168
+ });
169
+ }
170
+
171
+ return finalize();
172
+ }
173
+
174
+ /** @internal Test-only: clear the LRU cache. */
175
+ export function _resetRerankCacheForTests(): void {
176
+ cache.clear();
177
+ }
@@ -26,13 +26,11 @@
26
26
  // only as a per-turn ordering signal, not compared across turns.
27
27
 
28
28
  import type { AssistantConfig } from "../../config/types.js";
29
- import {
30
- embedWithBackend,
31
- generateSparseEmbedding,
32
- } from "../embedding-backend.js";
29
+ import { applyCorrectionIfCalibrated } from "../anisotropy.js";
30
+ import { embedWithBackend } from "../embedding-backend.js";
33
31
  import { clampUnitInterval } from "../validation.js";
34
32
  import { hybridQueryConceptPages } from "./qdrant.js";
35
- import { hybridQuerySkills } from "./skill-qdrant.js";
33
+ import { generateBm25QueryEmbedding } from "./sparse-bm25.js";
36
34
 
37
35
  /**
38
36
  * Clamp a value into the closed unit interval [0, 1]. Re-exported under the
@@ -40,6 +38,79 @@ import { hybridQuerySkills } from "./skill-qdrant.js";
40
38
  */
41
39
  export const clamp01 = clampUnitInterval;
42
40
 
41
+ /**
42
+ * Built-in defaults for adaptive sparse weighting. Live here (not in the
43
+ * config schema) so operators don't see two new knobs in their config until
44
+ * they actually want to tune them.
45
+ *
46
+ * Below `MIN_SPREAD`, the sparse channel is treated as no-signal (its scores
47
+ * are uniform across the candidate set, so it can't rank anything) and the
48
+ * sparse weight collapses to 0. At or above `FULL_SPREAD`, sparse weight
49
+ * stays at its configured value. Linear interpolation between.
50
+ */
51
+ const ADAPTIVE_SPARSE_MIN_SPREAD = 0.2;
52
+ const ADAPTIVE_SPARSE_FULL_SPREAD = 0.5;
53
+
54
+ /**
55
+ * Per-query effective dense + sparse weights, derived from the configured
56
+ * base weights and the spread of normalized sparse scores across the hit
57
+ * set. When the sparse channel can't discriminate (low spread or fewer
58
+ * than two sparse-bearing candidates), its weight collapses and dense
59
+ * weight is boosted to compensate so `dense + sparse` still equals
60
+ * `baseDense + baseSparse` and `fused` stays interpretable as a [0, 1]
61
+ * similarity.
62
+ *
63
+ * Pure function — exported so the diagnostic surface in
64
+ * `memory-v2-routes.explain-similarity` can show the effective weights and
65
+ * the measured spread alongside per-channel score statistics.
66
+ */
67
+ export function effectiveWeights(
68
+ hits: ReadonlyArray<{ sparseScore?: number }>,
69
+ maxSparse: number,
70
+ baseDense: number,
71
+ baseSparse: number,
72
+ config: AssistantConfig,
73
+ ): { dense: number; sparse: number; spread: number } {
74
+ // Short-circuit when the channel is already disabled or unscored. Returning
75
+ // base weights here keeps `fused` numerically identical to today's output
76
+ // for the no-sparse-signal cases the existing tests assume.
77
+ if (baseSparse === 0 || maxSparse === 0) {
78
+ return { dense: baseDense, sparse: baseSparse, spread: 0 };
79
+ }
80
+ let min = Infinity;
81
+ let max = -Infinity;
82
+ let count = 0;
83
+ for (const h of hits) {
84
+ if (h.sparseScore === undefined) continue;
85
+ const norm = h.sparseScore / maxSparse;
86
+ if (norm < min) min = norm;
87
+ if (norm > max) max = norm;
88
+ count++;
89
+ }
90
+ // With < 2 sparse-bearing hits the spread is undefined — fall back to base
91
+ // weights so single-hit retrievals still surface their sparse contribution
92
+ // (and the existing fusion-math tests stay green).
93
+ if (count < 2) {
94
+ return { dense: baseDense, sparse: baseSparse, spread: 0 };
95
+ }
96
+ const spread = max - min;
97
+
98
+ const minSpread =
99
+ config.memory.v2.min_sparse_spread ?? ADAPTIVE_SPARSE_MIN_SPREAD;
100
+ const fullSpread =
101
+ config.memory.v2.full_sparse_spread ?? ADAPTIVE_SPARSE_FULL_SPREAD;
102
+ // Degenerate config (full <= min): no interpolation range. Don't try to
103
+ // adapt; trust the operator's base weights and report the measured spread
104
+ // for diagnostics.
105
+ if (fullSpread <= minSpread) {
106
+ return { dense: baseDense, sparse: baseSparse, spread };
107
+ }
108
+ const factor = clamp01((spread - minSpread) / (fullSpread - minSpread));
109
+ const sparse = baseSparse * factor;
110
+ const dense = baseDense + (baseSparse - sparse);
111
+ return { dense, sparse, spread };
112
+ }
113
+
43
114
  /**
44
115
  * Compute hybrid (dense + sparse) similarity scores between a query text and
45
116
  * a fixed set of candidate concept-page slugs.
@@ -63,23 +134,41 @@ export const clamp01 = clampUnitInterval;
63
134
  * Edge cases:
64
135
  * - Empty `candidateSlugs` → returns an empty map without touching Qdrant
65
136
  * or the embedding backend.
66
- * - Empty query text or all-zero sparse vector still queries (dense may
67
- * still hit), and the sparse contribution to fusion is zero.
137
+ * - Empty / whitespace-only `text` returns an empty map without touching
138
+ * Qdrant or the embedding backend. The Gemini embedding API rejects empty
139
+ * content with HTTP 400, and short-circuiting here prevents the failure
140
+ * from cascading through `Promise.all` in `computeOwnActivation` (e.g.
141
+ * turn 1 has no prior assistant message, so its `simBatch` channel is
142
+ * called with `""`). Treating the channel's contribution as 0 is the
143
+ * same outcome a no-hit query would produce.
68
144
  */
69
145
  export async function simBatch(
70
146
  text: string,
71
147
  candidateSlugs: readonly string[],
72
148
  config: AssistantConfig,
149
+ options?: { signal?: AbortSignal },
73
150
  ): Promise<Map<string, number>> {
74
151
  if (candidateSlugs.length === 0) {
75
152
  return new Map();
76
153
  }
154
+ if (text.trim().length === 0) {
155
+ return new Map();
156
+ }
77
157
 
78
- // Sparse uses the shared TF-IDF encoder so the query and stored vectors
79
- // share a vocabulary with PKB indexing.
80
- const denseResult = await embedWithBackend(config, [text]);
81
- const denseVector = denseResult.vectors[0];
82
- const sparseVector = generateSparseEmbedding(text);
158
+ // Sparse uses BM25: the query side encodes binary occurrences per token,
159
+ // and the stored doc vectors carry the IDF · TF-saturated weights — Qdrant
160
+ // dot product then yields the BM25 score directly.
161
+ throwIfAborted(options?.signal);
162
+ const denseResult = await embedWithBackend(config, [text], {
163
+ signal: options?.signal,
164
+ });
165
+ const denseVector = await applyCorrectionIfCalibrated(
166
+ denseResult.vectors[0],
167
+ denseResult.provider,
168
+ denseResult.model,
169
+ );
170
+ throwIfAborted(options?.signal);
171
+ const sparseVector = generateBm25QueryEmbedding(text);
83
172
 
84
173
  const hits = await hybridQueryConceptPages(
85
174
  denseVector,
@@ -93,81 +182,28 @@ export async function simBatch(
93
182
  }
94
183
 
95
184
  const maxSparse = computeMaxSparse(hits);
96
- const { dense_weight: denseWeight, sparse_weight: sparseWeight } =
185
+ const { dense_weight: baseDense, sparse_weight: baseSparse } =
97
186
  config.memory.v2;
187
+ const { dense: denseWeight, sparse: sparseWeight } = effectiveWeights(
188
+ hits,
189
+ maxSparse,
190
+ baseDense,
191
+ baseSparse,
192
+ config,
193
+ );
98
194
 
99
195
  const scores = new Map<string, number>();
100
196
  for (const hit of hits) {
101
197
  scores.set(hit.slug, fuseHit(hit, maxSparse, denseWeight, sparseWeight));
102
198
  }
199
+
103
200
  return scores;
104
201
  }
105
202
 
106
- /**
107
- * Compute hybrid (dense + sparse) similarity scores between a query text and
108
- * a fixed set of candidate skill ids. Mirrors `simBatch` but targets the
109
- * dedicated `memory_v2_skills` Qdrant collection via `hybridQuerySkills`.
110
- *
111
- * Differences from `simBatch`:
112
- * - Keys are skill `id` values (not concept-page slugs).
113
- * - Restricts the query to the caller's candidate ids server-side via
114
- * `hybridQuerySkills`'s `restrictToIds` parameter. Without this, when the
115
- * skills collection has more skills than `ids.length`, Qdrant would
116
- * return its global top-K and candidate ids absent from that top-K would
117
- * silently score 0 — corrupting the activation calculation.
118
- *
119
- * Returns a `Map<id, score>` of fused scores in [0, 1]. Ids that did not hit
120
- * either channel are absent from the map.
121
- *
122
- * Edge cases:
123
- * - Empty `ids` → returns an empty map without touching Qdrant or the
124
- * embedding backend.
125
- * - Empty query text → still queries (dense may still hit), and the sparse
126
- * contribution is zero.
127
- */
128
- export async function simSkillBatch(
129
- text: string,
130
- ids: readonly string[],
131
- config: AssistantConfig,
132
- ): Promise<Map<string, number>> {
133
- if (ids.length === 0) {
134
- return new Map();
135
- }
136
-
137
- const denseResult = await embedWithBackend(config, [text]);
138
- const denseVector = denseResult.vectors[0];
139
- const sparseVector = generateSparseEmbedding(text);
140
-
141
- const hits = await hybridQuerySkills(
142
- denseVector,
143
- sparseVector,
144
- ids.length,
145
- ids,
146
- );
147
-
148
- if (hits.length === 0) {
149
- return new Map();
150
- }
151
-
152
- // Defensive post-filter — `hybridQuerySkills` restricts server-side, so
153
- // every hit should already be in `ids`, but keep this guard so a buggy
154
- // payload (e.g. a missing/typoed id index) can't silently inject
155
- // out-of-set ids into the score map.
156
- const idSet = new Set(ids);
157
- const filtered = hits.filter((h) => idSet.has(h.id));
158
- if (filtered.length === 0) {
159
- return new Map();
203
+ function throwIfAborted(signal: AbortSignal | undefined): void {
204
+ if (signal?.aborted) {
205
+ throw new DOMException("Aborted", "AbortError");
160
206
  }
161
-
162
- const maxSparse = computeMaxSparse(filtered);
163
- const { dense_weight: denseWeight, sparse_weight: sparseWeight } =
164
- config.memory.v2;
165
-
166
- const scores = new Map<string, number>();
167
- for (const hit of filtered) {
168
- scores.set(hit.id, fuseHit(hit, maxSparse, denseWeight, sparseWeight));
169
- }
170
- return scores;
171
207
  }
172
208
 
173
209
  /**
@@ -2,9 +2,10 @@ import { getConfig } from "../../config/loader.js";
2
2
  import type { SkillCapabilityInput } from "../../skills/skill-memory.js";
3
3
 
4
4
  /**
5
- * Render the prose-style capability statement embedded into the
6
- * `memory_v2_skills` Qdrant collection and rendered in
7
- * `### Skills You Can Use`. Capped at 500 chars to match v1's behavior.
5
+ * Render the prose-style capability statement embedded into the unified
6
+ * `memory_v2_concept_pages` Qdrant collection (under the `skills/<id>` slug
7
+ * prefix) and rendered in `### Skills You Can Use`. Capped at 500 chars to
8
+ * match v1's behavior.
8
9
  */
9
10
  export function buildSkillContent(input: SkillCapabilityInput): string {
10
11
  let content = `The "${input.displayName}" skill (${input.id}) is available. ${input.description}.`;