@vellumai/assistant 0.7.1 → 0.7.2

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 (535) hide show
  1. package/ARCHITECTURE.md +32 -49
  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/security.md +20 -0
  7. package/docs/plugins.md +7 -9
  8. package/knip.json +1 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
  11. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  12. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  13. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  15. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  16. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  17. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
  19. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  20. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  21. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  22. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  23. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  24. package/openapi.yaml +565 -12
  25. package/package.json +6 -3
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  27. package/src/__tests__/app-bundler.test.ts +170 -1
  28. package/src/__tests__/app-control-flow.test.ts +374 -0
  29. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  30. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  31. package/src/__tests__/app-executors.test.ts +30 -43
  32. package/src/__tests__/approval-routes-http.test.ts +23 -6
  33. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  34. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  35. package/src/__tests__/assistant-event-hub.test.ts +109 -2
  36. package/src/__tests__/assistant-event.test.ts +10 -0
  37. package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
  38. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
  39. package/src/__tests__/background-shell-host-bash.test.ts +14 -15
  40. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  41. package/src/__tests__/btw-routes.test.ts +13 -4
  42. package/src/__tests__/call-controller.test.ts +49 -1
  43. package/src/__tests__/call-domain.test.ts +0 -2
  44. package/src/__tests__/call-routes-http.test.ts +0 -2
  45. package/src/__tests__/channel-readiness-service.test.ts +59 -1
  46. package/src/__tests__/checker.test.ts +3 -4
  47. package/src/__tests__/config-loader-backfill.test.ts +90 -155
  48. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  49. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  50. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  51. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  54. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  55. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  56. package/src/__tests__/conversation-lifecycle.test.ts +36 -0
  57. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  58. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
  59. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
  60. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  61. package/src/__tests__/conversation-slash-commands.test.ts +0 -4
  62. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
  64. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  65. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  66. package/src/__tests__/credentials-cli.test.ts +5 -12
  67. package/src/__tests__/cu-unified-flow.test.ts +185 -23
  68. package/src/__tests__/daemon-credential-client.test.ts +101 -19
  69. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  70. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  71. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  72. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  73. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  74. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
  75. package/src/__tests__/heartbeat-service.test.ts +718 -1
  76. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  77. package/src/__tests__/host-app-control-proxy.test.ts +602 -0
  78. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  79. package/src/__tests__/host-bash-proxy.test.ts +246 -47
  80. package/src/__tests__/host-bash-routes.test.ts +294 -0
  81. package/src/__tests__/host-browser-proxy.test.ts +24 -22
  82. package/src/__tests__/host-browser-routes.test.ts +39 -13
  83. package/src/__tests__/host-cu-proxy.test.ts +41 -52
  84. package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
  85. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  86. package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
  87. package/src/__tests__/host-file-proxy.test.ts +37 -43
  88. package/src/__tests__/host-file-read-tool.test.ts +17 -0
  89. package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
  90. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  91. package/src/__tests__/host-proxy-base.test.ts +312 -0
  92. package/src/__tests__/host-shell-tool.test.ts +22 -4
  93. package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
  94. package/src/__tests__/host-transfer-proxy.test.ts +121 -22
  95. package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
  96. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  97. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  98. package/src/__tests__/identity-routes.test.ts +103 -1
  99. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  100. package/src/__tests__/inline-command-runner.test.ts +0 -1
  101. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
  102. package/src/__tests__/integration-status.test.ts +85 -5
  103. package/src/__tests__/intent-routing.test.ts +0 -1
  104. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  105. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  106. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  107. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  108. package/src/__tests__/mcp-cli.test.ts +338 -2
  109. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  110. package/src/__tests__/migration-import-commit-http.test.ts +108 -2
  111. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  112. package/src/__tests__/oauth-cli.test.ts +0 -2
  113. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  114. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  115. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
  116. package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
  117. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  118. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  119. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  120. package/src/__tests__/require-fresh-approval.test.ts +0 -1
  121. package/src/__tests__/retry-backoff.test.ts +87 -0
  122. package/src/__tests__/runtime-events-sse.test.ts +10 -6
  123. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  124. package/src/__tests__/schedule-retry.test.ts +715 -0
  125. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  126. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  127. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  128. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  129. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  130. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  131. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  132. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  133. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  134. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  135. package/src/__tests__/slack-channel-config.test.ts +9 -14
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  137. package/src/__tests__/system-prompt.test.ts +0 -1
  138. package/src/__tests__/telegram-config.test.ts +0 -1
  139. package/src/__tests__/test-preload.ts +8 -0
  140. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  141. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  142. package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
  143. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  145. package/src/__tests__/tool-executor.test.ts +0 -1
  146. package/src/__tests__/twilio-config.test.ts +3 -16
  147. package/src/__tests__/twilio-routes.test.ts +3 -5
  148. package/src/__tests__/twilio-validation.test.ts +93 -0
  149. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
  150. package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
  151. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  152. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  153. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  154. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  155. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  156. package/src/backup/__tests__/paths.test.ts +0 -22
  157. package/src/backup/__tests__/restore.test.ts +51 -151
  158. package/src/backup/paths.ts +2 -18
  159. package/src/backup/restore.ts +107 -231
  160. package/src/bundler/app-bundler.ts +51 -3
  161. package/src/calls/relay-server.ts +4 -44
  162. package/src/calls/twilio-config.ts +2 -17
  163. package/src/calls/twilio-rest.ts +33 -105
  164. package/src/calls/twilio-routes.ts +11 -12
  165. package/src/channels/types.ts +8 -7
  166. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  167. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  168. package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
  169. package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
  170. package/src/cli/commands/backup.ts +6 -331
  171. package/src/cli/commands/clients.ts +36 -37
  172. package/src/cli/commands/contacts.ts +73 -0
  173. package/src/cli/commands/conversations.ts +2 -5
  174. package/src/cli/commands/credentials.ts +15 -7
  175. package/src/cli/commands/domain.ts +66 -15
  176. package/src/cli/commands/gateway.ts +183 -0
  177. package/src/cli/commands/keys.ts +9 -6
  178. package/src/cli/commands/mcp.ts +116 -156
  179. package/src/cli/commands/memory-v2.ts +296 -1
  180. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  181. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
  182. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
  183. package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
  184. package/src/cli/commands/platform/disconnect.ts +5 -4
  185. package/src/cli/commands/platform/index.ts +0 -18
  186. package/src/cli/lib/daemon-credential-client.ts +110 -28
  187. package/src/cli/program.ts +2 -0
  188. package/src/config/assistant-feature-flags.ts +67 -10
  189. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  190. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  191. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  192. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  193. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  194. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  195. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  196. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  197. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  198. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  199. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  200. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  201. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  202. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  203. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  204. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  205. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  206. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  207. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  208. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  209. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  210. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  211. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  212. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  213. package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
  214. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  215. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
  216. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  217. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  218. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  219. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  220. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  221. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  222. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  223. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  224. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  225. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  226. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  227. package/src/config/bundled-tool-registry.ts +21 -0
  228. package/src/config/env-registry.ts +0 -2
  229. package/src/config/env.ts +19 -12
  230. package/src/config/feature-flag-registry.json +21 -133
  231. package/src/config/loader.ts +73 -99
  232. package/src/config/sanitize-for-transfer.ts +2 -0
  233. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  234. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
  235. package/src/config/schemas/calls.ts +0 -9
  236. package/src/config/schemas/heartbeat.ts +63 -0
  237. package/src/config/schemas/ingress.ts +10 -6
  238. package/src/config/schemas/llm.ts +5 -10
  239. package/src/config/schemas/memory-lifecycle.ts +77 -24
  240. package/src/config/schemas/memory-v2.ts +48 -4
  241. package/src/config/schemas/platform.ts +6 -0
  242. package/src/config/schemas/services.ts +1 -15
  243. package/src/config/schemas/skills.ts +0 -6
  244. package/src/config/seed-inference-profiles.ts +1 -1
  245. package/src/contacts/contact-store.ts +0 -30
  246. package/src/contacts/contacts-write.ts +0 -27
  247. package/src/context/window-manager.ts +1 -2
  248. package/src/credential-execution/feature-gates.ts +10 -10
  249. package/src/credential-execution/process-manager.ts +12 -41
  250. package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
  251. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  252. package/src/daemon/config-watcher.ts +4 -3
  253. package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
  254. package/src/daemon/conversation-agent-loop.ts +32 -28
  255. package/src/daemon/conversation-lifecycle.ts +8 -1
  256. package/src/daemon/conversation-process.ts +16 -11
  257. package/src/daemon/conversation-runtime-assembly.ts +2 -2
  258. package/src/daemon/conversation-surfaces.ts +125 -4
  259. package/src/daemon/conversation-tool-setup.ts +16 -55
  260. package/src/daemon/conversation.ts +21 -2
  261. package/src/daemon/doordash-steps.ts +1 -1
  262. package/src/daemon/handlers/shared.ts +4 -1
  263. package/src/daemon/host-app-control-proxy.ts +293 -0
  264. package/src/daemon/host-bash-proxy.ts +84 -74
  265. package/src/daemon/host-browser-proxy.ts +67 -82
  266. package/src/daemon/host-cu-proxy.ts +81 -86
  267. package/src/daemon/host-file-proxy.ts +93 -69
  268. package/src/daemon/host-proxy-base.ts +294 -0
  269. package/src/daemon/host-proxy-preactivation.ts +82 -0
  270. package/src/daemon/host-transfer-proxy.ts +247 -129
  271. package/src/daemon/lifecycle.ts +115 -117
  272. package/src/daemon/message-protocol.ts +3 -8
  273. package/src/daemon/message-types/contacts.ts +23 -1
  274. package/src/daemon/message-types/conversations.ts +11 -8
  275. package/src/daemon/message-types/host-app-control.ts +150 -0
  276. package/src/daemon/message-types/host-bash.ts +4 -0
  277. package/src/daemon/message-types/host-cu.ts +2 -0
  278. package/src/daemon/message-types/host-file.ts +4 -0
  279. package/src/daemon/message-types/host-transfer.ts +3 -0
  280. package/src/daemon/message-types/schedules.ts +8 -3
  281. package/src/daemon/message-types/skills.ts +2 -2
  282. package/src/daemon/process-message.ts +18 -1
  283. package/src/daemon/shutdown-handlers.ts +0 -3
  284. package/src/daemon/tool-setup-types.ts +51 -0
  285. package/src/daemon/tool-side-effects.ts +1 -1
  286. package/src/events/tool-audit-listener.ts +2 -1
  287. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
  288. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
  289. package/src/heartbeat/heartbeat-run-store.ts +236 -0
  290. package/src/heartbeat/heartbeat-service.ts +280 -49
  291. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  292. package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
  293. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  294. package/src/home/post-connect-feed.ts +68 -0
  295. package/src/home/relationship-state-writer.ts +17 -92
  296. package/src/home/suggested-prompts.ts +46 -10
  297. package/src/inbound/public-ingress-urls.ts +32 -34
  298. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  299. package/src/ipc/assistant-server.ts +14 -1
  300. package/src/ipc/cli-client.ts +32 -1
  301. package/src/live-voice/live-voice-metrics.ts +10 -10
  302. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  303. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  304. package/src/mcp/mcp-auth-state.ts +133 -0
  305. package/src/mcp/mcp-oauth-provider.ts +19 -0
  306. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  307. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  308. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  309. package/src/memory/anisotropy.test.ts +247 -0
  310. package/src/memory/anisotropy.ts +443 -0
  311. package/src/memory/auto-analysis-constants.ts +17 -0
  312. package/src/memory/auto-analysis-guard.ts +5 -15
  313. package/src/memory/canonical-guardian-store.ts +7 -7
  314. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  315. package/src/memory/context-search/agent-protocol.ts +6 -6
  316. package/src/memory/context-search/agent-runner.ts +32 -7
  317. package/src/memory/context-search/sources/memory-v2.ts +17 -5
  318. package/src/memory/conversation-crud.ts +1 -1
  319. package/src/memory/conversation-key-store.ts +2 -15
  320. package/src/memory/db-init.ts +4 -0
  321. package/src/memory/embedding-backend.ts +9 -21
  322. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
  323. package/src/memory/graph/conversation-graph-memory.ts +1 -24
  324. package/src/memory/graph/graph-search.ts +8 -0
  325. package/src/memory/graph/retriever.ts +28 -0
  326. package/src/memory/graph/tools.ts +1 -1
  327. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  328. package/src/memory/jobs/embed-concept-page.ts +28 -2
  329. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  330. package/src/memory/jobs-store.ts +66 -22
  331. package/src/memory/jobs-worker.ts +112 -63
  332. package/src/memory/memory-v2-activation-log-store.ts +1 -1
  333. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  334. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  335. package/src/memory/migrations/index.ts +5 -0
  336. package/src/memory/migrations/registry.ts +8 -0
  337. package/src/memory/pkb/pkb-search.ts +7 -0
  338. package/src/memory/qdrant-client.ts +50 -20
  339. package/src/memory/schema/infrastructure.ts +15 -0
  340. package/src/memory/search/semantic.ts +7 -0
  341. package/src/memory/sparse-tokenize.ts +49 -0
  342. package/src/memory/v2/__tests__/activation.test.ts +77 -95
  343. package/src/memory/v2/__tests__/injection.test.ts +43 -21
  344. package/src/memory/v2/__tests__/sim.test.ts +166 -6
  345. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  346. package/src/memory/v2/__tests__/static-context.test.ts +0 -1
  347. package/src/memory/v2/activation.ts +69 -88
  348. package/src/memory/v2/consolidation-job.ts +3 -5
  349. package/src/memory/v2/constants.ts +7 -0
  350. package/src/memory/v2/injection.ts +86 -53
  351. package/src/memory/v2/prompts/consolidation.ts +312 -91
  352. package/src/memory/v2/qdrant.ts +99 -1
  353. package/src/memory/v2/sim.ts +126 -16
  354. package/src/memory/v2/skill-qdrant.ts +12 -3
  355. package/src/memory/v2/skill-store.ts +16 -1
  356. package/src/memory/v2/sparse-bm25.ts +245 -0
  357. package/src/memory/v2/static-context.ts +6 -5
  358. package/src/messaging/providers/gmail/types.ts +0 -49
  359. package/src/messaging/providers/slack/adapter.ts +1 -31
  360. package/src/messaging/providers/slack/types.ts +0 -32
  361. package/src/notifications/README.md +10 -10
  362. package/src/notifications/broadcaster.ts +1 -1
  363. package/src/notifications/guardian-question-mode.ts +5 -5
  364. package/src/oauth/connect-orchestrator.ts +4 -0
  365. package/src/oauth/credential-token-resolver.ts +1 -3
  366. package/src/oauth/manual-token-connection.ts +0 -4
  367. package/src/outbound-proxy/index.ts +1 -37
  368. package/src/outbound-proxy/logging.ts +1 -1
  369. package/src/outbound-proxy/policy.ts +6 -5
  370. package/src/outbound-proxy/router.ts +2 -1
  371. package/src/permissions/approval-policy.test.ts +6 -275
  372. package/src/permissions/approval-policy.ts +0 -51
  373. package/src/permissions/checker.test.ts +0 -1
  374. package/src/permissions/checker.ts +3 -17
  375. package/src/permissions/gateway-threshold-reader.ts +2 -0
  376. package/src/permissions/prompter.ts +34 -1
  377. package/src/permissions/secret-prompter.ts +6 -2
  378. package/src/prompts/bootstrap-cleanup.ts +27 -0
  379. package/src/prompts/system-prompt.ts +3 -18
  380. package/src/prompts/templates/SOUL.md +13 -1
  381. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  382. package/src/runtime/assistant-event-hub.ts +118 -96
  383. package/src/runtime/assistant-event.ts +1 -0
  384. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  385. package/src/runtime/auth/middleware.ts +0 -96
  386. package/src/runtime/auth/route-policy.ts +19 -0
  387. package/src/runtime/btw-sidechain.ts +2 -3
  388. package/src/runtime/channel-invite-transport.ts +2 -48
  389. package/src/runtime/channel-invite-transports/email.ts +1 -1
  390. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  391. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  392. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  393. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  394. package/src/runtime/channel-invite-types.ts +54 -0
  395. package/src/runtime/channel-readiness-service.ts +32 -13
  396. package/src/runtime/http-server.ts +3 -329
  397. package/src/runtime/http-types.ts +0 -5
  398. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  399. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  400. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  401. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
  402. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  403. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  404. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  405. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  406. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  407. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
  408. package/src/runtime/migrations/migration-transport.ts +7 -7
  409. package/src/runtime/migrations/vbundle-builder.ts +327 -60
  410. package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
  411. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  412. package/src/runtime/migrations/vbundle-importer.ts +245 -68
  413. package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
  414. package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
  415. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  416. package/src/runtime/migrations/vbundle-validator.ts +114 -0
  417. package/src/runtime/pending-interactions.ts +35 -9
  418. package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
  419. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  420. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  421. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  422. package/src/runtime/routes/approval-interception-types.ts +13 -0
  423. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  424. package/src/runtime/routes/backup-routes.ts +15 -38
  425. package/src/runtime/routes/btw-routes.ts +14 -37
  426. package/src/runtime/routes/client-routes.ts +1 -0
  427. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  428. package/src/runtime/routes/conversation-query-routes.ts +36 -1
  429. package/src/runtime/routes/conversation-routes.ts +30 -13
  430. package/src/runtime/routes/document-pdf-renderer.ts +165 -0
  431. package/src/runtime/routes/documents-routes.ts +30 -0
  432. package/src/runtime/routes/errors.ts +19 -4
  433. package/src/runtime/routes/events-routes.ts +12 -6
  434. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  435. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  436. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  437. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  438. package/src/runtime/routes/host-bash-routes.ts +36 -6
  439. package/src/runtime/routes/host-browser-routes.ts +108 -13
  440. package/src/runtime/routes/host-cu-routes.ts +44 -14
  441. package/src/runtime/routes/host-file-routes.ts +33 -10
  442. package/src/runtime/routes/host-transfer-routes.ts +64 -24
  443. package/src/runtime/routes/http-adapter.ts +1 -0
  444. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  445. package/src/runtime/routes/identity-routes.ts +15 -43
  446. package/src/runtime/routes/inbound-message-handler.ts +1 -9
  447. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
  448. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  449. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  450. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  451. package/src/runtime/routes/index.ts +8 -0
  452. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  453. package/src/runtime/routes/memory-item-routes.ts +10 -12
  454. package/src/runtime/routes/memory-v2-routes.ts +441 -1
  455. package/src/runtime/routes/migration-routes.ts +96 -0
  456. package/src/runtime/routes/schedule-routes.ts +7 -0
  457. package/src/runtime/verification-templates.ts +4 -7
  458. package/src/schedule/integration-status.ts +66 -2
  459. package/src/schedule/recurrence-engine.ts +4 -1
  460. package/src/schedule/retry-backoff.ts +18 -0
  461. package/src/schedule/retry-policy.ts +82 -0
  462. package/src/schedule/schedule-recovery.ts +64 -0
  463. package/src/schedule/schedule-store.ts +106 -2
  464. package/src/schedule/scheduler-types.ts +25 -0
  465. package/src/schedule/scheduler.ts +63 -38
  466. package/src/security/oauth-callback-registry.ts +8 -0
  467. package/src/sequence/analytics.ts +5 -5
  468. package/src/sequence/engine.ts +1 -1
  469. package/src/skills/catalog-files.ts +2 -8
  470. package/src/skills/include-graph.ts +5 -5
  471. package/src/skills/remote-skill-policy.ts +5 -5
  472. package/src/skills/skill-file-provider.ts +1 -1
  473. package/src/skills/skill-file-types.ts +13 -0
  474. package/src/skills/skillssh-audit-types.ts +28 -0
  475. package/src/skills/skillssh-registry.ts +8 -21
  476. package/src/telemetry/types.ts +2 -0
  477. package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
  478. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  479. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  480. package/src/tools/apps/executors.ts +56 -69
  481. package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
  482. package/src/tools/browser/browser-execution.ts +2 -2
  483. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
  484. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  485. package/src/tools/browser/cdp-client/factory.ts +23 -24
  486. package/src/tools/browser/cdp-client/index.ts +1 -14
  487. package/src/tools/computer-use/definitions.ts +42 -20
  488. package/src/tools/executor.ts +2 -0
  489. package/src/tools/host-filesystem/edit.ts +26 -0
  490. package/src/tools/host-filesystem/read.ts +26 -0
  491. package/src/tools/host-filesystem/transfer.ts +31 -1
  492. package/src/tools/host-filesystem/write.ts +26 -0
  493. package/src/tools/host-terminal/host-shell.ts +58 -0
  494. package/src/tools/schedule/create.ts +6 -0
  495. package/src/tools/schedule/list.ts +2 -0
  496. package/src/tools/schedule/update.ts +10 -0
  497. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  498. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  499. package/src/tools/skills/load.ts +0 -32
  500. package/src/tools/tool-approval-handler.ts +1 -5
  501. package/src/tools/types.ts +4 -0
  502. package/src/usage/pricing.ts +1 -1
  503. package/src/workspace/hatched-date.ts +86 -0
  504. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  505. package/src/workspace/migrations/006-services-config.ts +8 -5
  506. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  507. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  508. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  509. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  510. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  511. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  512. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
  513. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  514. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
  515. package/src/workspace/migrations/AGENTS.md +1 -1
  516. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  517. package/src/workspace/migrations/utils.ts +21 -0
  518. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
  519. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
  520. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
  521. package/src/__tests__/twilio-rest.test.ts +0 -34
  522. package/src/backup/__tests__/backup-key.test.ts +0 -152
  523. package/src/backup/__tests__/backup-worker.test.ts +0 -782
  524. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  525. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  526. package/src/backup/backup-key.ts +0 -137
  527. package/src/backup/backup-worker.ts +0 -472
  528. package/src/backup/offsite-writer.ts +0 -222
  529. package/src/backup/stream-crypt.ts +0 -263
  530. package/src/daemon/message-types/pairing.ts +0 -58
  531. package/src/outbound-proxy/config.ts +0 -20
  532. package/src/outbound-proxy/health.ts +0 -18
  533. package/src/outbound-proxy/types.ts +0 -150
  534. package/src/runtime/capability-tokens.ts +0 -190
  535. package/src/signals/mcp-reload.ts +0 -18
