@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
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Verifies that the queue-drain paths in `conversation-process.ts` re-add
3
+ * the `app-control` skill to the conversation's preactivated set when the
4
+ * dequeued message's `userMessageInterface` supports the `host_app_control`
5
+ * proxy capability.
6
+ *
7
+ * Both `drainSingleMessage` (single-message path) and `drainBatch`
8
+ * (batched path) reset `preactivatedSkillIds = undefined` at the top of
9
+ * each drain. Without an explicit re-add, queued messages 2+ would lose
10
+ * the `app-control` skill — its tools wouldn't be projected to the LLM —
11
+ * even though the `HostAppControlProxy` is still attached to the
12
+ * conversation. This mirrors the existing parallel re-add for
13
+ * `computer-use` and uses the same `supportsHostProxy(_, "host_app_control")`
14
+ * gate that `prepareConversationForMessage` and the `conversation-routes`
15
+ * instantiation block use at first-message time.
16
+ */
17
+
18
+ import { describe, expect, mock, test } from "bun:test";
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Module mocks for downstream side effects (DB writes, slash resolution,
22
+ // notification preference extraction). The drain paths must be allowed to
23
+ // reach the preactivation block; they must not be allowed to touch a real DB.
24
+ // ---------------------------------------------------------------------------
25
+
26
+ mock.module("../util/logger.js", () => ({
27
+ getLogger: () =>
28
+ new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
29
+ }));
30
+
31
+ mock.module("../memory/conversation-crud.js", () => ({
32
+ setConversationOriginChannelIfUnset: () => {},
33
+ setConversationOriginInterfaceIfUnset: () => {},
34
+ provenanceFromTrustContext: () => ({
35
+ source: "user",
36
+ trustContext: undefined,
37
+ }),
38
+ addMessage: () => ({ id: "msg-mock" }),
39
+ }));
40
+
41
+ mock.module("../memory/canonical-guardian-store.js", () => ({
42
+ listPendingRequestsByConversationScope: () => [],
43
+ }));
44
+
45
+ mock.module("../memory/trace-event-store.js", () => ({
46
+ persistTraceEvent: () => {},
47
+ getMaxSequence: () => 0,
48
+ }));
49
+
50
+ mock.module("../notifications/preference-extractor.js", () => ({
51
+ extractPreferences: async () => ({ detected: false, preferences: [] }),
52
+ }));
53
+
54
+ mock.module("../notifications/preferences-store.js", () => ({
55
+ createPreference: () => {},
56
+ }));
57
+
58
+ mock.module("../agent/attachments.ts", () => ({
59
+ enrichMessageWithSourcePaths: <T>(msg: T) => msg,
60
+ }));
61
+
62
+ // Stub the batched-drain helper so the test doesn't fall through to real
63
+ // SQLite paths after the preactivation block has already run. The drain
64
+ // chain doesn't recurse here because our stubbed `runAgentLoop` is a no-op.
65
+ mock.module("../daemon/conversation-messaging.js", () => ({
66
+ persistQueuedMessageBody: async () => "user-msg-id",
67
+ }));
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Imports under test (after mocks)
71
+ // ---------------------------------------------------------------------------
72
+
73
+ import type { TurnInterfaceContext } from "../channels/types.js";
74
+ import type { ProcessConversationContext } from "../daemon/conversation-process.js";
75
+ import { drainQueue } from "../daemon/conversation-process.js";
76
+ import {
77
+ MessageQueue,
78
+ type QueuedMessage,
79
+ } from "../daemon/conversation-queue-manager.js";
80
+ import { TraceEmitter } from "../daemon/trace-emitter.js";
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Fake context — captures preactivation calls, satisfies the bare minimum
84
+ // of `ProcessConversationContext`. `runAgentLoop` resolves immediately so
85
+ // the drain-chain does not recurse forever.
86
+ // ---------------------------------------------------------------------------
87
+
88
+ interface FakeRecord {
89
+ preactivatedSkillIdCalls: string[];
90
+ }
91
+
92
+ function makeFakeContext(opts: {
93
+ queue: MessageQueue;
94
+ turnInterfaceContext?: TurnInterfaceContext;
95
+ }): ProcessConversationContext & FakeRecord {
96
+ const calls: string[] = [];
97
+ let preactivatedSkillIds: string[] | undefined = undefined;
98
+ const ctx = {
99
+ conversationId: "conv-app-control-preactivation",
100
+ messages: [],
101
+ processing: false,
102
+ abortController: null,
103
+ queue: opts.queue,
104
+ traceEmitter: new TraceEmitter("conv-app-control-preactivation", () => {}),
105
+ surfaceActionRequestIds: new Set<string>(),
106
+ usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
107
+ get preactivatedSkillIds(): string[] | undefined {
108
+ return preactivatedSkillIds;
109
+ },
110
+ set preactivatedSkillIds(value: string[] | undefined) {
111
+ preactivatedSkillIds = value;
112
+ },
113
+ preactivatedSkillIdCalls: calls,
114
+ addPreactivatedSkillId(id: string) {
115
+ calls.push(id);
116
+ if (!preactivatedSkillIds) {
117
+ preactivatedSkillIds = [id];
118
+ } else if (!preactivatedSkillIds.includes(id)) {
119
+ preactivatedSkillIds.push(id);
120
+ }
121
+ },
122
+ async ensureActorScopedHistory() {},
123
+ async persistUserMessage() {
124
+ return "user-msg-id";
125
+ },
126
+ async runAgentLoop() {
127
+ // No-op: the drain path's finally block would normally call drainQueue
128
+ // recursively. We intentionally do not chain another drain here so the
129
+ // test asserts on what the FIRST dequeue produced.
130
+ },
131
+ getTurnChannelContext: () => null,
132
+ setTurnChannelContext() {},
133
+ getTurnInterfaceContext: () => opts.turnInterfaceContext ?? null,
134
+ setTurnInterfaceContext() {},
135
+ emitActivityState() {},
136
+ async forceCompact() {
137
+ return {
138
+ compacted: false,
139
+ reason: "no-op",
140
+ estimatedInputTokens: 0,
141
+ previousEstimatedInputTokens: 0,
142
+ maxInputTokens: 100000,
143
+ compactedMessages: 0,
144
+ } as never;
145
+ },
146
+ setTransportHints() {},
147
+ applyHostEnvFromTransport() {},
148
+ } as unknown as ProcessConversationContext & FakeRecord;
149
+ return ctx;
150
+ }
151
+
152
+ function makeQueuedMessage(opts: {
153
+ requestId: string;
154
+ content?: string;
155
+ turnInterfaceContext?: TurnInterfaceContext;
156
+ }): QueuedMessage {
157
+ return {
158
+ content: opts.content ?? "follow up",
159
+ attachments: [],
160
+ requestId: opts.requestId,
161
+ onEvent: () => {},
162
+ metadata: {},
163
+ sentAt: Date.now(),
164
+ turnInterfaceContext: opts.turnInterfaceContext,
165
+ };
166
+ }
167
+
168
+ // ---------------------------------------------------------------------------
169
+ // Tests
170
+ // ---------------------------------------------------------------------------
171
+
172
+ describe("drainQueue preactivation re-add for host-proxy interfaces", () => {
173
+ test("drainSingleMessage re-adds 'app-control' for macOS-sourced queued message", async () => {
174
+ const queue = new MessageQueue();
175
+ const ifCtx: TurnInterfaceContext = {
176
+ userMessageInterface: "macos",
177
+ assistantMessageInterface: "macos",
178
+ };
179
+ queue.push(
180
+ makeQueuedMessage({ requestId: "req-2", turnInterfaceContext: ifCtx }),
181
+ );
182
+ const ctx = makeFakeContext({ queue, turnInterfaceContext: ifCtx });
183
+
184
+ await drainQueue(ctx);
185
+
186
+ // Both CU and app-control must be re-preactivated for queued macOS turns.
187
+ expect(ctx.preactivatedSkillIdCalls).toContain("computer-use");
188
+ expect(ctx.preactivatedSkillIdCalls).toContain("app-control");
189
+ expect(ctx.preactivatedSkillIds).toContain("app-control");
190
+ });
191
+
192
+ test("drainSingleMessage does not re-add 'app-control' for chrome-extension (host_app_control unsupported)", async () => {
193
+ const queue = new MessageQueue();
194
+ // chrome-extension supports host_browser but NOT host_app_control. The
195
+ // CU re-add (no-arg form) also returns false for chrome-extension, so
196
+ // neither skill should be re-preactivated.
197
+ const ifCtx: TurnInterfaceContext = {
198
+ userMessageInterface: "chrome-extension",
199
+ assistantMessageInterface: "chrome-extension",
200
+ };
201
+ queue.push(
202
+ makeQueuedMessage({ requestId: "req-2", turnInterfaceContext: ifCtx }),
203
+ );
204
+ const ctx = makeFakeContext({ queue, turnInterfaceContext: ifCtx });
205
+
206
+ await drainQueue(ctx);
207
+
208
+ expect(ctx.preactivatedSkillIdCalls).not.toContain("computer-use");
209
+ expect(ctx.preactivatedSkillIdCalls).not.toContain("app-control");
210
+ });
211
+
212
+ test("drainSingleMessage does not re-add 'app-control' for non-host-proxy interface (slack)", async () => {
213
+ const queue = new MessageQueue();
214
+ const ifCtx: TurnInterfaceContext = {
215
+ userMessageInterface: "slack",
216
+ assistantMessageInterface: "slack",
217
+ };
218
+ queue.push(
219
+ makeQueuedMessage({ requestId: "req-2", turnInterfaceContext: ifCtx }),
220
+ );
221
+ const ctx = makeFakeContext({ queue, turnInterfaceContext: ifCtx });
222
+
223
+ await drainQueue(ctx);
224
+
225
+ expect(ctx.preactivatedSkillIdCalls).not.toContain("computer-use");
226
+ expect(ctx.preactivatedSkillIdCalls).not.toContain("app-control");
227
+ });
228
+
229
+ test("drainBatch re-adds 'app-control' for macOS-sourced batched queue", async () => {
230
+ const queue = new MessageQueue();
231
+ const ifCtx: TurnInterfaceContext = {
232
+ userMessageInterface: "macos",
233
+ assistantMessageInterface: "macos",
234
+ };
235
+ // Two passthrough siblings with matching interface — buildPassthroughBatch
236
+ // groups them into a batch, exercising drainBatch.
237
+ queue.push(
238
+ makeQueuedMessage({
239
+ requestId: "req-2",
240
+ content: "msg-2",
241
+ turnInterfaceContext: ifCtx,
242
+ }),
243
+ );
244
+ queue.push(
245
+ makeQueuedMessage({
246
+ requestId: "req-3",
247
+ content: "msg-3",
248
+ turnInterfaceContext: ifCtx,
249
+ }),
250
+ );
251
+ const ctx = makeFakeContext({ queue, turnInterfaceContext: ifCtx });
252
+
253
+ await drainQueue(ctx);
254
+
255
+ // Batched path mirrors the single-message preactivation block.
256
+ expect(ctx.preactivatedSkillIdCalls).toContain("computer-use");
257
+ expect(ctx.preactivatedSkillIdCalls).toContain("app-control");
258
+ expect(ctx.preactivatedSkillIds).toContain("app-control");
259
+ });
260
+
261
+ test("drainSingleMessage skips 'app-control' re-add when isInteractive=false", async () => {
262
+ const queue = new MessageQueue();
263
+ const ifCtx: TurnInterfaceContext = {
264
+ userMessageInterface: "macos",
265
+ assistantMessageInterface: "macos",
266
+ };
267
+ const qm = makeQueuedMessage({
268
+ requestId: "req-2",
269
+ turnInterfaceContext: ifCtx,
270
+ });
271
+ qm.isInteractive = false;
272
+ queue.push(qm);
273
+ const ctx = makeFakeContext({ queue, turnInterfaceContext: ifCtx });
274
+
275
+ await drainQueue(ctx);
276
+
277
+ // Both branches share the outer `isInteractive !== false` gate, so
278
+ // app-control follows CU's behavior and is skipped for non-interactive
279
+ // turns even on macOS.
280
+ expect(ctx.preactivatedSkillIdCalls).not.toContain("computer-use");
281
+ expect(ctx.preactivatedSkillIdCalls).not.toContain("app-control");
282
+ });
283
+ });
@@ -207,6 +207,12 @@ function createFakeConversation(conversationId: string): Conversation {
207
207
  setHostCuProxy(this: { hostCuProxy: unknown }, proxy: unknown) {
208
208
  this.hostCuProxy = proxy;
209
209
  },
210
+ setHostAppControlProxy(
211
+ this: { hostAppControlProxy: unknown },
212
+ proxy: unknown,
213
+ ) {
214
+ this.hostAppControlProxy = proxy;
215
+ },
210
216
  restoreBrowserProxyAvailability: () => {},
211
217
  addPreactivatedSkillId: () => {},
212
218
  hasAnyPendingConfirmation: () => false,
@@ -172,13 +172,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
172
172
  hasPendingConfirmation: () => false,
173
173
  setHostBrowserProxy: () => {},
174
174
  setHostCuProxy: () => {},
175
+ setHostAppControlProxy: () => {},
175
176
  restoreBrowserProxyAvailability: () => {},
176
177
  addPreactivatedSkillId: () => {},
177
178
  } as unknown as import("../daemon/conversation.js").Conversation;
178
179
 
179
180
  const req = new Request("http://localhost/v1/messages", {
180
181
  method: "POST",
181
- headers: { "Content-Type": "application/json", "x-vellum-actor-principal-id": "test-user", "x-vellum-principal-type": "actor" },
182
+ headers: {
183
+ "Content-Type": "application/json",
184
+ "x-vellum-actor-principal-id": "test-user",
185
+ "x-vellum-principal-type": "actor",
186
+ },
182
187
  body: JSON.stringify({
183
188
  conversationKey: "guardian-conversation-key",
184
189
  content: "05BECB approve",
@@ -188,17 +193,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
188
193
  });
189
194
 
190
195
  const res = await callHandler(
191
- (args) => handleSendMessage(args, {
192
- sendMessageDeps: {
193
- getOrCreateConversation: async () => session,
194
- assistantEventHub: { publish: async () => {} } as any,
195
- resolveAttachments: () => [],
196
- },
197
- }),
196
+ (args) =>
197
+ handleSendMessage(args, {
198
+ sendMessageDeps: {
199
+ getOrCreateConversation: async () => session,
200
+ assistantEventHub: { publish: async () => {} } as any,
201
+ resolveAttachments: () => [],
202
+ },
203
+ }),
198
204
  req,
199
205
  undefined,
200
206
  202,
201
- );
207
+ );
202
208
 
203
209
  expect(res.status).toBe(202);
204
210
  const body = (await res.json()) as {
@@ -250,13 +256,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
250
256
  hasPendingConfirmation: () => false,
251
257
  setHostBrowserProxy: () => {},
252
258
  setHostCuProxy: () => {},
259
+ setHostAppControlProxy: () => {},
253
260
  restoreBrowserProxyAvailability: () => {},
254
261
  addPreactivatedSkillId: () => {},
255
262
  } as unknown as import("../daemon/conversation.js").Conversation;
256
263
 
257
264
  const req = new Request("http://localhost/v1/messages", {
258
265
  method: "POST",
259
- headers: { "Content-Type": "application/json", "x-vellum-actor-principal-id": "test-user", "x-vellum-principal-type": "actor" },
266
+ headers: {
267
+ "Content-Type": "application/json",
268
+ "x-vellum-actor-principal-id": "test-user",
269
+ "x-vellum-principal-type": "actor",
270
+ },
260
271
  body: JSON.stringify({
261
272
  conversationKey: "guardian-conversation-key",
262
273
  content: "hello there",
@@ -266,17 +277,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
266
277
  });
267
278
 
268
279
  const res = await callHandler(
269
- (args) => handleSendMessage(args, {
270
- sendMessageDeps: {
271
- getOrCreateConversation: async () => session,
272
- assistantEventHub: { publish: async () => {} } as any,
273
- resolveAttachments: () => [],
274
- },
275
- }),
280
+ (args) =>
281
+ handleSendMessage(args, {
282
+ sendMessageDeps: {
283
+ getOrCreateConversation: async () => session,
284
+ assistantEventHub: { publish: async () => {} } as any,
285
+ resolveAttachments: () => [],
286
+ },
287
+ }),
276
288
  req,
277
289
  undefined,
278
290
  202,
279
- );
291
+ );
280
292
 
281
293
  expect(res.status).toBe(202);
282
294
  expect(routeGuardianReplyMock).toHaveBeenCalledTimes(1);
@@ -324,13 +336,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
324
336
  requestId === "tool-approval-live",
325
337
  setHostBrowserProxy: () => {},
326
338
  setHostCuProxy: () => {},
339
+ setHostAppControlProxy: () => {},
327
340
  restoreBrowserProxyAvailability: () => {},
328
341
  addPreactivatedSkillId: () => {},
329
342
  } as unknown as import("../daemon/conversation.js").Conversation;
330
343
 
331
344
  const req = new Request("http://localhost/v1/messages", {
332
345
  method: "POST",
333
- headers: { "Content-Type": "application/json", "x-vellum-actor-principal-id": "test-user", "x-vellum-principal-type": "actor" },
346
+ headers: {
347
+ "Content-Type": "application/json",
348
+ "x-vellum-actor-principal-id": "test-user",
349
+ "x-vellum-principal-type": "actor",
350
+ },
334
351
  body: JSON.stringify({
335
352
  conversationKey: "guardian-conversation-key",
336
353
  content: "approve",
@@ -340,17 +357,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
340
357
  });
341
358
 
342
359
  const res = await callHandler(
343
- (args) => handleSendMessage(args, {
344
- sendMessageDeps: {
345
- getOrCreateConversation: async () => session,
346
- assistantEventHub: { publish: async () => {} } as any,
347
- resolveAttachments: () => [],
348
- },
349
- }),
360
+ (args) =>
361
+ handleSendMessage(args, {
362
+ sendMessageDeps: {
363
+ getOrCreateConversation: async () => session,
364
+ assistantEventHub: { publish: async () => {} } as any,
365
+ resolveAttachments: () => [],
366
+ },
367
+ }),
350
368
  req,
351
369
  undefined,
352
370
  202,
353
- );
371
+ );
354
372
 
355
373
  expect(res.status).toBe(202);
356
374
  expect(routeGuardianReplyMock).toHaveBeenCalledTimes(1);
@@ -402,13 +420,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
402
420
  hasPendingConfirmation: (id: string) => id === "tool-req-code-1",
403
421
  setHostBrowserProxy: () => {},
404
422
  setHostCuProxy: () => {},
423
+ setHostAppControlProxy: () => {},
405
424
  restoreBrowserProxyAvailability: () => {},
406
425
  addPreactivatedSkillId: () => {},
407
426
  } as unknown as import("../daemon/conversation.js").Conversation;
408
427
 
409
428
  const req = new Request("http://localhost/v1/messages", {
410
429
  method: "POST",
411
- headers: { "Content-Type": "application/json", "x-vellum-actor-principal-id": "test-user", "x-vellum-principal-type": "actor" },
430
+ headers: {
431
+ "Content-Type": "application/json",
432
+ "x-vellum-actor-principal-id": "test-user",
433
+ "x-vellum-principal-type": "actor",
434
+ },
412
435
  body: JSON.stringify({
413
436
  conversationKey: "guardian-conversation-key",
414
437
  content: "A1B2C3 approve",
@@ -418,17 +441,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
418
441
  });
419
442
 
420
443
  const res = await callHandler(
421
- (args) => handleSendMessage(args, {
422
- sendMessageDeps: {
423
- getOrCreateConversation: async () => session,
424
- assistantEventHub: { publish: async () => {} } as any,
425
- resolveAttachments: () => [],
426
- },
427
- }),
444
+ (args) =>
445
+ handleSendMessage(args, {
446
+ sendMessageDeps: {
447
+ getOrCreateConversation: async () => session,
448
+ assistantEventHub: { publish: async () => {} } as any,
449
+ resolveAttachments: () => [],
450
+ },
451
+ }),
428
452
  req,
429
453
  undefined,
430
454
  202,
431
- );
455
+ );
432
456
 
433
457
  expect(res.status).toBe(202);
434
458
  expect(routeGuardianReplyMock).toHaveBeenCalledTimes(1);
@@ -476,13 +500,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
476
500
  hasPendingConfirmation: (id: string) => id === "pending-reject-1",
477
501
  setHostBrowserProxy: () => {},
478
502
  setHostCuProxy: () => {},
503
+ setHostAppControlProxy: () => {},
479
504
  restoreBrowserProxyAvailability: () => {},
480
505
  addPreactivatedSkillId: () => {},
481
506
  } as unknown as import("../daemon/conversation.js").Conversation;
482
507
 
483
508
  const req = new Request("http://localhost/v1/messages", {
484
509
  method: "POST",
485
- headers: { "Content-Type": "application/json", "x-vellum-actor-principal-id": "test-user", "x-vellum-principal-type": "actor" },
510
+ headers: {
511
+ "Content-Type": "application/json",
512
+ "x-vellum-actor-principal-id": "test-user",
513
+ "x-vellum-principal-type": "actor",
514
+ },
486
515
  body: JSON.stringify({
487
516
  conversationKey: "guardian-conversation-key",
488
517
  content: "reject",
@@ -492,17 +521,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
492
521
  });
493
522
 
494
523
  const res = await callHandler(
495
- (args) => handleSendMessage(args, {
496
- sendMessageDeps: {
497
- getOrCreateConversation: async () => session,
498
- assistantEventHub: { publish: async () => {} } as any,
499
- resolveAttachments: () => [],
500
- },
501
- }),
524
+ (args) =>
525
+ handleSendMessage(args, {
526
+ sendMessageDeps: {
527
+ getOrCreateConversation: async () => session,
528
+ assistantEventHub: { publish: async () => {} } as any,
529
+ resolveAttachments: () => [],
530
+ },
531
+ }),
502
532
  req,
503
533
  undefined,
504
534
  202,
505
- );
535
+ );
506
536
 
507
537
  expect(res.status).toBe(202);
508
538
  expect(routeGuardianReplyMock).toHaveBeenCalledTimes(1);
@@ -544,13 +574,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
544
574
  hasPendingConfirmation: (id: string) => id === "pending-1",
545
575
  setHostBrowserProxy: () => {},
546
576
  setHostCuProxy: () => {},
577
+ setHostAppControlProxy: () => {},
547
578
  restoreBrowserProxyAvailability: () => {},
548
579
  addPreactivatedSkillId: () => {},
549
580
  } as unknown as import("../daemon/conversation.js").Conversation;
550
581
 
551
582
  const req = new Request("http://localhost/v1/messages", {
552
583
  method: "POST",
553
- headers: { "Content-Type": "application/json", "x-vellum-actor-principal-id": "test-user", "x-vellum-principal-type": "actor" },
584
+ headers: {
585
+ "Content-Type": "application/json",
586
+ "x-vellum-actor-principal-id": "test-user",
587
+ "x-vellum-principal-type": "actor",
588
+ },
554
589
  body: JSON.stringify({
555
590
  conversationKey: "guardian-conversation-key",
556
591
  content: "tell me more about this request",
@@ -560,17 +595,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
560
595
  });
561
596
 
562
597
  const res = await callHandler(
563
- (args) => handleSendMessage(args, {
564
- sendMessageDeps: {
565
- getOrCreateConversation: async () => session,
566
- assistantEventHub: { publish: async () => {} } as any,
567
- resolveAttachments: () => [],
568
- },
569
- }),
598
+ (args) =>
599
+ handleSendMessage(args, {
600
+ sendMessageDeps: {
601
+ getOrCreateConversation: async () => session,
602
+ assistantEventHub: { publish: async () => {} } as any,
603
+ resolveAttachments: () => [],
604
+ },
605
+ }),
570
606
  req,
571
607
  undefined,
572
608
  202,
573
- );
609
+ );
574
610
 
575
611
  expect(res.status).toBe(202);
576
612
  expect(routeGuardianReplyMock).toHaveBeenCalledTimes(1);
@@ -614,13 +650,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
614
650
  hasPendingConfirmation: () => false,
615
651
  setHostBrowserProxy: () => {},
616
652
  setHostCuProxy: () => {},
653
+ setHostAppControlProxy: () => {},
617
654
  restoreBrowserProxyAvailability: () => {},
618
655
  addPreactivatedSkillId: () => {},
619
656
  } as unknown as import("../daemon/conversation.js").Conversation;
620
657
 
621
658
  const req = new Request("http://localhost/v1/messages", {
622
659
  method: "POST",
623
- headers: { "Content-Type": "application/json", "x-vellum-actor-principal-id": "test-user", "x-vellum-principal-type": "actor" },
660
+ headers: {
661
+ "Content-Type": "application/json",
662
+ "x-vellum-actor-principal-id": "test-user",
663
+ "x-vellum-principal-type": "actor",
664
+ },
624
665
  body: JSON.stringify({
625
666
  conversationKey: "guardian-conversation-key",
626
667
  content: "no sorry, beats 0 and 3 should be new threads",
@@ -630,14 +671,15 @@ describe("handleSendMessage canonical guardian reply interception", () => {
630
671
  });
631
672
 
632
673
  await callHandler(
633
- (args) => handleSendMessage(args, {
634
- sendMessageDeps: {
635
- getOrCreateConversation: async () => session,
636
- assistantEventHub: { publish: async () => {} } as any,
637
- resolveAttachments: () => [],
638
- },
639
- approvalConversationGenerator: mockGenerator as any,
640
- }),
674
+ (args) =>
675
+ handleSendMessage(args, {
676
+ sendMessageDeps: {
677
+ getOrCreateConversation: async () => session,
678
+ assistantEventHub: { publish: async () => {} } as any,
679
+ resolveAttachments: () => [],
680
+ },
681
+ approvalConversationGenerator: mockGenerator as any,
682
+ }),
641
683
  req,
642
684
  undefined,
643
685
  202,
@@ -685,13 +727,18 @@ describe("handleSendMessage canonical guardian reply interception", () => {
685
727
  hasPendingConfirmation: () => false,
686
728
  setHostBrowserProxy: () => {},
687
729
  setHostCuProxy: () => {},
730
+ setHostAppControlProxy: () => {},
688
731
  restoreBrowserProxyAvailability: () => {},
689
732
  addPreactivatedSkillId: () => {},
690
733
  } as unknown as import("../daemon/conversation.js").Conversation;
691
734
 
692
735
  const req = new Request("http://localhost/v1/messages", {
693
736
  method: "POST",
694
- headers: { "Content-Type": "application/json", "x-vellum-actor-principal-id": "test-user", "x-vellum-principal-type": "actor" },
737
+ headers: {
738
+ "Content-Type": "application/json",
739
+ "x-vellum-actor-principal-id": "test-user",
740
+ "x-vellum-principal-type": "actor",
741
+ },
695
742
  body: JSON.stringify({
696
743
  conversationKey: "guardian-conversation-key",
697
744
  content: "no sorry, beats 0 and 3 should be new threads",
@@ -701,14 +748,15 @@ describe("handleSendMessage canonical guardian reply interception", () => {
701
748
  });
702
749
 
703
750
  await callHandler(
704
- (args) => handleSendMessage(args, {
705
- sendMessageDeps: {
706
- getOrCreateConversation: async () => session,
707
- assistantEventHub: { publish: async () => {} } as any,
708
- resolveAttachments: () => [],
709
- },
710
- approvalConversationGenerator: mockGenerator as any,
711
- }),
751
+ (args) =>
752
+ handleSendMessage(args, {
753
+ sendMessageDeps: {
754
+ getOrCreateConversation: async () => session,
755
+ assistantEventHub: { publish: async () => {} } as any,
756
+ resolveAttachments: () => [],
757
+ },
758
+ approvalConversationGenerator: mockGenerator as any,
759
+ }),
712
760
  req,
713
761
  undefined,
714
762
  202,
@@ -260,6 +260,7 @@ function makeConversation() {
260
260
  hasPendingConfirmation: () => false,
261
261
  setHostBrowserProxy: () => {},
262
262
  setHostCuProxy: () => {},
263
+ setHostAppControlProxy: () => {},
263
264
  addPreactivatedSkillId: () => {},
264
265
  usageStats: {
265
266
  inputTokens: 1000,