@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,8 +1,8 @@
1
1
  /**
2
2
  * Managed CES contract and wiring tests.
3
3
  *
4
- * Validates the contract surface, behavioral invariants, and feature-flag
5
- * gating for the managed (three-container pod) CES sidecar integration:
4
+ * Validates the contract surface and behavioral invariants for the managed
5
+ * (three-container pod) CES sidecar integration:
6
6
  *
7
7
  * 1. Pod creation contract: well-known path constants match the
8
8
  * stateful_template.yaml K8s spec (read-only mount + private PVC).
@@ -18,25 +18,11 @@
18
18
  * (UpdateManagedCredential, MakeAuthenticatedRequest) validate expected
19
19
  * payloads and reject malformed ones at the contract level.
20
20
  *
21
- * 5. Feature-flag rollback: when the `ces-managed-sidecar` flag is off,
22
- * the process manager falls back to local discovery and never attempts
23
- * the managed sidecar path.
24
- *
25
21
  * All tests use contract schemas and handle parsers to verify behavioral
26
22
  * contracts — no real CES process or socket dependencies are needed.
27
23
  */
28
24
 
29
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
30
-
31
- import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
32
-
33
- beforeEach(() => {
34
- _setOverridesForTesting({});
35
- });
36
-
37
- afterEach(() => {
38
- _setOverridesForTesting({});
39
- });
25
+ import { describe, expect, test } from "bun:test";
40
26
 
41
27
  import {
42
28
  CES_PROTOCOL_VERSION,
@@ -53,26 +39,11 @@ import {
53
39
  UpdateManagedCredentialSchema,
54
40
  } from "@vellumai/service-contracts/credential-rpc";
55
41
 
56
- import type { AssistantConfig } from "../config/schema.js";
57
- import {
58
- isCesManagedSidecarEnabled,
59
- isCesToolsEnabled,
60
- } from "../credential-execution/feature-gates.js";
61
42
  import {
62
43
  CES_ASSISTANT_DATA_READONLY_MOUNT,
63
44
  CES_PRIVATE_DATA_DIR,
64
- type CesProcessManagerConfig,
65
45
  } from "../credential-execution/process-manager.js";
66
46
 
67
- // ---------------------------------------------------------------------------
68
- // Helpers
69
- // ---------------------------------------------------------------------------
70
-
71
- /** Create a minimal AssistantConfig (flag overrides are now set via _setOverridesForTesting). */
72
- function makeConfig(): AssistantConfig {
73
- return {} as AssistantConfig;
74
- }
75
-
76
47
  // ---------------------------------------------------------------------------
77
48
  // Well-known paths contract
78
49
  // ---------------------------------------------------------------------------
@@ -440,102 +411,3 @@ describe("managed OAuth materialization through CES sidecar", () => {
440
411
  }
441
412
  });
442
413
  });
