@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
@@ -1,427 +0,0 @@
1
- /**
2
- * End-to-end WebSocket dispatch test for the PR10 envelopes:
3
- *
4
- * - `host_browser_event` — the extension forwards every
5
- * `chrome.debugger.onEvent` firing to the runtime over the
6
- * browser-relay WebSocket. This test asserts that the runtime's
7
- * inbound frame handler fans the event out to subscribers of the
8
- * module-level browser-session event bus with the method + params
9
- * + cdpSessionId preserved.
10
- *
11
- * - `host_browser_session_invalidated` — the extension forwards a
12
- * detach notification over the same socket. This test asserts
13
- * that the runtime-side `BrowserSessionManager` evicts any stale
14
- * session whose `targetId` matches the invalidated envelope and
15
- * that the next CDP command against that session throws, forcing
16
- * the owning tool to create a fresh session (which in production
17
- * triggers a reattach on the extension's dispatcher).
18
- *
19
- * Unlike the unit test in `host-browser-event-routes.test.ts`, this
20
- * file stands up the full `RuntimeHttpServer` so the WS upgrade,
21
- * frame parse, dispatch switch, and resolver helpers all run through
22
- * their production code paths. The capability-token transport is
23
- * used so the test does not depend on a valid guardian-bound JWT.
24
- */
25
-
26
- import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
27
-
28
- // ── Module mocks (must be declared before the real imports below) ───
29
-
30
- mock.module("../util/logger.js", () => ({
31
- getLogger: () =>
32
- new Proxy({} as Record<string, unknown>, {
33
- get: () => () => {},
34
- }),
35
- }));
36
-
37
- mock.module("../config/loader.js", () => ({
38
- getConfig: () => ({
39
- ui: {},
40
- model: "test",
41
- provider: "test",
42
- memory: { enabled: false },
43
- rateLimit: { maxRequestsPerMinute: 0 },
44
- secretDetection: { enabled: false },
45
- contextWindow: { maxInputTokens: 200000 },
46
- services: {
47
- inference: {
48
- mode: "your-own",
49
- provider: "anthropic",
50
- model: "claude-opus-4-6",
51
- },
52
- "image-generation": {
53
- mode: "your-own",
54
- provider: "gemini",
55
- model: "gemini-3.1-flash-image-preview",
56
- },
57
- "web-search": { mode: "your-own", provider: "inference-provider-native" },
58
- },
59
- }),
60
- }));
61
-
62
- // ── Real imports (after mocks) ──────────────────────────────────────
63
-
64
- import {
65
- __resetBrowserSessionEventsForTests,
66
- BrowserSessionManager,
67
- createExtensionBackend,
68
- type ForwardedCdpEvent,
69
- onCdpEvent,
70
- } from "../browser-session/index.js";
71
- import { HostBrowserProxy } from "../daemon/host-browser-proxy.js";
72
- import { getDb } from "../memory/db-connection.js";
73
- import { initializeDb } from "../memory/db-init.js";
74
- import { assistantEventHub } from "../runtime/assistant-event-hub.js";
75
- import { mintToken } from "../runtime/auth/token-service.js";
76
- import {
77
- mintHostBrowserCapability,
78
- resetCapabilityTokenSecretForTests,
79
- setCapabilityTokenSecretForTests,
80
- } from "../runtime/capability-tokens.js";
81
- import { RuntimeHttpServer } from "../runtime/http-server.js";
82
-
83
- initializeDb();
84
-
85
- function mintSseToken(guardianId: string): string {
86
- return mintToken({
87
- aud: "vellum-daemon",
88
- sub: `actor:self:${guardianId}`,
89
- scope_profile: "actor_client_v1",
90
- policy_epoch: 1,
91
- ttlSeconds: 3600,
92
- });
93
- }
94
-
95
- // ── Helpers ─────────────────────────────────────────────────────────
96
-
97
- async function waitFor(
98
- predicate: () => boolean,
99
- timeoutMs = 2000,
100
- ): Promise<void> {
101
- const deadline = Date.now() + timeoutMs;
102
- while (!predicate()) {
103
- if (Date.now() > deadline) {
104
- throw new Error(
105
- `waitFor: predicate did not become true within ${timeoutMs}ms`,
106
- );
107
- }
108
- await new Promise((r) => setTimeout(r, 10));
109
- }
110
- }
111
-
112
- async function waitForRegistryEntry(
113
- _guardianId: string,
114
- timeoutMs = 2000,
115
- ): Promise<void> {
116
- await waitFor(
117
- () =>
118
- assistantEventHub.getMostRecentClientByCapability("host_browser") != null,
119
- timeoutMs,
120
- );
121
- }
122
-
123
- // ── Tests ───────────────────────────────────────────────────────────
124
-
125
- describe("host_browser WS event + invalidation e2e", () => {
126
- let server: RuntimeHttpServer;
127
- let port: number;
128
- let runtimeBaseUrl: string;
129
-
130
- beforeEach(async () => {
131
- setCapabilityTokenSecretForTests(Buffer.alloc(32, 0xab));
132
-
133
- const db = getDb();
134
- db.run("DELETE FROM contact_channels");
135
- db.run("DELETE FROM contacts");
136
- HostBrowserProxy.reset();
137
- __resetBrowserSessionEventsForTests();
138
-
139
- // Pick a non-colliding port in the same band as the other
140
- // host-browser e2e tests but offset so parallel runs don't
141
- // step on one another.
142
- port = 19900 + Math.floor(Math.random() * 200);
143
- runtimeBaseUrl = `http://127.0.0.1:${port}`;
144
- server = new RuntimeHttpServer({ port });
145
- await server.start();
146
- });
147
-
148
- afterEach(async () => {
149
- await server?.stop();
150
- HostBrowserProxy.reset();
151
- __resetBrowserSessionEventsForTests();
152
- resetCapabilityTokenSecretForTests();
153
- });
154
-
155
- test("host_browser_event frame fans out to browser-session event bus subscribers", async () => {
156
- const guardianId = `guardian-${crypto.randomUUID()}`;
157
- const { token } = mintHostBrowserCapability(guardianId);
158
-
159
- const { createMockChromeExtension } =
160
- await import("./fixtures/mock-chrome-extension.js");
161
- const mockExt = createMockChromeExtension({
162
- runtimeBaseUrl,
163
- token,
164
- sseToken: mintSseToken(guardianId),
165
- resultTransport: "ws",
166
- });
167
- await mockExt.start();
168
- await mockExt.waitForConnection();
169
- await waitForRegistryEntry(guardianId);
170
-
171
- // Subscribe BEFORE sending the frame so we're guaranteed to see
172
- // the fanout. The subscription is module-level so it survives
173
- // across the WS round-trip naturally.
174
- const observed: ForwardedCdpEvent[] = [];
175
- const unsubscribe = onCdpEvent((event) => observed.push(event));
176
-
177
- mockExt.sendHostBrowserEvent({
178
- method: "Page.frameNavigated",
179
- params: { frame: { id: "frame-1", url: "https://example.com" } },
180
- cdpSessionId: "target-abc",
181
- });
182
-
183
- // The WS dispatch hop is asynchronous — poll until the event
184
- // lands or the test times out.
185
- await waitFor(() => observed.length === 1);
186
-
187
- expect(observed[0].method).toBe("Page.frameNavigated");
188
- expect(observed[0].params).toEqual({
189
- frame: { id: "frame-1", url: "https://example.com" },
190
- });
191
- expect(observed[0].cdpSessionId).toBe("target-abc");
192
-
193
- unsubscribe();
194
- await mockExt.stop();
195
- });
196
-
197
- test("host_browser_event frames with no params are still routed", async () => {
198
- const guardianId = `guardian-${crypto.randomUUID()}`;
199
- const { token } = mintHostBrowserCapability(guardianId);
200
-
201
- const { createMockChromeExtension } =
202
- await import("./fixtures/mock-chrome-extension.js");
203
- const mockExt = createMockChromeExtension({
204
- runtimeBaseUrl,
205
- token,
206
- sseToken: mintSseToken(guardianId),
207
- resultTransport: "ws",
208
- });
209
- await mockExt.start();
210
- await mockExt.waitForConnection();
211
- await waitForRegistryEntry(guardianId);
212
-
213
- const observed: ForwardedCdpEvent[] = [];
214
- const unsubscribe = onCdpEvent((event) => observed.push(event));
215
-
216
- mockExt.sendHostBrowserEvent({ method: "Target.targetDestroyed" });
217
-
218
- await waitFor(() => observed.length === 1);
219
- expect(observed[0].method).toBe("Target.targetDestroyed");
220
- expect(observed[0].params).toBeUndefined();
221
- expect(observed[0].cdpSessionId).toBeUndefined();
222
-
223
- unsubscribe();
224
- await mockExt.stop();
225
- });
226
-
227
- test("host_browser_session_invalidated frame evicts stale sessions and the next command forces reattach", async () => {
228
- const guardianId = `guardian-${crypto.randomUUID()}`;
229
- const { token } = mintHostBrowserCapability(guardianId);
230
-
231
- const { createMockChromeExtension } =
232
- await import("./fixtures/mock-chrome-extension.js");
233
- const mockExt = createMockChromeExtension({
234
- runtimeBaseUrl,
235
- token,
236
- sseToken: mintSseToken(guardianId),
237
- resultTransport: "ws",
238
- });
239
- await mockExt.start();
240
- await mockExt.waitForConnection();
241
- await waitForRegistryEntry(guardianId);
242
-
243
- // Stand up a BrowserSessionManager that mirrors what a tool
244
- // invocation would build. The backend counts dispatch attempts
245
- // so we can assert the first post-invalidation send never
246
- // reached the backend while the second (after reattach) did.
247
- const sent: Array<{ method: string }> = [];
248
- const backend = createExtensionBackend({
249
- isAvailable: () => true,
250
- sendCdp: async (command) => {
251
- sent.push({ method: command.method });
252
- return { result: { ok: true } };
253
- },
254
- dispose: () => {},
255
- });
256
- const manager = new BrowserSessionManager({ backends: [backend] });
257
- const session = manager.createSession();
258
- session.targetId = "tab-77";
259
-
260
- // Fire the invalidation envelope from the extension side.
261
- mockExt.sendSessionInvalidated({
262
- targetId: "tab-77",
263
- reason: "target_closed",
264
- });
265
-
266
- // Wait until the WS dispatch hop lands — `isTargetInvalidated`
267
- // peeks at the registry without consuming the entry, so we can
268
- // poll safely.
269
- const { isTargetInvalidated } =
270
- await import("../browser-session/events.js");
271
- await waitFor(() => isTargetInvalidated("tab-77"));
272
-
273
- // The next send against the invalidated session MUST throw —
274
- // the manager consumes the invalidation flag, evicts the
275
- // session, and rejects the command so the caller can create a
276
- // fresh session (which triggers a reattach on the extension
277
- // side).
278
- await expect(
279
- manager.send(session.id, { method: "Page.navigate" }),
280
- ).rejects.toThrow(/invalidated/);
281
-
282
- // Sanity: the backend never saw the doomed command.
283
- expect(sent).toHaveLength(0);
284
-
285
- // The evicted session is gone — sending again throws
286
- // "Unknown browser session", which is the signal a tool uses
287
- // to rebuild a fresh session.
288
- await expect(
289
- manager.send(session.id, { method: "Page.navigate" }),
290
- ).rejects.toThrow(/Unknown browser session/);
291
-
292
- // Creating a fresh session proves the reattach path works:
293
- // the caller bounces through `createSession` and a subsequent
294
- // send dispatches normally through the backend.
295
- const fresh = manager.createSession();
296
- const result = await manager.send(fresh.id, {
297
- method: "Page.navigate",
298
- });
299
- expect(result.result).toEqual({ ok: true });
300
- expect(sent).toEqual([{ method: "Page.navigate" }]);
301
-
302
- await mockExt.stop();
303
- });
304
-
305
- test("keepalive frames are accepted without closing the socket or producing warnings", async () => {
306
- const guardianId = `guardian-${crypto.randomUUID()}`;
307
- const { token } = mintHostBrowserCapability(guardianId);
308
-
309
- const { createMockChromeExtension } =
310
- await import("./fixtures/mock-chrome-extension.js");
311
- const mockExt = createMockChromeExtension({
312
- runtimeBaseUrl,
313
- token,
314
- sseToken: mintSseToken(guardianId),
315
- resultTransport: "ws",
316
- });
317
- await mockExt.start();
318
- await mockExt.waitForConnection();
319
- await waitForRegistryEntry(guardianId);
320
-
321
- // Send a keepalive frame (the extension sends these periodically
322
- // to prevent the runtime from considering the connection stale).
323
- // The frame may contain extra keys (e.g. timestamp) that the
324
- // runtime should silently ignore (lenient validation).
325
- mockExt.sendRaw(JSON.stringify({ type: "keepalive", ts: Date.now() }));
326
-
327
- // Small delay to let the keepalive frame process.
328
- await new Promise((r) => setTimeout(r, 15));
329
-
330
- // Verify the socket is still alive by sending a normal host_browser_event
331
- // frame after the keepalive — if the socket had been torn down, this
332
- // would never arrive.
333
- const observed: ForwardedCdpEvent[] = [];
334
- const unsubscribe = onCdpEvent((event) => observed.push(event));
335
-
336
- mockExt.sendHostBrowserEvent({ method: "Page.loadEventFired" });
337
- await waitFor(() => observed.length === 1);
338
- expect(observed[0].method).toBe("Page.loadEventFired");
339
-
340
- unsubscribe();
341
- await mockExt.stop();
342
- });
343
-
344
- test("normal host_browser flows still pass after keepalive traffic", async () => {
345
- const guardianId = `guardian-${crypto.randomUUID()}`;
346
- const { token } = mintHostBrowserCapability(guardianId);
347
-
348
- const { createMockChromeExtension } =
349
- await import("./fixtures/mock-chrome-extension.js");
350
- const mockExt = createMockChromeExtension({
351
- runtimeBaseUrl,
352
- token,
353
- sseToken: mintSseToken(guardianId),
354
- resultTransport: "ws",
355
- });
356
- await mockExt.start();
357
- await mockExt.waitForConnection();
358
- await waitForRegistryEntry(guardianId);
359
-
360
- // Simulate a burst of keepalive frames (as would happen during an
361
- // idle period with the extension's alarm-based keepalive ticker).
362
- for (let i = 0; i < 5; i++) {
363
- mockExt.sendRaw(JSON.stringify({ type: "keepalive" }));
364
- }
365
-
366
- // Small delay to let all keepalive frames process.
367
- await new Promise((r) => setTimeout(r, 50));
368
-
369
- // Now send a host_browser_event and verify it still fans out
370
- // correctly — proving keepalive traffic does not interfere with
371
- // normal message processing.
372
- const observed: ForwardedCdpEvent[] = [];
373
- const unsubscribe = onCdpEvent((event) => observed.push(event));
374
-
375
- mockExt.sendHostBrowserEvent({
376
- method: "Network.requestWillBeSent",
377
- params: { requestId: "req-42", url: "https://example.com/api" },
378
- cdpSessionId: "session-xyz",
379
- });
380
-
381
- await waitFor(() => observed.length === 1);
382
- expect(observed[0].method).toBe("Network.requestWillBeSent");
383
- expect(observed[0].params).toEqual({
384
- requestId: "req-42",
385
- url: "https://example.com/api",
386
- });
387
- expect(observed[0].cdpSessionId).toBe("session-xyz");
388
-
389
- unsubscribe();
390
- await mockExt.stop();
391
- });
392
-
393
- test("malformed host_browser_event frames are dropped without tearing down the socket", async () => {
394
- const guardianId = `guardian-${crypto.randomUUID()}`;
395
- const { token } = mintHostBrowserCapability(guardianId);
396
-
397
- const { createMockChromeExtension } =
398
- await import("./fixtures/mock-chrome-extension.js");
399
- const mockExt = createMockChromeExtension({
400
- runtimeBaseUrl,
401
- token,
402
- sseToken: mintSseToken(guardianId),
403
- resultTransport: "ws",
404
- });
405
- await mockExt.start();
406
- await mockExt.waitForConnection();
407
- await waitForRegistryEntry(guardianId);
408
-
409
- const observed: ForwardedCdpEvent[] = [];
410
- const unsubscribe = onCdpEvent((event) => observed.push(event));
411
-
412
- // Send a frame with no method — the resolver must reject it
413
- // and the WS dispatcher must swallow the rejection.
414
- mockExt.sendHostBrowserEvent({ method: "" });
415
-
416
- // Follow up with a valid frame and assert that ONLY the valid
417
- // frame was published — proving the socket survived the bad
418
- // frame and the dispatcher kept processing subsequent messages.
419
- mockExt.sendHostBrowserEvent({ method: "Page.loadEventFired" });
420
-
421
- await waitFor(() => observed.length === 1);
422
- expect(observed[0].method).toBe("Page.loadEventFired");
423
-
424
- unsubscribe();
425
- await mockExt.stop();
426
- });
427
- });
@@ -1,34 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import { twilioAuthHeader, twilioBaseUrl } from "../calls/twilio-rest.js";
4
-
5
- describe("twilioAuthHeader", () => {
6
- test("returns a valid Basic auth header", () => {
7
- const header = twilioAuthHeader("AC_test_sid", "test_token");
8
- const expected =
9
- "Basic " + Buffer.from("AC_test_sid:test_token").toString("base64");
10
- expect(header).toBe(expected);
11
- });
12
-
13
- test("encodes special characters correctly", () => {
14
- const header = twilioAuthHeader("AC_special!@#", "tok$%^&");
15
- const decoded = Buffer.from(
16
- header.replace("Basic ", ""),
17
- "base64",
18
- ).toString();
19
- expect(decoded).toBe("AC_special!@#:tok$%^&");
20
- });
21
- });
22
-
23
- describe("twilioBaseUrl", () => {
24
- test("constructs correct base URL for a given account SID", () => {
25
- const url = twilioBaseUrl("AC_abc123");
26
- expect(url).toBe("https://api.twilio.com/2010-04-01/Accounts/AC_abc123");
27
- });
28
-
29
- test("handles different account SIDs", () => {
30
- const url = twilioBaseUrl("AC_xyz789");
31
- expect(url).toContain("AC_xyz789");
32
- expect(url).toStartWith("https://api.twilio.com/2010-04-01/Accounts/");
33
- });
34
- });
@@ -1,152 +0,0 @@
1
- /**
2
- * Tests for the backup key read/generate helpers. All tests run against a
3
- * temp directory and explicitly pass the key path -- nothing touches the
4
- * real `~/.vellum/` tree or depends on daemon startup state.
5
- */
6
-
7
- import {
8
- mkdirSync,
9
- mkdtempSync,
10
- readdirSync,
11
- rmSync,
12
- statSync,
13
- writeFileSync,
14
- } from "node:fs";
15
- import * as fsPromises from "node:fs/promises";
16
- import { tmpdir } from "node:os";
17
- import { basename, join } from "node:path";
18
- import {
19
- afterEach,
20
- beforeEach,
21
- describe,
22
- expect,
23
- spyOn,
24
- test,
25
- } from "bun:test";
26
-
27
- import { ensureBackupKey, readBackupKey } from "../backup-key.js";
28
-
29
- describe("backup-key", () => {
30
- let root: string;
31
- let keyPath: string;
32
-
33
- beforeEach(() => {
34
- root = mkdtempSync(join(tmpdir(), "vellum-backup-key-"));
35
- // Nest the key file one level down so we can verify that the
36
- // parent directory is created on demand with the expected mode.
37
- keyPath = join(root, "backup", "backup.key");
38
- });
39
-
40
- afterEach(() => {
41
- try {
42
- rmSync(root, { recursive: true, force: true });
43
- } catch {
44
- // best-effort
45
- }
46
- });
47
-
48
- describe("ensureBackupKey", () => {
49
- test("first call generates a fresh 32-byte key and writes it with mode 0600", async () => {
50
- const key = await ensureBackupKey(keyPath);
51
-
52
- expect(key).toBeInstanceOf(Buffer);
53
- expect(key.length).toBe(32);
54
-
55
- const fileMode = statSync(keyPath).mode & 0o777;
56
- expect(fileMode).toBe(0o600);
57
- });
58
-
59
- test("creates the parent directory with mode 0700 when missing", async () => {
60
- await ensureBackupKey(keyPath);
61
-
62
- const parent = join(root, "backup");
63
- const dirMode = statSync(parent).mode & 0o777;
64
- // On some platforms umask can strip bits further, but it must
65
- // never be more permissive than 0o700.
66
- expect(dirMode & ~0o700).toBe(0);
67
- expect(dirMode & 0o700).toBe(0o700);
68
- });
69
-
70
- test("second call returns the same bytes persisted on the first call", async () => {
71
- const first = await ensureBackupKey(keyPath);
72
- const second = await ensureBackupKey(keyPath);
73
- expect(Buffer.compare(first, second)).toBe(0);
74
- });
75
-
76
- test("throws when an existing key file has the wrong size", async () => {
77
- mkdirSync(join(root, "backup"), { recursive: true, mode: 0o700 });
78
- // 16 bytes is half the expected length -- simulate a truncated or
79
- // otherwise corrupt key file.
80
- writeFileSync(keyPath, Buffer.alloc(16, 0xaa), { mode: 0o600 });
81
-
82
- await expect(ensureBackupKey(keyPath)).rejects.toThrow(
83
- /invalid length 16/,
84
- );
85
- });
86
-
87
- test("does not leave any .tmp file behind after a successful write", async () => {
88
- await ensureBackupKey(keyPath);
89
- const parent = join(root, "backup");
90
- const base = basename(keyPath);
91
- const entries = readdirSync(parent);
92
- const tmpEntries = entries.filter((e) => e.startsWith(`${base}.tmp.`));
93
- expect(tmpEntries).toEqual([]);
94
- });
95
-
96
- test("two concurrent callers converge on the same persisted bytes", async () => {
97
- // Race two ensureBackupKey calls. Exactly one caller's bytes win
98
- // the rename; the other caller must re-read the file and return
99
- // those same bytes rather than the key it generated locally.
100
- const [a, b] = await Promise.all([
101
- ensureBackupKey(keyPath),
102
- ensureBackupKey(keyPath),
103
- ]);
104
- expect(Buffer.compare(a, b)).toBe(0);
105
- const onDisk = statSync(keyPath);
106
- expect(onDisk.size).toBe(32);
107
- });
108
-
109
- test("propagates non-ENOENT stat errors instead of treating them as missing", async () => {
110
- // Simulate flaky storage (EIO / ESTALE). If pathExists swallowed
111
- // these, the key file would appear "missing" and be silently
112
- // regenerated, rotating away bytes used to encrypt existing data.
113
- const statSpy = spyOn(fsPromises, "stat").mockImplementation(
114
- async () => {
115
- const err = new Error("simulated EIO") as NodeJS.ErrnoException;
116
- err.code = "EIO";
117
- throw err;
118
- },
119
- );
120
- try {
121
- await expect(ensureBackupKey(keyPath)).rejects.toThrow(
122
- /simulated EIO/,
123
- );
124
- // And the key file must NOT have been created as a side effect.
125
- expect(() => statSync(keyPath)).toThrow();
126
- } finally {
127
- statSpy.mockRestore();
128
- }
129
- });
130
- });
131
-
132
- describe("readBackupKey", () => {
133
- test("returns null when the key file is missing", async () => {
134
- const result = await readBackupKey(keyPath);
135
- expect(result).toBeNull();
136
- });
137
-
138
- test("returns the same bytes that ensureBackupKey wrote", async () => {
139
- const generated = await ensureBackupKey(keyPath);
140
- const read = await readBackupKey(keyPath);
141
- expect(read).not.toBeNull();
142
- expect(Buffer.compare(read!, generated)).toBe(0);
143
- });
144
-
145
- test("throws when the existing key file has the wrong size", async () => {
146
- mkdirSync(join(root, "backup"), { recursive: true, mode: 0o700 });
147
- writeFileSync(keyPath, Buffer.alloc(8, 0xff), { mode: 0o600 });
148
-
149
- await expect(readBackupKey(keyPath)).rejects.toThrow(/invalid length 8/);
150
- });
151
- });
152
- });