@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
@@ -115,8 +115,6 @@ describe("resolveSlash command contract", () => {
115
115
  "/context foo",
116
116
  "/models foo",
117
117
  "/status foo",
118
- "/pair foo",
119
- "/pair",
120
118
  "/btw",
121
119
  ];
122
120
 
@@ -138,7 +136,6 @@ describe("classifySlash is a pure classifier matching resolveSlash kinds", () =>
138
136
  input: string;
139
137
  kind: "passthrough" | "compact" | "unknown";
140
138
  }> = [
141
- { input: "/pair", kind: "passthrough" },
142
139
  { input: "/models", kind: "unknown" },
143
140
  { input: "/context", kind: "unknown" },
144
141
  { input: "/status", kind: "unknown" },
@@ -149,7 +146,6 @@ describe("classifySlash is a pure classifier matching resolveSlash kinds", () =>
149
146
  { input: "/opus", kind: "unknown" },
150
147
  { input: "hello", kind: "passthrough" },
151
148
  { input: " /compact ", kind: "compact" },
152
- { input: "/pair foo", kind: "passthrough" },
153
149
  { input: "/models foo", kind: "passthrough" },
154
150
  ];
155
151
 
@@ -0,0 +1,202 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ createSurfaceMutex,
5
+ handleSurfaceAction,
6
+ type SurfaceConversationContext,
7
+ surfaceProxyResolver,
8
+ } from "../daemon/conversation-surfaces.js";
9
+ import type {
10
+ ServerMessage,
11
+ SurfaceData,
12
+ SurfaceType,
13
+ UiSurfaceShow,
14
+ } from "../daemon/message-protocol.js";
15
+ import type { UserMessageAttachment } from "../daemon/message-types/shared.js";
16
+
17
+ interface ProcessMessageCall {
18
+ content: string;
19
+ attachments: UserMessageAttachment[];
20
+ requestId?: string;
21
+ activeSurfaceId?: string;
22
+ displayContent?: string;
23
+ }
24
+
25
+ function makeContext(sent: ServerMessage[] = []): SurfaceConversationContext & {
26
+ processMessageCalls: ProcessMessageCall[];
27
+ } {
28
+ const processMessageCalls: ProcessMessageCall[] = [];
29
+ return {
30
+ conversationId: "conv-1",
31
+ traceEmitter: { emit: () => {} },
32
+ sendToClient: (msg: ServerMessage) => sent.push(msg),
33
+ pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
34
+ lastSurfaceAction: new Map<
35
+ string,
36
+ { actionId: string; data?: Record<string, unknown> }
37
+ >(),
38
+ surfaceState: new Map<
39
+ string,
40
+ {
41
+ surfaceType: SurfaceType;
42
+ data: SurfaceData;
43
+ title?: string;
44
+ actions?: Array<{
45
+ id: string;
46
+ label: string;
47
+ style?: string;
48
+ data?: Record<string, unknown>;
49
+ }>;
50
+ }
51
+ >(),
52
+ surfaceUndoStacks: new Map<string, string[]>(),
53
+ accumulatedSurfaceState: new Map<string, Record<string, unknown>>(),
54
+ surfaceActionRequestIds: new Set<string>(),
55
+ currentTurnSurfaces: [],
56
+ isProcessing: () => false,
57
+ enqueueMessage: () => ({ queued: false, requestId: "req-1" }),
58
+ getQueueDepth: () => 0,
59
+ processMessage: async (
60
+ content: string,
61
+ attachments: UserMessageAttachment[],
62
+ _onEvent: (msg: ServerMessage) => void,
63
+ requestId?: string,
64
+ activeSurfaceId?: string,
65
+ _currentPage?: string,
66
+ _options?: { isInteractive?: boolean },
67
+ displayContent?: string,
68
+ ) => {
69
+ processMessageCalls.push({
70
+ content,
71
+ attachments,
72
+ requestId,
73
+ activeSurfaceId,
74
+ displayContent,
75
+ });
76
+ return "msg-1";
77
+ },
78
+ withSurface: createSurfaceMutex(),
79
+ processMessageCalls,
80
+ };
81
+ }
82
+
83
+ describe("surface action delivery to assistant", () => {
84
+ test("table action button click triggers processMessage with action content", async () => {
85
+ const sent: ServerMessage[] = [];
86
+ const ctx = makeContext(sent);
87
+
88
+ // Step 1: Show a table surface with actions
89
+ const showResult = await surfaceProxyResolver(ctx, "ui_show", {
90
+ surface_type: "table",
91
+ title: "Newsletters",
92
+ data: {
93
+ columns: [
94
+ { id: "sender", label: "Sender" },
95
+ { id: "count", label: "Count" },
96
+ ],
97
+ rows: [
98
+ { id: "row-1", cells: { sender: "Newsletter A", count: "5" } },
99
+ { id: "row-2", cells: { sender: "Newsletter B", count: "3" } },
100
+ ],
101
+ selectionMode: "multiple",
102
+ },
103
+ actions: [
104
+ { id: "archive", label: "Archive", style: "primary" },
105
+ { id: "unsubscribe", label: "Unsubscribe", style: "destructive" },
106
+ ],
107
+ });
108
+
109
+ expect(showResult.isError).toBe(false);
110
+ expect(showResult.yieldToUser).toBe(true);
111
+
112
+ // Verify surface was shown and pending action was registered
113
+ const showMessage = sent.find(
114
+ (msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
115
+ ) as UiSurfaceShow;
116
+ expect(showMessage).toBeDefined();
117
+ const surfaceId = showMessage.surfaceId;
118
+ expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(true);
119
+ expect(ctx.surfaceState.has(surfaceId)).toBe(true);
120
+
121
+ // Step 2: Simulate user clicking "Archive" with selected rows
122
+ const actionData = {
123
+ selectedIds: ["row-1", "row-2"],
124
+ };
125
+
126
+ await handleSurfaceAction(ctx, surfaceId, "archive", actionData);
127
+
128
+ // Step 3: Verify processMessage was called
129
+ expect(ctx.processMessageCalls.length).toBe(1);
130
+ const call = ctx.processMessageCalls[0];
131
+ expect(call.content).toContain("[User action on table surface:");
132
+ expect(call.content).toContain("archive");
133
+ expect(call.content).toContain("selectedIds");
134
+ expect(call.content).toContain("row-1");
135
+ expect(call.content).toContain("row-2");
136
+ expect(call.activeSurfaceId).toBe(surfaceId);
137
+
138
+ // Verify pending action was cleared
139
+ expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(false);
140
+
141
+ // Verify the requestId was tracked as a surface action
142
+ expect(ctx.surfaceActionRequestIds.size).toBe(1);
143
+ });
144
+
145
+ test("table action without selection data still triggers processMessage", async () => {
146
+ const sent: ServerMessage[] = [];
147
+ const ctx = makeContext(sent);
148
+
149
+ // Show table surface
150
+ await surfaceProxyResolver(ctx, "ui_show", {
151
+ surface_type: "table",
152
+ title: "Emails",
153
+ data: {
154
+ columns: [{ id: "subject", label: "Subject" }],
155
+ rows: [{ id: "r1", cells: { subject: "Hello" } }],
156
+ },
157
+ actions: [{ id: "archive", label: "Archive" }],
158
+ });
159
+
160
+ const showMessage = sent.find(
161
+ (msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
162
+ ) as UiSurfaceShow;
163
+ const surfaceId = showMessage.surfaceId;
164
+
165
+ // Click action WITHOUT selection data (data is undefined)
166
+ await handleSurfaceAction(ctx, surfaceId, "archive", undefined);
167
+
168
+ // processMessage must still be called
169
+ expect(ctx.processMessageCalls.length).toBe(1);
170
+ expect(ctx.processMessageCalls[0].content).toContain(
171
+ "[User action on table surface:",
172
+ );
173
+ });
174
+
175
+ test("action on history-restored surface (no pending) still processes", async () => {
176
+ const sent: ServerMessage[] = [];
177
+ const ctx = makeContext(sent);
178
+
179
+ // Simulate a history-restored surface: surfaceState exists, but
180
+ // pendingSurfaceActions does NOT have an entry.
181
+ ctx.surfaceState.set("hist-surface-1", {
182
+ surfaceType: "table",
183
+ data: {
184
+ columns: [{ id: "col", label: "Col" }],
185
+ rows: [],
186
+ } as unknown as SurfaceData,
187
+ title: "History Table",
188
+ actions: [{ id: "delete", label: "Delete" }],
189
+ });
190
+
191
+ // Click the action — should go through the history-restored path
192
+ await handleSurfaceAction(ctx, "hist-surface-1", "delete", {
193
+ selectedIds: ["row-1"],
194
+ });
195
+
196
+ // processMessage should still be called
197
+ expect(ctx.processMessageCalls.length).toBe(1);
198
+ expect(ctx.processMessageCalls[0].content).toContain(
199
+ "[User action on app:",
200
+ );
201
+ });
202
+ });
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Tests for the surfaceProxyResolver's app_control_* dispatch branch.
3
+ *
4
+ * Mirrors the structure of cu-unified-flow.test.ts but exercises the
5
+ * sibling branch added for app-control: unavailability when no proxy is
6
+ * attached, end-to-end dispatch through HostAppControlProxy.request, and
7
+ * the local short-circuit for app_control_stop (no client round-trip).
8
+ */
9
+
10
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
11
+
12
+ const sentMessages: unknown[] = [];
13
+ let mockHasClient = true;
14
+
15
+ mock.module("../runtime/assistant-event-hub.js", () => ({
16
+ broadcastMessage: (msg: unknown) => sentMessages.push(msg),
17
+ assistantEventHub: {
18
+ getMostRecentClientByCapability: (cap: string) =>
19
+ cap === "host_app_control" && mockHasClient
20
+ ? { id: "mock-client" }
21
+ : null,
22
+ },
23
+ }));
24
+
25
+ mock.module("../runtime/pending-interactions.js", () => ({
26
+ register: () => undefined,
27
+ resolve: () => undefined,
28
+ get: () => undefined,
29
+ getByKind: () => [],
30
+ getByConversation: () => [],
31
+ removeByConversation: () => {},
32
+ }));
33
+
34
+ const { surfaceProxyResolver } =
35
+ await import("../daemon/conversation-surfaces.js");
36
+ const { HostAppControlProxy, _resetActiveAppControlConversationId } =
37
+ await import("../daemon/host-app-control-proxy.js");
38
+ type SurfaceConversationContext =
39
+ import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Test helpers
43
+ // ---------------------------------------------------------------------------
44
+
45
+ /**
46
+ * Build a minimal SurfaceConversationContext with an optional
47
+ * hostAppControlProxy. Only the fields required by the app-control routing
48
+ * path are populated.
49
+ */
50
+ function buildMockContext(
51
+ hostAppControlProxy?: InstanceType<typeof HostAppControlProxy>,
52
+ conversationId = "test-session",
53
+ setHostAppControlProxy?: (
54
+ proxy: InstanceType<typeof HostAppControlProxy> | undefined,
55
+ ) => void,
56
+ ): SurfaceConversationContext {
57
+ return {
58
+ conversationId,
59
+ traceEmitter: { emit: () => {} },
60
+ sendToClient: () => {},
61
+ pendingSurfaceActions: new Map(),
62
+ lastSurfaceAction: new Map(),
63
+ surfaceState: new Map(),
64
+ surfaceUndoStacks: new Map(),
65
+ accumulatedSurfaceState: new Map(),
66
+ surfaceActionRequestIds: new Set(),
67
+ currentTurnSurfaces: [],
68
+ hostAppControlProxy,
69
+ setHostAppControlProxy,
70
+ isProcessing: () => false,
71
+ enqueueMessage: () => ({ queued: false, requestId: "r1" }),
72
+ getQueueDepth: () => 0,
73
+ processMessage: async () => "",
74
+ withSurface: async (_id, fn) => fn(),
75
+ };
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Tests
80
+ // ---------------------------------------------------------------------------
81
+
82
+ describe("surfaceProxyResolver — app-control tool routing", () => {
83
+ beforeEach(() => {
84
+ sentMessages.length = 0;
85
+ mockHasClient = true;
86
+ _resetActiveAppControlConversationId();
87
+ });
88
+
89
+ afterEach(() => {
90
+ _resetActiveAppControlConversationId();
91
+ });
92
+
93
+ // -------------------------------------------------------------------------
94
+ // Unavailability
95
+ // -------------------------------------------------------------------------
96
+
97
+ describe("no app-control proxy attached", () => {
98
+ test("returns isError result when ctx.hostAppControlProxy is undefined", async () => {
99
+ const ctx = buildMockContext(/* no proxy */);
100
+
101
+ const result = await surfaceProxyResolver(ctx, "app_control_observe", {
102
+ tool: "observe",
103
+ app: "com.example.editor",
104
+ });
105
+
106
+ expect(result.isError).toBe(true);
107
+ expect(result.content).toContain("not available");
108
+ expect(result.content).toContain("app-control");
109
+ // No envelope dispatched.
110
+ expect(sentMessages).toHaveLength(0);
111
+ });
112
+
113
+ test("returns isError when proxy exists but no client is connected", async () => {
114
+ mockHasClient = false;
115
+ const proxy = new HostAppControlProxy("conv-1");
116
+ const ctx = buildMockContext(proxy);
117
+
118
+ const result = await surfaceProxyResolver(ctx, "app_control_observe", {
119
+ tool: "observe",
120
+ app: "com.example.editor",
121
+ });
122
+
123
+ expect(result.isError).toBe(true);
124
+ expect(result.content).toContain("not available");
125
+ expect(sentMessages).toHaveLength(0);
126
+
127
+ proxy.dispose();
128
+ });
129
+
130
+ test("returns isError for app_control_stop when no proxy is attached", async () => {
131
+ const ctx = buildMockContext();
132
+
133
+ const result = await surfaceProxyResolver(ctx, "app_control_stop", {
134
+ tool: "stop",
135
+ });
136
+
137
+ expect(result.isError).toBe(true);
138
+ expect(result.content).toContain("not available");
139
+ expect(sentMessages).toHaveLength(0);
140
+ });
141
+ });
142
+
143
+ // -------------------------------------------------------------------------
144
+ // Dispatch through proxy.request
145
+ // -------------------------------------------------------------------------
146
+
147
+ describe("non-stop tools dispatch through proxy.request", () => {
148
+ test("app_control_observe routes through proxy and returns observation", async () => {
149
+ const proxy = new HostAppControlProxy("conv-1");
150
+ const ctx = buildMockContext(proxy, "conv-1");
151
+
152
+ const resultPromise = surfaceProxyResolver(ctx, "app_control_observe", {
153
+ tool: "observe",
154
+ app: "com.example.editor",
155
+ });
156
+
157
+ // The proxy fired exactly one host_app_control_request envelope.
158
+ expect(sentMessages).toHaveLength(1);
159
+ const sent = sentMessages[0] as Record<string, unknown>;
160
+ expect(sent.type).toBe("host_app_control_request");
161
+ expect(sent.toolName).toBe("app_control_observe");
162
+ expect(sent.conversationId).toBe("conv-1");
163
+ expect(sent.input).toEqual({
164
+ tool: "observe",
165
+ app: "com.example.editor",
166
+ });
167
+
168
+ const requestId = sent.requestId as string;
169
+ proxy.resolve(requestId, {
170
+ requestId: "ignored-by-proxy",
171
+ state: "running",
172
+ executionResult: "Window observed",
173
+ });
174
+
175
+ const result = await resultPromise;
176
+ expect(result.isError).toBe(false);
177
+ expect(result.content).toContain("State: running");
178
+ expect(result.content).toContain("Window observed");
179
+
180
+ proxy.dispose();
181
+ });
182
+ });
183
+
184
+ // -------------------------------------------------------------------------
185
+ // Local short-circuit on app_control_stop
186
+ // -------------------------------------------------------------------------
187
+
188
+ describe("app_control_stop short-circuits locally", () => {
189
+ test("calls proxy.dispose() and returns a stopped summary without a client round-trip", async () => {
190
+ const proxy = new HostAppControlProxy("conv-1");
191
+ const ctx = buildMockContext(proxy);
192
+
193
+ let disposeCalls = 0;
194
+ const realDispose = proxy.dispose.bind(proxy);
195
+ proxy.dispose = () => {
196
+ disposeCalls++;
197
+ realDispose();
198
+ };
199
+
200
+ let requestCalls = 0;
201
+ const realRequest = proxy.request.bind(proxy);
202
+ proxy.request = (...args) => {
203
+ requestCalls++;
204
+ return realRequest(...args);
205
+ };
206
+
207
+ const result = await surfaceProxyResolver(ctx, "app_control_stop", {
208
+ tool: "stop",
209
+ });
210
+
211
+ expect(result.isError).toBe(false);
212
+ expect(result.content.toLowerCase()).toContain("stopped");
213
+ expect(disposeCalls).toBe(1);
214
+ expect(requestCalls).toBe(0);
215
+ // No envelope dispatched for the local short-circuit.
216
+ expect(sentMessages).toHaveLength(0);
217
+ });
218
+
219
+ test("clears the conversation reference via setHostAppControlProxy(undefined) when the setter is provided", async () => {
220
+ const proxy = new HostAppControlProxy("conv-1");
221
+
222
+ // Capture how the resolver clears the proxy reference. The setter
223
+ // mirrors Conversation.setHostAppControlProxy: dispose the existing
224
+ // proxy when transitioning to undefined.
225
+ const setterCalls: Array<unknown> = [];
226
+ let attached: InstanceType<typeof HostAppControlProxy> | undefined =
227
+ proxy;
228
+ const setter = (
229
+ next: InstanceType<typeof HostAppControlProxy> | undefined,
230
+ ) => {
231
+ setterCalls.push(next);
232
+ if (attached && attached !== next) attached.dispose();
233
+ attached = next;
234
+ };
235
+
236
+ const ctx = buildMockContext(proxy, "conv-1", setter);
237
+
238
+ const result = await surfaceProxyResolver(ctx, "app_control_stop", {
239
+ tool: "stop",
240
+ });
241
+
242
+ expect(result.isError).toBe(false);
243
+ // The resolver invoked the setter with undefined exactly once.
244
+ expect(setterCalls).toEqual([undefined]);
245
+ expect(attached).toBeUndefined();
246
+ });
247
+ });
248
+
249
+ // -------------------------------------------------------------------------
250
+ // Discriminator injection (Gap A)
251
+ // -------------------------------------------------------------------------
252
+
253
+ describe("tool discriminator injection", () => {
254
+ test("injects `tool` derived from toolName when the agent input omits it", async () => {
255
+ const proxy = new HostAppControlProxy("conv-1");
256
+ const ctx = buildMockContext(proxy, "conv-1");
257
+
258
+ // Agent inputs do not carry the discriminator — the resolver has to
259
+ // synthesize it from `toolName` ("app_control_observe" → "observe")
260
+ // before forwarding to the proxy / desktop client.
261
+ const resultPromise = surfaceProxyResolver(ctx, "app_control_observe", {
262
+ app: "com.example.editor",
263
+ });
264
+
265
+ expect(sentMessages).toHaveLength(1);
266
+ const sent = sentMessages[0] as Record<string, unknown>;
267
+ expect(sent.input).toEqual({
268
+ tool: "observe",
269
+ app: "com.example.editor",
270
+ });
271
+
272
+ const requestId = sent.requestId as string;
273
+ proxy.resolve(requestId, {
274
+ requestId: "ignored-by-proxy",
275
+ state: "running",
276
+ });
277
+ await resultPromise;
278
+
279
+ proxy.dispose();
280
+ });
281
+
282
+ test('injects `tool: "start"` so the singleton-lock guard fires', async () => {
283
+ // Establish a lock owned by conv-other.
284
+ const ownerProxy = new HostAppControlProxy("conv-other");
285
+ const ownerCtrl = new AbortController();
286
+ const ownerPromise = ownerProxy.request(
287
+ "app_control_start",
288
+ { tool: "start", app: "com.example.editor" },
289
+ "conv-other",
290
+ ownerCtrl.signal,
291
+ );
292
+ const ownerSent = sentMessages[0] as Record<string, unknown>;
293
+ ownerProxy.resolve(ownerSent.requestId as string, {
294
+ requestId: "ignored-by-proxy",
295
+ state: "running",
296
+ });
297
+ await ownerPromise;
298
+ sentMessages.length = 0;
299
+
300
+ // conv-1 attempts to start without a discriminator in its input. The
301
+ // resolver must inject `tool: "start"`, which causes the proxy's
302
+ // singleton-lock guard to fire and reject without dispatching.
303
+ const proxy = new HostAppControlProxy("conv-1");
304
+ const ctx = buildMockContext(proxy, "conv-1");
305
+ const result = await surfaceProxyResolver(ctx, "app_control_start", {
306
+ app: "com.example.editor",
307
+ });
308
+
309
+ expect(result.isError).toBe(true);
310
+ expect(result.content).toContain("conv-other");
311
+ expect(sentMessages).toHaveLength(0); // No envelope dispatched.
312
+
313
+ proxy.dispose();
314
+ ownerProxy.dispose();
315
+ });
316
+ });
317
+ });
@@ -17,12 +17,10 @@ import {
17
17
  import type { AssistantConfig } from "../config/schema.js";
18
18
  import {
19
19
  CES_GRANT_AUDIT_FLAG_KEY,
20
- CES_MANAGED_SIDECAR_FLAG_KEY,
21
20
  CES_SECURE_INSTALL_FLAG_KEY,
22
21
  CES_SHELL_LOCKDOWN_FLAG_KEY,
23
22
  CES_TOOLS_FLAG_KEY,
24
23
  isCesGrantAuditEnabled,
25
- isCesManagedSidecarEnabled,
26
24
  isCesSecureInstallEnabled,
27
25
  isCesShellLockdownEnabled,
28
26
  isCesToolsEnabled,
@@ -51,7 +49,6 @@ const ALL_CES_FLAG_KEYS = [
51
49
  CES_SHELL_LOCKDOWN_FLAG_KEY,
52
50
  CES_SECURE_INSTALL_FLAG_KEY,
53
51
  CES_GRANT_AUDIT_FLAG_KEY,
54
- CES_MANAGED_SIDECAR_FLAG_KEY,
55
52
  ] as const;
56
53
 
57
54
  /** All CES predicate functions paired with their flag keys and expected defaults. */
@@ -80,12 +77,6 @@ const ALL_CES_PREDICATES = [
80
77
  key: CES_GRANT_AUDIT_FLAG_KEY,
81
78
  defaultEnabled: false,
82
79
  },
83
- {
84
- name: "isCesManagedSidecarEnabled",
85
- fn: isCesManagedSidecarEnabled,
86
- key: CES_MANAGED_SIDECAR_FLAG_KEY,
87
- defaultEnabled: true,
88
- },
89
80
  ] as const;
90
81
 
91
82
  // ---------------------------------------------------------------------------
@@ -180,7 +171,7 @@ describe("CES flags do not affect unrelated flags", () => {
180
171
  expect(isAssistantFeatureFlagEnabled("browser", config)).toBe(true);
181
172
  });
182
173
 
183
- test("enabling all CES flags does not change sounds flag (defaultEnabled: true)", () => {
174
+ test("enabling all CES flags does not change unrelated default-open flags", () => {
184
175
  const overrides: Record<string, boolean> = {};
185
176
  for (const key of ALL_CES_FLAG_KEYS) {
186
177
  overrides[key] = true;
@@ -188,7 +179,9 @@ describe("CES flags do not affect unrelated flags", () => {
188
179
  _setOverridesForTesting(overrides);
189
180
  const config = makeConfig();
190
181
 
191
- // sounds defaults to true in the registry and should stay true
192
- expect(isAssistantFeatureFlagEnabled("sounds", config)).toBe(true);
182
+ // Unknown flags default open unless explicitly overridden.
183
+ expect(
184
+ isAssistantFeatureFlagEnabled("unrelated-default-open", config),
185
+ ).toBe(true);
193
186
  });
194
187
  });