443
-
444
- // ---------------------------------------------------------------------------
445
- // Feature-flag rollback safety
446
- // ---------------------------------------------------------------------------
447
-
448
- describe("feature-flag rollback safety", () => {
449
- test("managed sidecar flag defaults to true (enabled by default)", () => {
450
- const config = makeConfig();
451
- expect(isCesManagedSidecarEnabled(config)).toBe(true);
452
- });
453
-
454
- test("managed sidecar flag can be explicitly enabled", () => {
455
- _setOverridesForTesting({
456
- "ces-managed-sidecar": true,
457
- });
458
- const config = makeConfig();
459
- expect(isCesManagedSidecarEnabled(config)).toBe(true);
460
- });
461
-
462
- test("managed sidecar flag can be explicitly disabled", () => {
463
- _setOverridesForTesting({
464
- "ces-managed-sidecar": false,
465
- });
466
- const config = makeConfig();
467
- expect(isCesManagedSidecarEnabled(config)).toBe(false);
468
- });
469
-
470
- test("enabling managed sidecar does not enable CES tools", () => {
471
- _setOverridesForTesting({
472
- "ces-managed-sidecar": true,
473
- });
474
- const config = makeConfig();
475
- // CES tools flag should remain independently controlled
476
- expect(isCesToolsEnabled(config)).toBe(false);
477
- });
478
-
479
- test("disabling managed sidecar does not affect other CES flags", () => {
480
- _setOverridesForTesting({
481
- "ces-managed-sidecar": false,
482
- "ces-tools": true,
483
- });
484
- const config = makeConfig();
485
- expect(isCesToolsEnabled(config)).toBe(true);
486
- });
487
- });
488
-
489
- // ---------------------------------------------------------------------------
490
- // Process manager config wiring
491
- // ---------------------------------------------------------------------------
492
-
493
- describe("process manager config wiring", () => {
494
- test("CesProcessManagerConfig accepts assistantConfig for flag gating", () => {
495
- _setOverridesForTesting({
496
- "ces-managed-sidecar": true,
497
- });
498
- const config: CesProcessManagerConfig = {
499
- assistantConfig: makeConfig(),
500
- };
501
- expect(config.assistantConfig).toBeDefined();
502
- expect(isCesManagedSidecarEnabled(config.assistantConfig!)).toBe(true);
503
- });
504
-
505
- test("CesProcessManagerConfig requires assistantConfig for feature-flag gate", () => {
506
- const config: CesProcessManagerConfig = {
507
- assistantConfig: makeConfig(),
508
- };
509
- expect(config.assistantConfig).toBeDefined();
510
- });
511
-
512
- test("when flag is off, managed discovery is skipped", () => {
513
- _setOverridesForTesting({
514
- "ces-managed-sidecar": false,
515
- });
516
- const config: CesProcessManagerConfig = {
517
- assistantConfig: makeConfig(),
518
- };
519
- // The managed path should be gated
520
- expect(isCesManagedSidecarEnabled(config.assistantConfig!)).toBe(false);
521
- });
522
- });
523
-
524
- // ---------------------------------------------------------------------------
525
- // Non-CES internal consumers intact
526
- // ---------------------------------------------------------------------------
527
-
528
- describe("non-CES internal consumers intact when flag is off", () => {
529
- test("existing non-agent flows are unaffected by managed sidecar flag", () => {
530
- _setOverridesForTesting({
531
- "ces-managed-sidecar": false,
532
- });
533
- const config = makeConfig();
534
- expect(isCesManagedSidecarEnabled(config)).toBe(false);
535
- });
536
-
537
- test("process manager without config allows managed mode (backward compat)", () => {
538
- const config: CesProcessManagerConfig = {};
539
- expect(config.assistantConfig).toBeUndefined();
540
- });
541
- });
@@ -57,24 +57,17 @@ mock.module("../security/secure-keys.js", () => ({
57
57
  }));
58
58
 