@@ -34,7 +34,7 @@ import type {
34
34
  * Twilio. A `<Stream>` media-stream is opened and the daemon transcribes
35
35
  * audio server-side via the provider's batch API.
36
36
  */
37
- export type TelephonyStrategyKind =
37
+ type TelephonyStrategyKind =
38
38
  | "conversation-relay-native"
39
39
  | "media-stream-custom";
40
40
 
@@ -51,7 +51,7 @@ export type TwilioNativeProvider = "Deepgram" | "Google";
51
51
  * ConversationRelay. Only present when `strategyKind` is
52
52
  * `"conversation-relay-native"`.
53
53
  */
54
- export interface TwilioNativeMapping {
54
+ interface TwilioNativeMapping {
55
55
  /** Twilio-native provider name for the TwiML `transcriptionProvider` attribute. */
56
56
  readonly provider: TwilioNativeProvider;
57
57
  /**
@@ -69,7 +69,7 @@ export interface TwilioNativeMapping {
69
69
  * The telephony routing resolver reads these fields from the catalog
70
70
  * instead of maintaining its own hardcoded maps.
71
71
  */
72
- export interface TelephonyRouting {
72
+ interface TelephonyRouting {
73
73
  /** Which Twilio call-setup strategy this provider uses. */
74
74
  readonly strategyKind: TelephonyStrategyKind;
75
75
  /**
@@ -84,10 +84,10 @@ export interface TelephonyRouting {
84
84
  // ---------------------------------------------------------------------------
85
85
 
86
86
  /** How the provider's credentials are configured by the user. */
87
- export type SttSetupMode = "api-key" | "cli";
87
+ type SttSetupMode = "api-key" | "cli";
88
88
 
89
89
  /** Guide for obtaining API credentials from a provider. */
90
- export interface SttCredentialsGuide {
90
+ interface SttCredentialsGuide {
91
91
  readonly description: string;
92
92
  readonly url: string;
93
93
  readonly linkLabel: string;
@@ -100,7 +100,7 @@ export interface SttCredentialsGuide {
100
100
  /**
101
101
  * Metadata for a single STT provider.
102
102
  */
103
- export interface SttProviderEntry {
103
+ interface SttProviderEntry {
104
104
  /** Canonical provider identifier (must match an {@link SttProviderId} variant). */
105
105
  readonly id: SttProviderId;
106
106
 
@@ -262,8 +262,7 @@ const CATALOG: ReadonlyMap<SttProviderId, SttProviderEntry> = new Map<
262
262
  subtitle:
263
263
  "High-accuracy speech-to-text powered by OpenAI Whisper. Requires an OpenAI API key.",
264
264
  setupMode: "api-key",
265
- setupHint:
266
- "Enter your OpenAI API key to enable Whisper transcription.",
265
+ setupHint: "Enter your OpenAI API key to enable Whisper transcription.",
267
266
  credentialProvider: "openai",
268
267
  supportedBoundaries: new Set<SttBoundaryId>([
269
268
  "daemon-batch",
@@ -14,6 +14,31 @@
14
14
 
15
15
  import type { HostProxyCapability, InterfaceId } from "../channels/types.js";
16
16
  import type { ServerMessage } from "../daemon/message-protocol.js";
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Message type → capability inference
20
+ // ---------------------------------------------------------------------------
21
+
22
+ const HOST_PREFIX_TO_CAPABILITY: Record<string, HostProxyCapability> = {
23
+ host_bash: "host_bash",
24
+ host_file: "host_file",
25
+ host_transfer: "host_file", // transfers piggyback on host_file capability
26
+ host_cu: "host_cu",
27
+ host_browser: "host_browser",
28
+ host_app_control: "host_app_control",
29
+ };
30
+
31
+ /**
32
+ * Infer the {@link HostProxyCapability} a message should be targeted at based
33
+ * on its `type` field. Returns `undefined` for message types that are not
34
+ * host-proxy messages (i.e. they should broadcast to all subscribers).
35
+ */
36
+ export function capabilityForMessageType(
37
+ type: string,
38
+ ): HostProxyCapability | undefined {
39
+ const stem = type.replace(/_(request|cancel)$/, "");
40
+ return HOST_PREFIX_TO_CAPABILITY[stem];
41
+ }
17
42
  import { emitFeedEvent } from "../home/emit-feed-event.js";
18
43
  import { rewriteCommandPreview } from "../home/rewrite-command-preview.js";
19
44
  import { redactSecrets } from "../security/secret-scanner.js";
@@ -22,7 +47,6 @@ import { summarizeToolInput } from "../tools/tool-input-summary.js";
22
47
  import { getLogger } from "../util/logger.js";
23
48
  import type { AssistantEvent } from "./assistant-event.js";
24
49
  import { buildAssistantEvent } from "./assistant-event.js";
25
- import * as pendingInteractions from "./pending-interactions.js";
26
50
 
27
51
  const log = getLogger("assistant-event-hub");
28
52
 
@@ -61,6 +85,7 @@ export interface ClientEntry extends BaseSubscriberEntry {
61
85
  clientId: string;
62
86
  interfaceId: InterfaceId;
63
87
  capabilities: HostProxyCapability[];
88
+ machineName?: string;
64
89
  }
65
90
 
66
91
  export interface ProcessEntry extends BaseSubscriberEntry {
@@ -216,7 +241,12 @@ export class AssistantEventHub {
216
241
  * Publish an event to all matching subscribers.
217
242
  *
218
243
  * Matching rules:
219
- * - if `filter.conversationId` is set, `event.conversationId` must equal it
244
+ * - if `targetClientId` is set, deliver only to the subscriber with that
245
+ * clientId, bypassing the conversation-id filter entirely (the web-origin
246
+ * event's conversationId differs from the macOS client's subscribed
247
+ * conversation).
248
+ * - if `filter.conversationId` is set (and `targetClientId` is not), the
249
+ * `event.conversationId` must equal it
220
250
  * - if `targetCapability` is set, only subscribers whose capabilities include
221
251
  * it receive the event; untargeted events go to all
222
252
  *
@@ -225,7 +255,7 @@ export class AssistantEventHub {
225
255
  */
226
256
  async publish(
227
257
  event: AssistantEvent,
228
- options?: { targetCapability?: HostProxyCapability },
258
+ options?: { targetCapability?: HostProxyCapability; targetClientId?: string },
229
259
  ): Promise<void> {
230
260
  if (event.conversationId) {
231
261
  try {
@@ -236,29 +266,40 @@ export class AssistantEventHub {
236
266
  }
237
267
 
238
268
  const targetCapability = options?.targetCapability;
269
+ const targetClientId = options?.targetClientId;
239
270
  const snapshot = Array.from(this.subscribers);
240
271
  const errors: unknown[] = [];
241
272
 
242
273
  for (const entry of snapshot) {
243
274
  if (!entry.active) continue;
244
275
 
245
- // Conversation scoping: scoped events skip subscribers filtering on a
246
- // different conversation.
247
- if (
248
- event.conversationId != null &&
249
- entry.filter.conversationId != null &&
250
- entry.filter.conversationId !== event.conversationId
251
- )
252
- continue;
253
-
254
- // Capability targeting: targeted events only go to subscribers that
255
- // declare the required capability.
256
- if (targetCapability != null) {
276
+ if (targetClientId != null) {
277
+ // Targeted: bypass conversation filter, deliver only to the named client.
278
+ if (entry.type !== "client" || entry.clientId !== targetClientId)
279
+ continue;
257
280
  if (
258
- entry.type !== "client" ||
281
+ targetCapability != null &&
259
282
  !entry.capabilities.includes(targetCapability)
260
283
  )
261
284
  continue;
285
+ } else {
286
+ // Untargeted: existing conversation-scoped + capability logic.
287
+ if (
288
+ event.conversationId != null &&
289
+ entry.filter.conversationId != null &&
290
+ entry.filter.conversationId !== event.conversationId
291
+ )
292
+ continue;
293
+
294
+ // Capability targeting: targeted events only go to subscribers that
295
+ // declare the required capability.
296
+ if (targetCapability != null) {
297
+ if (
298
+ entry.type !== "client" ||
299
+ !entry.capabilities.includes(targetCapability)
300
+ )
301
+ continue;
302
+ }
262
303
  }
263
304
 
264
305
  try {
@@ -276,6 +317,18 @@ export class AssistantEventHub {
276
317
  }
277
318
  }
278
319
 
320
+ /**
321
+ * Return the active client subscriber with the given clientId, or
322
+ * `undefined` if no such subscriber exists.
323
+ */
324
+ getClientById(clientId: string): ClientEntry | undefined {
325
+ for (const entry of this.subscribers) {
326
+ if (entry.active && entry.type === "client" && entry.clientId === clientId)
327
+ return entry;
328
+ }
329
+ return undefined;
330
+ }
331
+
279
332
  /**
280
333
  * Returns true when at least one active subscriber would receive the given
281
334
  * event based on the same conversation matching rules as publish().
@@ -338,6 +391,35 @@ export class AssistantEventHub {
338
391
  return this.listClientsByCapability(capability)[0];
339
392
  }
340
393
 
394
+ /**
395
+ * Return the best client for the given capability using an explicit
396
+ * interface preference order. Among clients that support `capability`,
397
+ * the one whose `interfaceId` appears earliest in `interfacePreference`
398
+ * wins. Within the same interface tier, `lastActiveAt` is the tiebreaker
399
+ * (most recent first). Clients not in the preference list are considered last.
400
+ *
401
+ * Used by {@link HostBrowserProxy} to prefer the Chrome Extension
402
+ * (`chrome-extension`) over the macOS SSE bridge (`macos`) when both are
403
+ * connected, so `chrome.debugger` is used ahead of the localhost:9222 path.
404
+ */
405
+ getPreferredClientByCapability(
406
+ capability: HostProxyCapability,
407
+ interfacePreference: InterfaceId[],
408
+ ): ClientEntry | undefined {
409
+ const clients = this.listClientsByCapability(capability);
410
+ if (clients.length === 0) return undefined;
411
+ // listClientsByCapability returns clients sorted by lastActiveAt desc
412
+ // (most recent first). A stable sort by preference index preserves that
413
+ // ordering within each interface tier.
414
+ return clients.sort((a, b) => {
415
+ const ai = interfacePreference.indexOf(a.interfaceId);
416
+ const bi = interfacePreference.indexOf(b.interfaceId);
417
+ const ea = ai === -1 ? interfacePreference.length : ai;
418
+ const eb = bi === -1 ? interfacePreference.length : bi;
419
+ return ea - eb;
420
+ })[0];
421
+ }
422
+
341
423
  /**
342
424
  * Return all client subscribers with the given interface type,
343
425
  * sorted by `lastActiveAt` descending.
@@ -372,10 +454,7 @@ export class AssistantEventHub {
372
454
  disposeClient(clientId: string): number {
373
455
  const targets: SubscriberEntry[] = [];
374
456
  for (const entry of this.subscribers) {
375
- if (
376
- entry.type === "client" &&
377
- entry.clientId === clientId
378
- ) {
457
+ if (entry.type === "client" && entry.clientId === clientId) {
379
458
  targets.push(entry);
380
459
  }
381
460
  }
@@ -432,25 +511,27 @@ let _hubChain = Promise.resolve();
432
511
  * When `conversationId` is omitted, it is auto-extracted from the message
433
512
  * payload (if present).
434
513
  *
514
+ * Target capability is inferred automatically from the message type — callers
515
+ * never need to specify it. Host-proxy messages (`host_bash_*`,
516
+ * `host_file_*`, `host_transfer_*`, `host_cu_*`, `host_browser_*`) are routed
517
+ * only to subscribers that declare the matching capability; all other messages
518
+ * broadcast to every subscriber.
519
+ *
435
520
  * This is the primary entrypoint for emitting events — handlers, routes, and
436
521
  * services should call this directly instead of threading a broadcast callback.
437
522
  */
438
523
  export function broadcastMessage(
439
524
  msg: ServerMessage,
440
525
  conversationId?: string,
441
- options?: { targetCapability?: HostProxyCapability },
526
+ options?: { targetClientId?: string },
442
527
  ): void {
443
528
  const resolvedConversationId = conversationId ?? extractConversationId(msg);
529
+ const targetClientId = options?.targetClientId;
444
530
 
445
- // Register pending interactions so approval/host prompts are tracked
446
- // regardless of which path triggered the broadcast.
447
- if (resolvedConversationId) {
448
- registerPendingInteraction(msg, resolvedConversationId);
449
- }
450
-
451
- // Emit feed events for confirmation requests (tool approval prompts).
531
+ // Confirmation-request side effects: feed event + canonical guardian request.
452
532
  if (msg.type === "confirmation_request" && resolvedConversationId) {
453
533
  void emitConfirmationFeedEvent(msg, resolvedConversationId);
534
+ void createCanonicalRequestForConfirmation(msg, resolvedConversationId);
454
535
  }
455
536
 
456
537
  // `conversation_list_invalidated` is a list-level system event — publish
@@ -460,8 +541,13 @@ export function broadcastMessage(
460
541
  ? undefined
461
542
  : resolvedConversationId;
462
543
  const event = buildAssistantEvent(msg, scopedConversationId);
544
+ const targetCapability = capabilityForMessageType(msg.type);
545
+ const publishOptions =
546
+ targetCapability != null || targetClientId != null
547
+ ? { targetCapability, targetClientId }
548
+ : undefined;
463
549
  _hubChain = _hubChain
464
- .then(() => assistantEventHub.publish(event, options))
550
+ .then(() => assistantEventHub.publish(event, publishOptions))
465
551
  .then(() => {
466
552
  // When a conversation title changes, also broadcast an unscoped
467
553
  // `conversation_list_invalidated` so every connected client's sidebar
@@ -495,7 +581,7 @@ function extractConversationId(msg: ServerMessage): string | undefined {
495
581
  return undefined;
496
582
  }
497
583
 
498
- // ── Pending interaction registration ──────────────────────────────────────────
584
+ // ── Canonical guardian request ────────────────────────────────────────────────
499
585
 
500
586
  function resolveCanonicalRequestSourceType(
501
587
  sourceChannel: string,
@@ -505,74 +591,10 @@ function resolveCanonicalRequestSourceType(
505
591
  return "channel";
506
592
  }
507
593
 
508
- /**
509
- * Register pending interactions for request-type messages so approval and
510
- * host prompts are tracked regardless of which code path broadcasts them.
511
- *
512
- * Heavy dependencies (conversation-store, canonical-guardian-store, etc.) are
513
- * imported lazily so that loading this module during tests doesn't trigger
514
- * config/data-dir side effects.
515
- */
516
- function registerPendingInteraction(
517
- msg: ServerMessage,
518
- conversationId: string,
519
- ): void {
520
- if (msg.type === "confirmation_request") {
521
- pendingInteractions.register(msg.requestId, {
522
- conversationId,
523
- kind: "confirmation",
524
- confirmationDetails: {
525
- toolName: msg.toolName,
526
- input: msg.input,
527
- riskLevel: msg.riskLevel,
528
- executionTarget: msg.executionTarget,
529
- allowlistOptions: msg.allowlistOptions,
530
- scopeOptions: msg.scopeOptions,
531
- persistentDecisionsAllowed: msg.persistentDecisionsAllowed,
532
- },
533
- });
534
-
535
- // Create canonical guardian request asynchronously — heavy deps are
536
- // imported lazily to avoid pulling in conversation-store (and
537
- // transitively config/loader → ensureDataDir) at module-load time.
538
- void createCanonicalRequestForConfirmation(msg, conversationId);
539
- } else if (msg.type === "secret_request") {
540
- pendingInteractions.register(msg.requestId, {
541
- conversationId,
542
- kind: "secret",
543
- });
544
- } else if (msg.type === "host_bash_request") {
545
- pendingInteractions.register(msg.requestId, {
546
- conversationId,
547
- kind: "host_bash",
548
- });
549
- } else if (msg.type === "host_browser_request") {
550
- pendingInteractions.register(msg.requestId, {
551
- conversationId,
552
- kind: "host_browser",
553
- });
554
- } else if (msg.type === "host_file_request") {
555
- pendingInteractions.register(msg.requestId, {
556
- conversationId,
557
- kind: "host_file",
558
- });
559
- } else if (msg.type === "host_cu_request") {
560
- pendingInteractions.register(msg.requestId, {
561
- conversationId,
562
- kind: "host_cu",
563
- });
564
- } else if (msg.type === "host_transfer_request") {
565
- pendingInteractions.register(msg.requestId, {
566
- conversationId,
567
- kind: "host_transfer",
568
- });
569
- }
570
- }
571
-
572
594
  /**
573
595
  * Lazily load heavy dependencies and create a canonical guardian request +
574
- * bridge for a confirmation_request message. Runs fire-and-forget from
575
- * registerPendingInteraction.
596
+ * bridge for a confirmation_request message. Called fire-and-forget from
597
+ * broadcastMessage.
576
598
  */
577
599
  async function createCanonicalRequestForConfirmation(
578
600
  msg: ServerMessage & { type: "confirmation_request" },
@@ -16,6 +16,7 @@ import type { ServerMessage } from "../daemon/message-protocol.js";
16
16
  export {
17
17
  formatSseFrame,
18
18
  formatSseHeartbeat,
19
+ formatSseHeartbeatWithData,
19
20
  } from "@vellumai/skill-host-contracts";
20
21
 
21
22
  /** Daemon-side specialization of the generic event envelope. */
@@ -35,12 +35,6 @@ mock.module("../../../config/env.js", () => ({
35
35
 
36
36
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../assistant-scope.js";
37
37
  import {
38
- mintHostBrowserCapability,
39
- resetCapabilityTokenSecretForTests,
40
- setCapabilityTokenSecretForTests,
41
- } from "../../capability-tokens.js";
42
- import {
43
- authenticateHostBrowserResultRequest,
44
38
  authenticateRequest,
45
39
  } from "../middleware.js";
46
40
  import { initAuthSigningKey, mintToken } from "../token-service.js";
@@ -272,55 +266,20 @@ describe("authenticateRequest", () => {
272
266
  });
273
267
 
274
268
  // ---------------------------------------------------------------------------
275
- // authenticateHostBrowserResultRequest — capability-token-aware auth for the
276
- // /v1/host-browser-result POST route. Verifies that both the capability-token
277
- // and JWT paths are accepted, and that a garbage bearer falls through to the
278
- // JWT path and emits a 401 like any other invalid token.
269
+ // /v1/host-browser-result auth exercises authenticateRequest with the
270
+ // same request shape the chrome extension sends. Validates that standard
271
+ // JWT auth applies after the capability-token system was removed.
279
272
  // ---------------------------------------------------------------------------
280
273
 
281
- describe("authenticateHostBrowserResultRequest", () => {
282
- const CAPABILITY_SECRET = Buffer.alloc(32, 7);
283
-
284
- beforeEach(() => {
285
- // Pin the capability-token HMAC secret so mint/verify agree across
286
- // the test run. The module-level secret cache is reset between
287
- // tests so dev-bypass flipping doesn't leak stale state.
288
- setCapabilityTokenSecretForTests(CAPABILITY_SECRET);
289
- });
290
-
291
- afterAll(() => {
292
- resetCapabilityTokenSecretForTests();
293
- });
294
-
295
- test("accepts a valid capability token and synthesizes an actor AuthContext", async () => {
296
- const { token } = mintHostBrowserCapability("guardian-cap-happy");
297
- const req = new Request("http://localhost/v1/host-browser-result", {
298
- method: "POST",
299
- headers: { Authorization: `Bearer ${token}` },
300
- });
301
-
302
- const result = await authenticateHostBrowserResultRequest(req);
303
- expect(result.ok).toBe(true);
304
- if (result.ok) {
305
- expect(result.context.principalType).toBe("actor");
306
- expect(result.context.assistantId).toBe(DAEMON_INTERNAL_ASSISTANT_ID);
307
- expect(result.context.actorPrincipalId).toBe("guardian-cap-happy");
308
- expect(result.context.scopeProfile).toBe("actor_client_v1");
309
- // The synthetic context must carry the scopes the route policy
310
- // requires — otherwise the router would 403 the POST even though
311
- // auth succeeded.
312
- expect(result.context.scopes.has("approval.write")).toBe(true);
313
- }
314
- });
315
-
316
- test("accepts a valid daemon-audience JWT (regression for the legacy path)", async () => {
274
+ describe("authenticateRequest for /v1/host-browser-result", () => {
275
+ test("accepts a valid daemon-audience JWT", async () => {
317
276
  const token = mintValidToken({ sub: "actor:self:jwt-principal" });
318
277
  const req = new Request("http://localhost/v1/host-browser-result", {
319
278
  method: "POST",
320
279
  headers: { Authorization: `Bearer ${token}` },
321
280
  });
322
281
 
323
- const result = await authenticateHostBrowserResultRequest(req);
282
+ const result = await authenticateRequest(req);
324
283
  expect(result.ok).toBe(true);
325
284
  if (result.ok) {
326
285
  expect(result.context.principalType).toBe("actor");
@@ -334,25 +293,21 @@ describe("authenticateHostBrowserResultRequest", () => {
334
293
  method: "POST",
335
294
  });
336
295
 
337
- const result = await authenticateHostBrowserResultRequest(req);
296
+ const result = await authenticateRequest(req);
338
297
  expect(result.ok).toBe(false);
339
298
  if (!result.ok) {
340
299
  expect(result.response.status).toBe(401);
341
300
  }
342
301
  });
343
302
 
344
- test("malformed bearer falls through to JWT path and 401s", async () => {
345
- // A bearer that is neither a valid capability token (bad HMAC) nor a
346
- // parseable JWT must fail the JWT path and return 401. This is the
347
- // primary regression guard against someone accidentally making the
348
- // capability-token branch "allow-anything" by swallowing
349
- // verification failures.
303
+ test("malformed bearer returns 401", async () => {
304
+ // A bearer that is not a parseable JWT must return 401.
350
305
  const req = new Request("http://localhost/v1/host-browser-result", {
351
306
  method: "POST",
352
307
  headers: { Authorization: "Bearer not-a-token.xxxxxxxxxxxxx" },
353
308
  });
354
309
 
355
- const result = await authenticateHostBrowserResultRequest(req);
310
+ const result = await authenticateRequest(req);
356
311
  expect(result.ok).toBe(false);
357
312
  if (!result.ok) {
358
313
  expect(result.response.status).toBe(401);
@@ -366,7 +321,7 @@ describe("authenticateHostBrowserResultRequest", () => {
366
321
  method: "POST",
367
322
  });
368
323
 
369
- const result = await authenticateHostBrowserResultRequest(req);
324
+ const result = await authenticateRequest(req);
370
325
  expect(result.ok).toBe(true);
371
326
  if (result.ok) {
372
327
  // Same synthetic context shape as authenticateRequest's dev
@@ -25,7 +25,6 @@
25
25
  import { isHttpAuthDisabled } from "../../config/env.js";
26
26
  import { getLogger } from "../../util/logger.js";
27
27
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
28
- import { verifyHostBrowserCapability } from "../capability-tokens.js";
29
28
  import { extractBearerToken } from "../middleware/auth.js";
30
29
  import { buildAuthContext } from "./context.js";
31
30
  import { resolveScopeProfile } from "./scopes.js";
@@ -188,99 +187,4 @@ export function authenticateRequest(req: Request): AuthenticateResult {
188
187
  return { ok: true, context: contextResult.context };
189
188
  }
190
189
 
191
- // ---------------------------------------------------------------------------
192
- // Capability-token-aware auth for /v1/host-browser-result
193
- // ---------------------------------------------------------------------------
194
190
 
195
- /**
196
- * Build a synthetic AuthContext from a verified host_browser capability
197
- * claim. The resulting context is shaped to look like an
198
- * `actor_client_v1` actor so downstream route policy (which requires
199
- * `approval.write`) and `requireBoundGuardian` (which compares
200
- * `actorPrincipalId` against the bound guardian) both accept it.
201
- *
202
- * The capability token already carries its own HMAC-checked expiry, so
203
- * there is no policy-epoch gate to apply here — we pin `policyEpoch` to
204
- * `Number.MAX_SAFE_INTEGER` the same way the dev-bypass context does.
205
- */
206
- function buildCapabilityAuthContext(guardianId: string): AuthContext {
207
- return {
208
- subject: `actor:${DAEMON_INTERNAL_ASSISTANT_ID}:${guardianId}`,
209
- principalType: "actor",
210
- assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
211
- actorPrincipalId: guardianId,
212
- scopeProfile: "actor_client_v1",
213
- scopes: resolveScopeProfile("actor_client_v1"),
214
- policyEpoch: Number.MAX_SAFE_INTEGER,
215
- };
216
- }
217
-
218
- /**
219
- * Authenticate a request that is allowed to present either a JWT or a
220
- * host_browser capability token. This is the auth entry point for
221
- * `/v1/host-browser-result` POST specifically — the chrome extension
222
- * stores a capability token (minted by the
223
- * `/v1/browser-extension-pair` flow) rather than a daemon JWT, so the
224
- * POST fallback used when the `/v1/browser-relay` WebSocket is
225
- * unavailable would otherwise 401 through the JWT-only
226
- * `authenticateRequest` path.
227
- *
228
- * Order of operations (mirrors `handleBrowserRelayUpgrade`):
229
- * 1. Extract the bearer token. Missing header → 401.
230
- * 2. Try `verifyHostBrowserCapability(token)` first. If it succeeds,
231
- * derive `guardianId` from the capability claims and synthesize an
232
- * AuthContext.
233
- * 3. Otherwise fall through to the standard JWT path so daemon-minted
234
- * JWTs (gateway-proxied or direct) continue to work as a
235
- * regression-safe compatibility path.
236
- *
237
- * Dev bypass (`isHttpAuthDisabled()`) is honored the same way as
238
- * `authenticateRequest` — we delegate to it directly to pick up the
239
- * shared synthetic dev-bypass context.
240
- */
241
- export async function authenticateHostBrowserResultRequest(
242
- req: Request,
243
- ): Promise<AuthenticateResult> {
244
- if (isHttpAuthDisabled()) {
245
- return { ok: true, context: buildDevBypassContext() };
246
- }
247
-
248
- const rawToken = extractBearerToken(req);
249
- if (!rawToken) {
250
- log.warn(
251
- { reason: "missing_token", path: "/v1/host-browser-result" },
252
- "Host browser result auth denied: missing Authorization header",
253
- );
254
- return {
255
- ok: false,
256
- response: Response.json(
257
- {
258
- error: {
259
- code: "UNAUTHORIZED",
260
- message: "Missing Authorization header",
261
- },
262
- },
263
- { status: 401 },
264
- ),
265
- };
266
- }
267
-
268
- // 1) Capability-token path (self-hosted default). The chrome
269
- // extension presents the token it received from the native
270
- // messaging pair flow. We derive `actorPrincipalId` from the
271
- // capability claims directly — the claims are HMAC-signed by the
272
- // same daemon so there is no cross-tenant risk.
273
- const capabilityClaims = await verifyHostBrowserCapability(rawToken);
274
- if (capabilityClaims) {
275
- return {
276
- ok: true,
277
- context: buildCapabilityAuthContext(capabilityClaims.guardianId),
278
- };
279
- }
280
-
281
- // 2) JWT compatibility path. Fall back to the existing daemon/gateway
282
- // JWT verification so cloud callers and any legacy self-hosted
283
- // clients still holding a daemon JWT continue to work. Any 401
284
- // emitted here already includes the JWT-specific reason.
285
- return authenticateRequest(req);
286
- }
@@ -151,8 +151,11 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
151
151
  { endpoint: "confirm", scopes: ["approval.write"] },
152
152
  { endpoint: "secret", scopes: ["approval.write"] },
153
153
  { endpoint: "trust-rules", scopes: ["approval.write"] },
154
+ { endpoint: "host-app-control-result", scopes: ["approval.write"] },
154
155
  { endpoint: "host-bash-result", scopes: ["approval.write"] },
155
156
  { endpoint: "host-browser-result", scopes: ["approval.write"] },
157
+ { endpoint: "host-browser-event", scopes: ["approval.write"] },
158
+ { endpoint: "host-browser-session-invalidated", scopes: ["approval.write"] },
156
159
  { endpoint: "host-cu-result", scopes: ["approval.write"] },
157
160
  { endpoint: "host-file-result", scopes: ["approval.write"] },
158
161
  { endpoint: "host-transfer-result", scopes: ["approval.write"] },
@@ -204,6 +207,8 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
204
207
  { endpoint: "contacts/invites:POST", scopes: ["settings.write"] },
205
208
  { endpoint: "contacts/invites/redeem", scopes: ["settings.write"] },
206
209
  { endpoint: "contacts/invites:DELETE", scopes: ["settings.write"] },
210
+ { endpoint: "contacts/prompt:POST", scopes: ["settings.write"] },
211
+ { endpoint: "resolve_contact_prompt:POST", scopes: ["settings.write"] },
207
212
  { endpoint: "integrations/telegram/config", scopes: ["settings.read"] },
208
213
  { endpoint: "integrations/telegram/config:POST", scopes: ["settings.write"] },
209
214
  {
@@ -422,7 +427,15 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
422
427
  { endpoint: "memory-items:DELETE", scopes: ["settings.write"] },
423
428
  { endpoint: "memory/v2/backfill:POST", scopes: ["settings.write"] },
424
429
  { endpoint: "memory/v2/validate:POST", scopes: ["settings.read"] },
430
+ { endpoint: "memory/v2/concept-page:POST", scopes: ["settings.read"] },
425
431
  { endpoint: "memory/v2/reembed-skills:POST", scopes: ["settings.write"] },
432
+ { endpoint: "memory/v2/explain-similarity:POST", scopes: ["settings.read"] },
433
+ { endpoint: "memory/v2/fit-anisotropy:POST", scopes: ["settings.write"] },
434
+ {
435
+ endpoint: "memory/v2/rebuild-corpus-stats:POST",
436
+ scopes: ["settings.write"],
437
+ },
438
+ { endpoint: "memory/v2/fit-anisotropy:POST", scopes: ["settings.write"] },
426
439
 
427
440
  // Trust rule listing
428
441
  { endpoint: "trust-rules/manage:GET", scopes: ["settings.read"] },
@@ -490,6 +503,9 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
490
503
  { endpoint: "consolidation", scopes: ["settings.read"] },
491
504
  { endpoint: "consolidation:POST", scopes: ["settings.write"] },
492
505
 
506
+ // Gateway log proxy
507
+ { endpoint: "gateway/logs/tail", scopes: ["settings.read"] },
508
+
493
509
  // Heartbeat (config, runs, checklist — all share the "heartbeat" policyKey)
494
510
  { endpoint: "heartbeat:GET", scopes: ["settings.read"] },
495
511
  { endpoint: "heartbeat", scopes: ["settings.write"] },
@@ -563,6 +579,9 @@ const INTERNAL_ENDPOINTS = [
563
579
  "internal/twilio/status",
564
580
  "internal/twilio/connect-action",
565
581
  "internal/oauth/callback",
582
+ "internal/mcp/auth/start",
583
+ "internal/mcp/auth/status",
584
+ "internal/mcp/reload", // ← new
566
585
  ];
567
586
  for (const endpoint of INTERNAL_ENDPOINTS) {
568
587
  registerPolicy(endpoint, {