59
59
  mock.module("../cli/lib/daemon-credential-client.js", () => ({
60
- setSecureKeyViaDaemon: async (
61
- type: string,
62
- name: string,
63
- value: string,
64
- ): Promise<boolean> => {
60
+ setSecureKeyViaDaemon: async (type: string, name: string, value: string) => {
65
61
  secureKeyStore.set(normalizeCredentialAccount(type, name), value);
66
- return true;
62
+ return { ok: true };
67
63
  },
68
- deleteSecureKeyViaDaemon: async (
69
- type: string,
70
- name: string,
71
- ): Promise<"deleted" | "not-found" | "error"> => {
64
+ deleteSecureKeyViaDaemon: async (type: string, name: string) => {
72
65
  const key = normalizeCredentialAccount(type, name);
73
66
  if (secureKeyStore.has(key)) {
74
67
  secureKeyStore.delete(key);
75
- return "deleted";
68
+ return { result: "deleted" as const };
76
69
  }
77
- return "not-found";
70
+ return { result: "not-found" as const };
78
71
  },
79
72
  }));
80
73
 
@@ -9,28 +9,27 @@ import { afterEach, describe, expect, mock, test } from "bun:test";
9
9
 
10
10
  const sentMessages: unknown[] = [];
11
11
  let mockHasClient = true; // Default to true for CU unified flow tests
12
+ let mockCuClients: Array<{ clientId: string; capabilities: string[] }> = [
13
+ { clientId: "mock-client-1", capabilities: ["host_cu"] },
14
+ ];
12
15
 
13
16
  mock.module("../runtime/assistant-event-hub.js", () => ({
14
17
  broadcastMessage: (msg: unknown) => sentMessages.push(msg),
15
18
  assistantEventHub: {
16
19
  getMostRecentClientByCapability: (cap: string) =>
17
20
  cap === "host_cu" && mockHasClient ? { id: "mock-client" } : null,
21
+ listClientsByCapability: (cap: string) =>
22
+ cap === "host_cu" ? mockCuClients : [],
23
+ getClientById: (id: string) =>
24
+ mockCuClients.find((c) => c.clientId === id) ?? null,
18
25
  },
19
26
  }));
20
27
 
21
- mock.module("../runtime/pending-interactions.js", () => ({
22
- resolve: () => undefined,
23
- get: () => undefined,
24
- getByKind: () => [],
25
- getByConversation: () => [],
26
- removeByConversation: () => {},
27
- }));
28
-
29
- const { surfaceProxyResolver } = await import(
30
- "../daemon/conversation-surfaces.js"
31
- );
28
+ const { surfaceProxyResolver } =
29
+ await import("../daemon/conversation-surfaces.js");
32
30
  const { HostCuProxy } = await import("../daemon/host-cu-proxy.js");
33
- type SurfaceConversationContext = import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
31
+ type SurfaceConversationContext =
32
+ import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
34
33
 
35
34
  // ---------------------------------------------------------------------------
36
35
  // Test helpers
@@ -73,6 +72,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
73
72
  function setupProxy(maxSteps?: number): SurfaceConversationContext {
74
73
  sentMessages.length = 0;
75
74
  mockHasClient = true;
75
+ mockCuClients = [{ clientId: "mock-client-1", capabilities: ["host_cu"] }];
76
76
  proxy = new HostCuProxy(maxSteps);
77
77
  return buildMockContext(proxy);
78
78
  }
@@ -239,7 +239,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
239
239
 
240
240
  // Simulate client resolving with observation
241
241
  const requestId = sent.requestId as string;
242
- proxy.resolve(requestId, {
242
+ proxy.processObservation(requestId, {
243
243
  axTree: "SubmitButton [1]\nTextField [2]",
244
244
  executionResult: "Clicked element 42",
245
245
  });
@@ -265,7 +265,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
265
265
  expect(sent.type).toBe("host_cu_request");
266
266
  expect(sent.toolName).toBe("computer_use_screenshot");
267
267
 
268
- proxy.resolve(sent.requestId as string, {
268
+ proxy.processObservation(sent.requestId as string, {
269
269
  axTree: "Window [1]",
270
270
  screenshot: "base64screenshot",
271
271
  screenshotWidthPx: 1920,
@@ -302,7 +302,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
302
302
  reasoning: "Type into search box",
303
303
  });
304
304
 
305
- proxy.resolve(sent.requestId as string, {
305
+ proxy.processObservation(sent.requestId as string, {
306
306
  axTree: "SearchBox [1] value='Hello world'",
307
307
  executionResult: "Typed text",
308
308
  });
@@ -326,7 +326,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
326
326
  reasoning: "Check what's on screen",
327
327
  });
328
328
  const sent1 = sentMessages[0] as Record<string, unknown>;
329
- proxy.resolve(sent1.requestId as string, {
329
+ proxy.processObservation(sent1.requestId as string, {
330
330
  axTree: "LoginButton [1]\nUsernameField [2]",
331
331
  });
332
332
  const r1 = await p1;
@@ -340,7 +340,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
340
340
  reasoning: "Click login button",
341
341
  });
342
342
  const sent2 = sentMessages[1] as Record<string, unknown>;
343
- proxy.resolve(sent2.requestId as string, {
343
+ proxy.processObservation(sent2.requestId as string, {
344
344
  axTree: "PasswordField [1]\nSubmitButton [2]",
345
345
  axDiff: "+ PasswordField [1]\n+ SubmitButton [2]\n- LoginButton [1]",
346
346
  executionResult: "Clicked element 1",
@@ -370,6 +370,168 @@ describe("surfaceProxyResolver — CU tool routing", () => {
370
370
  // Step limit enforced through resolver
371
371
  // -------------------------------------------------------------------------
372
372
 
373
+ // -------------------------------------------------------------------------
374
+ // Multi-client ambiguity guard
375
+ // -------------------------------------------------------------------------
376
+
377
+ describe("multi-client ambiguity guard", () => {
378
+ test("returns error when multiple CU clients connected and no target_client_id given", async () => {
379
+ const ctx = setupProxy();
380
+ mockCuClients = [
381
+ { clientId: "client-a", capabilities: ["host_cu"] },
382
+ { clientId: "client-b", capabilities: ["host_cu"] },
383
+ ];
384
+
385
+ const result = await surfaceProxyResolver(ctx, "computer_use_click", {
386
+ element_id: 1,
387
+ reasoning: "click",
388
+ });
389
+
390
+ expect(result.isError).toBe(true);
391
+ expect(result.content).toContain("multiple clients support host_cu");
392
+ expect(result.content).toContain("target_client_id");
393
+ // No message should have been dispatched
394
+ expect(sentMessages).toHaveLength(0);
395
+ });
396
+
397
+ test("proceeds when multiple clients connected and target_client_id is given", async () => {
398
+ const ctx = setupProxy();
399
+ mockCuClients = [
400
+ { clientId: "client-a", capabilities: ["host_cu"] },
401
+ { clientId: "client-b", capabilities: ["host_cu"] },
402
+ ];
403
+
404
+ const resultPromise = surfaceProxyResolver(ctx, "computer_use_click", {
405
+ element_id: 1,
406
+ reasoning: "click",
407
+ target_client_id: "client-a",
408
+ });
409
+
410
+ // Should have dispatched the request
411
+ expect(sentMessages).toHaveLength(1);
412
+ const sent = sentMessages[0] as Record<string, unknown>;
413
+ expect(sent.type).toBe("host_cu_request");
414
+ expect(sent.targetClientId).toBe("client-a");
415
+
416
+ proxy.processObservation(sent.requestId as string, { axTree: "ok" });
417
+ const result = await resultPromise;
418
+ expect(result.isError).toBe(false);
419
+ });
420
+
421
+ test("proceeds normally when exactly one CU client is connected", async () => {
422
+ const ctx = setupProxy();
423
+ // mockCuClients already has 1 entry from setupProxy
424
+
425
+ const resultPromise = surfaceProxyResolver(ctx, "computer_use_click", {
426
+ element_id: 2,
427
+ reasoning: "safe click",
428
+ });
429
+
430
+ expect(sentMessages).toHaveLength(1);
431
+ const sent = sentMessages[0] as Record<string, unknown>;
432
+ expect(sent.type).toBe("host_cu_request");
433
+
434
+ proxy.processObservation(sent.requestId as string, { axTree: "ok" });
435
+ const result = await resultPromise;
436
+ expect(result.isError).toBe(false);
437
+ });
438
+
439
+ test("multi-client guard does not apply to terminal tools (computer_use_done)", async () => {
440
+ const ctx = setupProxy();
441
+ mockCuClients = [
442
+ { clientId: "client-a", capabilities: ["host_cu"] },
443
+ { clientId: "client-b", capabilities: ["host_cu"] },
444
+ ];
445
+
446
+ // Terminal tools short-circuit before the ambiguity check
447
+ const result = await surfaceProxyResolver(ctx, "computer_use_done", {
448
+ summary: "all done",
449
+ });
450
+
451
+ expect(result.isError).toBe(false);
452
+ expect(result.content).toBe("all done");
453
+ expect(sentMessages).toHaveLength(0);
454
+ });
455
+ });
456
+
457
+ // -------------------------------------------------------------------------
458
+ // targetClientId validation (lives at resolver layer so step count and
459
+ // action history are not mutated when validation rejects the request).
460
+ // -------------------------------------------------------------------------
461
+
462
+ describe("targetClientId validation", () => {
463
+ test("returns fast error when targetClientId does not match any connected client", async () => {
464
+ const ctx = setupProxy();
465
+ mockCuClients = [{ clientId: "real-client", capabilities: ["host_cu"] }];
466
+ const stepCountBefore = proxy.stepCount;
467
+
468
+ const result = await surfaceProxyResolver(ctx, "computer_use_click", {
469
+ element_id: 1,
470
+ reasoning: "click",
471
+ target_client_id: "nonexistent-client",
472
+ });
473
+
474
+ expect(result.isError).toBe(true);
475
+ expect(result.content).toContain("nonexistent-client");
476
+ expect(result.content).toContain("host_cu");
477
+ // Critical: validation must run BEFORE recordAction. stepCount and
478
+ // actionHistory must be unchanged when rejection fires — otherwise
479
+ // every invalid target_client_id burns a step and leaves a ghost
480
+ // entry the LLM can reason about.
481
+ expect(proxy.stepCount).toBe(stepCountBefore);
482
+ expect(proxy.actionHistory).toHaveLength(0);
483
+ expect(sentMessages).toHaveLength(0);
484
+ });
485
+
486
+ test("returns fast error when targetClientId points to a client without host_cu capability", async () => {
487
+ const ctx = setupProxy();
488
+ mockCuClients = [
489
+ { clientId: "no-cu-client", capabilities: ["host_bash"] }, // bash, not cu
490
+ ];
491
+ const stepCountBefore = proxy.stepCount;
492
+
493
+ const result = await surfaceProxyResolver(ctx, "computer_use_click", {
494
+ element_id: 1,
495
+ reasoning: "click",
496
+ target_client_id: "no-cu-client",
497
+ });
498
+
499
+ expect(result.isError).toBe(true);
500
+ expect(result.content).toContain("no-cu-client");
501
+ expect(result.content).toContain("host_cu");
502
+ // No step burned, no ghost in history.
503
+ expect(proxy.stepCount).toBe(stepCountBefore);
504
+ expect(proxy.actionHistory).toHaveLength(0);
505
+ expect(sentMessages).toHaveLength(0);
506
+ });
507
+
508
+ test("dispatches and records action when targetClientId is valid", async () => {
509
+ const ctx = setupProxy();
510
+ mockCuClients = [
511
+ { clientId: "cu-client", capabilities: ["host_cu"] },
512
+ { clientId: "client-b", capabilities: ["host_cu"] }, // would otherwise trip ambiguity guard
513
+ ];
514
+
515
+ const resultPromise = surfaceProxyResolver(ctx, "computer_use_click", {
516
+ element_id: 5,
517
+ reasoning: "click",
518
+ target_client_id: "cu-client",
519
+ });
520
+
521
+ expect(sentMessages).toHaveLength(1);
522
+ const sent = sentMessages[0] as Record<string, unknown>;
523
+ expect(sent.type).toBe("host_cu_request");
524
+ expect(sent.targetClientId).toBe("cu-client");
525
+ // recordAction did fire on the success path.
526
+ expect(proxy.stepCount).toBe(1);
527
+ expect(proxy.actionHistory).toHaveLength(1);
528
+
529
+ proxy.processObservation(sent.requestId as string, { axTree: "ok" });
530
+ const result = await resultPromise;
531
+ expect(result.isError).toBe(false);
532
+ });
533
+ });
534
+
373
535
  describe("step limit enforcement through resolver", () => {
374
536
  test("rejects action tools when step limit exceeded", async () => {
375
537
  const ctx = setupProxy(2); // maxSteps = 2
@@ -423,7 +585,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
423
585
  });
424
586
 
425
587
  const sent = sentMessages[0] as Record<string, unknown>;
426
- proxy.resolve(sent.requestId as string, {
588
+ proxy.processObservation(sent.requestId as string, {
427
589
  executionError: "Element 999 not found in AX tree",
428
590
  axTree: "Window [1]",
429
591
  });
@@ -452,7 +614,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
452
614
  expect(sent.reasoning).toBe("Submit the form");
453
615
 
454
616
  // Resolve to avoid unhandled rejection on dispose
455
- proxy.resolve(sent.requestId as string, { axTree: "..." });
617
+ proxy.processObservation(sent.requestId as string, { axTree: "..." });
456
618
  await resultPromise;
457
619
  });
458
620
 
@@ -469,7 +631,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
469
631
 
470
632
  // Resolve to avoid hanging
471
633
  const sent = sentMessages[0] as Record<string, unknown>;
472
- proxy.resolve(sent.requestId as string, { axTree: "..." });
634
+ proxy.processObservation(sent.requestId as string, { axTree: "..." });
473
635
  });
474
636
  });
475
637
 
@@ -515,7 +677,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
515
677
  reasoning: "first",
516
678
  });
517
679
  const s1 = sentMessages[0] as Record<string, unknown>;
518
- proxy.resolve(s1.requestId as string, { axTree: "A" });
680
+ proxy.processObservation(s1.requestId as string, { axTree: "A" });
519
681
  await p1;
520
682
  expect(proxy.stepCount).toBe(1);
521
683
 
@@ -525,7 +687,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
525
687
  reasoning: "second",
526
688
  });
527
689
  const s2 = sentMessages[1] as Record<string, unknown>;
528
- proxy.resolve(s2.requestId as string, { axTree: "B" });
690
+ proxy.processObservation(s2.requestId as string, { axTree: "B" });
529
691
  await p2;
530
692
  expect(proxy.stepCount).toBe(2);
531
693
 
@@ -536,7 +698,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
536
698
  reasoning: "third",
537
699
  });
538
700
  const s3 = sentMessages[2] as Record<string, unknown>;
539
- proxy.resolve(s3.requestId as string, { axTree: "C" });
701
+ proxy.processObservation(s3.requestId as string, { axTree: "C" });
540
702
  await p3;
541
703
  expect(proxy.stepCount).toBe(3);
542
704
 
@@ -1,16 +1,20 @@
1
1
  import { describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  /**
4
- * Mock only the IPC client (to simulate daemon-unreachable) and the logger.
4
+ * Mock the IPC client and logger. The IPC mock returns configurable
5
+ * responses so we can test success, failure, and unreachable paths.
6
+ *
5
7
  * Do NOT mock secure-keys.js — daemon-credential-client falls back to it
6
8
  * for writes/deletes when the daemon is unreachable.
7
9
  */
8
10
 
11
+ let _ipcResponse: { ok: boolean; result?: unknown; error?: string } = {
12
+ ok: false,
13
+ error: "Could not connect to assistant daemon. Is it running?",
14
+ };
15
+
9
16
  mock.module("../ipc/cli-client.js", () => ({
10
- cliIpcCall: async () => ({
11
- ok: false,
12
- error: "Could not connect to assistant daemon. Is it running?",
13
- }),
17
+ cliIpcCall: async () => _ipcResponse,
14
18
  }));
15
19
 
16
20
  mock.module("../util/logger.js", () => ({
@@ -20,22 +24,100 @@ mock.module("../util/logger.js", () => ({
20
24
  }),
21
25
  }));
22
26
 
23
- import { setSecureKeyViaDaemon } from "../cli/lib/daemon-credential-client.js";
27
+ import {
28
+ deleteSecureKeyViaDaemon,
29
+ setSecureKeyViaDaemon,
30
+ } from "../cli/lib/daemon-credential-client.js";
24
31
  import { credentialKey } from "../security/credential-key.js";
25
32
  import { getSecureKeyAsync } from "../security/secure-keys.js";
26
33
 
27
- describe("daemon credential writes (daemon unreachable)", () => {
28
- test("falls back to direct write when daemon is not running", async () => {
29
- const result = await setSecureKeyViaDaemon(
30
- "api_key",
31
- "test-provider",
32
- "test-value",
33
- );
34
- expect(result).toBe(true);
35
-
36
- const readBack = await getSecureKeyAsync(
37
- credentialKey("test-provider", "api_key"),
38
- );
39
- expect(readBack).toBe("test-value");
34
+ describe("daemon credential client", () => {
35
+ describe("set daemon unreachable", () => {
36
+ test("falls back to direct write when daemon is not running", async () => {
37
+ _ipcResponse = {
38
+ ok: false,
39
+ error: "Could not connect to assistant daemon. Is it running?",
40
+ };
41
+
42
+ const result = await setSecureKeyViaDaemon(
43
+ "api_key",
44
+ "test-provider",
45
+ "test-value",
46
+ );
47
+ expect(result.ok).toBe(true);
48
+
49
+ const readBack = await getSecureKeyAsync(
50
+ credentialKey("test-provider", "api_key"),
51
+ );
52
+ expect(readBack).toBe("test-value");
53
+ });
54
+ });
55
+
56
+ describe("set — daemon error", () => {
57
+ test("surfaces daemon error message on IPC failure", async () => {
58
+ _ipcResponse = {
59
+ ok: false,
60
+ error:
61
+ "Failed to store credential in secure storage (backend: ces-rpc)",
62
+ };
63
+
64
+ const result = await setSecureKeyViaDaemon(
65
+ "credential",
66
+ "vellum:webhook_secret",
67
+ "some-value",
68
+ );
69
+ expect(result.ok).toBe(false);
70
+ expect(result.error).toBe(
71
+ "Failed to store credential in secure storage (backend: ces-rpc)",
72
+ );
73
+ });
74
+
75
+ test("surfaces validation error from daemon result", async () => {
76
+ _ipcResponse = {
77
+ ok: true,
78
+ result: {
79
+ success: false,
80
+ error: "API key validation failed: invalid format",
81
+ },
82
+ };
83
+
84
+ const result = await setSecureKeyViaDaemon(
85
+ "api_key",
86
+ "anthropic",
87
+ "bad-key",
88
+ );
89
+ expect(result.ok).toBe(false);
90
+ expect(result.error).toBe("API key validation failed: invalid format");
91
+ });
92
+ });
93
+
94
+ describe("delete — daemon error", () => {
95
+ test("surfaces daemon error message", async () => {
96
+ _ipcResponse = {
97
+ ok: false,
98
+ error: "Credential store is unreachable",
99
+ };
100
+
101
+ const result = await deleteSecureKeyViaDaemon(
102
+ "credential",
103
+ "vellum:temp_cred",
104
+ );
105
+ expect(result.result).toBe("error");
106
+ expect(result.error).toBe("Credential store is unreachable");
107
+ });
108
+
109
+ test("returns not-found for 404 errors", async () => {
110
+ _ipcResponse = {
111
+ ok: false,
112
+ error: "Credential not found (404)",
113
+ };
114
+
115
+ const result = await deleteSecureKeyViaDaemon(
116
+ "credential",
117
+ "vellum:missing",
118
+ );
119
+ expect(result.result).toBe("not-found");
120
+ expect(result.error).toBeUndefined();
121
+ });
40
122
  });
41
123
  });
@@ -36,6 +36,8 @@ describe("schedule_syntax column migration", () => {
36
36
  last_run_at INTEGER,
37
37
  last_status TEXT,
38
38
  retry_count INTEGER NOT NULL DEFAULT 0,
39
+ max_retries INTEGER NOT NULL DEFAULT 3,
40
+ retry_backoff_ms INTEGER NOT NULL DEFAULT 60000,
39
41
  created_by TEXT NOT NULL,
40
42
  mode TEXT NOT NULL DEFAULT 'execute',
41
43
  routing_intent TEXT NOT NULL DEFAULT 'all_channels',
@@ -39,7 +39,6 @@ mock.module("../config/loader.js", () => ({
39
39
  }),
40
40
  loadConfig: () => ({}),
41
41
  loadRawConfig: () => ({}),
42
- saveConfig: () => {},
43
42
  saveRawConfig: () => {},
44
43
  invalidateConfigCache: () => {},
45
44
  getNestedValue: () => undefined,
@@ -43,7 +43,6 @@ mock.module("../config/loader.js", () => ({
43
43
  calls: {
44
44
  enabled: true,
45
45
  provider: "twilio",
46
- webhookBaseUrl: "https://test.example.com",
47
46
  maxDurationSeconds: 3600,
48
47
  userConsultTimeoutSeconds: 120,
49
48
  disclosure: { enabled: false, text: "" },
@@ -16,8 +16,6 @@ mock.module("../calls/twilio-config.js", () => ({
16
16
  accountSid: "AC_test",
17
17
  authToken: "test_token",
18
18
  phoneNumber: "+15550001111",
19
- webhookBaseUrl: "https://test.example.com",
20
- wssBaseUrl: "wss://test.example.com",
21
19
  }),
22
20
  }));
23
21