@vellumai/assistant 0.12.1-dev.202609141913.5e16607 → 0.12.1-staging.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 (439) hide show
  1. package/AGENTS.md +0 -4
  2. package/ARCHITECTURE.md +12 -11
  3. package/docs/architecture/turn-actor.md +8 -15
  4. package/docs/guardian-request-flow.md +0 -16
  5. package/openapi.yaml +49 -291
  6. package/package.json +1 -1
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  8. package/src/__tests__/app-compiler.test.ts +0 -6
  9. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  10. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  11. package/src/__tests__/app-source-watcher.test.ts +218 -0
  12. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  13. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  14. package/src/__tests__/computer-use-tools.test.ts +5 -17
  15. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  16. package/src/__tests__/config-schema.test.ts +29 -28
  17. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  18. package/src/__tests__/config-watcher.test.ts +8 -23
  19. package/src/__tests__/connection-model-compat.test.ts +2 -2
  20. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  21. package/src/__tests__/conversation-pairing.test.ts +0 -26
  22. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  23. package/src/__tests__/conversation-queue.test.ts +207 -48
  24. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  25. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  26. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  27. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  28. package/src/__tests__/conversation-title-service.test.ts +10 -14
  29. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  30. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  31. package/src/__tests__/credential-record-retire.test.ts +1 -171
  32. package/src/__tests__/credential-routes.test.ts +3 -212
  33. package/src/__tests__/credential-security-invariants.test.ts +2 -4
  34. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  35. package/src/__tests__/document-sync-tags.test.ts +0 -25
  36. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  37. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  38. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  39. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  40. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  41. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  42. package/src/__tests__/host-file-proxy.test.ts +23 -11
  43. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  44. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  45. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  46. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  47. package/src/__tests__/mcp-health-check.test.ts +11 -8
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  49. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  51. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  52. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  53. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  54. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  55. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  56. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  57. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  58. package/src/__tests__/model-intents.test.ts +0 -3
  59. package/src/__tests__/notification-vellum-adapter.test.ts +2 -85
  60. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  61. package/src/__tests__/platform-callback-registration.test.ts +16 -8
  62. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  63. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  64. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  66. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  67. package/src/__tests__/speaker-identification.test.ts +56 -0
  68. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  69. package/src/__tests__/subagent-tools.test.ts +2 -3
  70. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  71. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  72. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  73. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  74. package/src/__tests__/workspace-git-service.test.ts +0 -26
  75. package/src/acp/__tests__/agent-process.test.ts +4 -258
  76. package/src/acp/__tests__/auth-required.test.ts +0 -96
  77. package/src/acp/__tests__/client-handler.test.ts +2 -90
  78. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  79. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  80. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  81. package/src/acp/__tests__/session-manager.test.ts +4 -895
  82. package/src/acp/agent-process.test.ts +1 -4
  83. package/src/acp/agent-process.ts +9 -94
  84. package/src/acp/auth-required.ts +1 -50
  85. package/src/acp/auto-install.test.ts +7 -9
  86. package/src/acp/client-handler.ts +3 -16
  87. package/src/acp/resolve-agent.test.ts +6 -220
  88. package/src/acp/resolve-agent.ts +23 -88
  89. package/src/acp/session-manager.test.ts +3 -196
  90. package/src/acp/session-manager.ts +18 -441
  91. package/src/acp/types.ts +5 -50
  92. package/src/api/events/app-files-changed.ts +3 -4
  93. package/src/api/events/notification-intent.ts +0 -2
  94. package/src/api/index.ts +0 -6
  95. package/src/apps/app-store.ts +1 -1
  96. package/src/bundler/app-compiler.ts +0 -15
  97. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  98. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  99. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  100. package/src/calls/call-constants.ts +4 -24
  101. package/src/calls/call-controller.ts +199 -499
  102. package/src/calls/call-transport.ts +0 -16
  103. package/src/calls/media-stream-output.ts +0 -32
  104. package/src/calls/media-stream-server.ts +30 -82
  105. package/src/calls/media-stream-stt-session.ts +0 -15
  106. package/src/calls/resolve-call-tts-provider.ts +2 -33
  107. package/src/calls/speaker-identification.ts +233 -0
  108. package/src/calls/tts-call-strategy.ts +5 -9
  109. package/src/calls/types.ts +0 -2
  110. package/src/calls/voice-control-protocol.ts +21 -53
  111. package/src/calls/voice-session-bridge.ts +46 -59
  112. package/src/calls/voice-triage-escalate.ts +56 -175
  113. package/src/cli/commands/apps.help.ts +13 -67
  114. package/src/cli/commands/apps.ts +0 -183
  115. package/src/cli/commands/bash.help.ts +13 -8
  116. package/src/cli/commands/bash.ts +25 -7
  117. package/src/cli/commands/channels/index.help.ts +2 -70
  118. package/src/cli/commands/channels/index.ts +3 -16
  119. package/src/cli/commands/credentials.help.ts +2 -6
  120. package/src/cli/commands/credentials.ts +2 -2
  121. package/src/cli/commands/mcp.help.ts +8 -8
  122. package/src/cli/commands/mcp.ts +1 -1
  123. package/src/cli/commands/roadmap.help.ts +1 -5
  124. package/src/cli/commands/roadmap.ts +1 -1
  125. package/src/cli/lib/bundled-marketplace.json +90 -103
  126. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  127. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  128. package/src/config/__tests__/platform-rehydration.test.ts +9 -99
  129. package/src/config/acp-defaults.test.ts +3 -13
  130. package/src/config/acp-defaults.ts +6 -23
  131. package/src/config/acp-schema.ts +2 -30
  132. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  133. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  134. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  135. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  136. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  137. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  138. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  139. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  140. package/src/config/bundled-skills/schedule/SKILL.md +4 -3
  141. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  142. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  143. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  144. package/src/config/default-profile-catalog.ts +1 -1
  145. package/src/config/env.ts +2 -2
  146. package/src/config/feature-flag-registry.json +12 -24
  147. package/src/config/loader.ts +0 -1
  148. package/src/config/memory-v3-gate.test.ts +0 -31
  149. package/src/config/memory-v3-gate.ts +16 -20
  150. package/src/config/platform-rehydration.ts +27 -11
  151. package/src/config/schema.ts +2 -2
  152. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  153. package/src/config/schemas/calls.ts +28 -1
  154. package/src/config/schemas/live-voice.ts +173 -1
  155. package/src/config/schemas/mcp.ts +1 -1
  156. package/src/config/schemas/memory-retrospective.ts +0 -10
  157. package/src/config/schemas/services.ts +0 -8
  158. package/src/config/schemas/skills.ts +14 -0
  159. package/src/credential-execution/process-manager.test.ts +19 -18
  160. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  161. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  162. package/src/daemon/app-source-watcher.ts +218 -0
  163. package/src/daemon/config-watcher.ts +194 -13
  164. package/src/daemon/conversation-process.ts +64 -35
  165. package/src/daemon/conversation-skill-tools.ts +31 -13
  166. package/src/daemon/conversation-store.ts +1 -1
  167. package/src/daemon/conversation-surfaces.ts +8 -10
  168. package/src/daemon/conversation-tool-setup.ts +43 -37
  169. package/src/daemon/conversation.ts +3 -0
  170. package/src/daemon/host-bash-proxy.ts +12 -8
  171. package/src/daemon/host-browser-proxy.ts +1 -15
  172. package/src/daemon/host-file-proxy.ts +10 -8
  173. package/src/daemon/host-transfer-proxy.ts +18 -14
  174. package/src/daemon/lifecycle.ts +5 -0
  175. package/src/daemon/mcp-reload-service.ts +5 -5
  176. package/src/daemon/message-types/sync.ts +0 -1
  177. package/src/daemon/orphan-reaper.ts +1 -1
  178. package/src/daemon/providers-setup.ts +1 -1
  179. package/src/daemon/shutdown-handlers.ts +9 -1
  180. package/src/daemon/skill-memory-refresh.ts +0 -64
  181. package/src/daemon/tool-side-effects.ts +18 -7
  182. package/src/home/__tests__/feed-writer.test.ts +0 -22
  183. package/src/home/feed-writer.ts +2 -11
  184. package/src/hooks/types.ts +0 -33
  185. package/src/inbound/platform-callback-registration.ts +5 -4
  186. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  187. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  188. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  189. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  190. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  191. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  192. package/src/live-voice/live-voice-session.ts +693 -213
  193. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  194. package/src/live-voice/protocol.ts +2 -0
  195. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  196. package/src/mcp/effective-config.ts +3 -3
  197. package/src/mcp/mcp-header-store.ts +45 -29
  198. package/src/mcp/startup.ts +4 -8
  199. package/src/messaging/provider.ts +3 -10
  200. package/src/messaging/providers/callback-routing.ts +0 -18
  201. package/src/messaging/providers/channel-transport.ts +0 -56
  202. package/src/messaging/providers/discord/transport.ts +0 -17
  203. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  204. package/src/messaging/providers/slack/adapter.ts +44 -7
  205. package/src/messaging/providers/slack/transport.ts +0 -16
  206. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  207. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  208. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  209. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  210. package/src/messaging/providers/whatsapp/client.ts +29 -0
  211. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  212. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  213. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  214. package/src/notifications/__tests__/guardian-feed-projection.test.ts +2 -158
  215. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  216. package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
  217. package/src/notifications/adapters/macos.ts +0 -3
  218. package/src/notifications/assistant-reply-producer.ts +3 -8
  219. package/src/notifications/conversation-pairing.ts +16 -36
  220. package/src/notifications/delivered-post-record.ts +1 -11
  221. package/src/notifications/guardian-feed-projection.ts +5 -125
  222. package/src/notifications/home-feed-side-effect.ts +7 -55
  223. package/src/notifications/schedule-result-producer.ts +22 -66
  224. package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
  225. package/src/oauth/byo-connection.test.ts +0 -45
  226. package/src/oauth/byo-connection.ts +6 -36
  227. package/src/oauth/connection-resolver.test.ts +0 -73
  228. package/src/oauth/connection-resolver.ts +2 -95
  229. package/src/oauth/seed-providers.ts +0 -165
  230. package/src/outbound-proxy/certs.ts +5 -3
  231. package/src/permissions/checker.test.ts +6 -11
  232. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  233. package/src/persistence/conversation-crud.ts +63 -71
  234. package/src/persistence/conversation-lineage.ts +4 -12
  235. package/src/persistence/conversation-title-service.ts +26 -9
  236. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  237. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  238. package/src/persistence/message-reads.ts +0 -30
  239. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  240. package/src/persistence/schema/memory-core.ts +0 -5
  241. package/src/platform/client.test.ts +2 -4
  242. package/src/platform/client.ts +10 -2
  243. package/src/plugin-api/constants.ts +0 -2
  244. package/src/plugin-api/index.ts +0 -1
  245. package/src/plugin-api/types.ts +0 -2
  246. package/src/plugins/defaults/index.ts +0 -2
  247. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  248. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  249. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  250. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  251. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  252. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  253. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  254. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  255. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  256. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  257. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  258. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  259. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  260. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  261. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  262. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  263. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  264. package/src/plugins/defaults/memory/startup.ts +1 -6
  265. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  266. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  267. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  268. package/src/plugins/defaults/memory/worker.ts +0 -6
  269. package/src/plugins/mcp-servers.ts +75 -15
  270. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  271. package/src/providers/gemini/client.ts +23 -9
  272. package/src/providers/inference/auth.ts +3 -2
  273. package/src/providers/inference/connection-availability.ts +8 -27
  274. package/src/providers/inference/resolve-auth.ts +4 -22
  275. package/src/providers/model-catalog.ts +0 -34
  276. package/src/providers/model-intents.ts +5 -10
  277. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  278. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  279. package/src/providers/openai/chat-completions-provider.ts +78 -192
  280. package/src/providers/openai/codex-models.ts +4 -0
  281. package/src/runtime/AGENTS.md +0 -2
  282. package/src/runtime/auth/same-actor.ts +3 -38
  283. package/src/runtime/http-server.ts +151 -160
  284. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  286. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  287. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  288. package/src/runtime/routes/acp-routes.test.ts +0 -15
  289. package/src/runtime/routes/acp-routes.ts +9 -72
  290. package/src/runtime/routes/app-management-routes.ts +0 -73
  291. package/src/runtime/routes/app-routes.ts +1 -1
  292. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  293. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  294. package/src/runtime/routes/credential-routes.ts +10 -19
  295. package/src/runtime/routes/debug-bash-routes.ts +173 -0
  296. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  297. package/src/runtime/routes/index.ts +2 -4
  298. package/src/runtime/routes/log-export-routes.ts +0 -2
  299. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  300. package/src/runtime/routes/migration-routes.ts +20 -6
  301. package/src/runtime/routes/platform-routes.ts +15 -18
  302. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  303. package/src/runtime/services/conversation-serializer.ts +3 -16
  304. package/src/runtime/sync/resource-sync-events.ts +1 -2
  305. package/src/schedule/worker.ts +1 -1
  306. package/src/security/ces-rpc-record-backend.ts +0 -7
  307. package/src/subagent/consult-context.ts +25 -12
  308. package/src/tools/acp/list-agents.test.ts +1 -3
  309. package/src/tools/acp/spawn.test.ts +11 -146
  310. package/src/tools/acp/spawn.ts +3 -21
  311. package/src/tools/acp/status.ts +3 -40
  312. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  313. package/src/tools/capability-offer.test.ts +2 -22
  314. package/src/tools/capability-offer.ts +1 -16
  315. package/src/tools/computer-use/definitions.ts +2 -4
  316. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  317. package/src/tools/credentials/metadata-store.ts +0 -100
  318. package/src/tools/host-filesystem/edit.test.ts +1 -2
  319. package/src/tools/host-filesystem/edit.ts +2 -2
  320. package/src/tools/host-filesystem/read.test.ts +1 -2
  321. package/src/tools/host-filesystem/read.ts +2 -2
  322. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  323. package/src/tools/host-filesystem/transfer.ts +2 -2
  324. package/src/tools/host-filesystem/write.test.ts +1 -2
  325. package/src/tools/host-filesystem/write.ts +2 -2
  326. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  327. package/src/tools/host-terminal/host-shell.ts +20 -4
  328. package/src/tools/policy-context.ts +7 -6
  329. package/src/tools/subagent/spawn.ts +8 -5
  330. package/src/tools/watch/watch-retro-report.ts +8 -0
  331. package/src/util/__tests__/language-subtag.test.ts +1 -23
  332. package/src/util/fs-watcher-error.ts +36 -0
  333. package/src/util/language-subtag.ts +0 -16
  334. package/src/util/platform.ts +0 -8
  335. package/src/workspace/git-service.ts +54 -5
  336. package/src/workspace/migrations/registry.ts +0 -6
  337. package/scripts/smoke-desktop-window-theme.ts +0 -186
  338. package/src/__tests__/git-porcelain.test.ts +0 -47
  339. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  340. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  341. package/src/__tests__/resolve-app.test.ts +0 -135
  342. package/src/__tests__/service-account-token.test.ts +0 -247
  343. package/src/__tests__/source-fingerprint.test.ts +0 -184
  344. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  345. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  346. package/src/acp/model-config.test.ts +0 -166
  347. package/src/acp/model-config.ts +0 -138
  348. package/src/api/events/acp-session-model-update.test.ts +0 -88
  349. package/src/api/events/acp-session-model-update.ts +0 -46
  350. package/src/apps/resolve-app.ts +0 -81
  351. package/src/apps/source-fingerprint.ts +0 -266
  352. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  353. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  354. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  355. package/src/calls/barge-in-guard.ts +0 -118
  356. package/src/calls/voice-leg-coordinator.ts +0 -189
  357. package/src/calls/voice-progress-cadence.ts +0 -423
  358. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  359. package/src/cli/commands/__tests__/bash.test.ts +0 -53
  360. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  361. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  362. package/src/cli/commands/channels/send.ts +0 -128
  363. package/src/config/__tests__/platform-identity.test.ts +0 -255
  364. package/src/config/acp-agent-write.ts +0 -43
  365. package/src/config/acp-schema.test.ts +0 -81
  366. package/src/config/platform-identity.ts +0 -201
  367. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  368. package/src/config/schemas/voice.ts +0 -150
  369. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  370. package/src/daemon/app-change-notify.ts +0 -45
  371. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  372. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  373. package/src/desktop/desktop-chrome-policy.ts +0 -42
  374. package/src/desktop/desktop-dependencies.test.ts +0 -102
  375. package/src/desktop/desktop-dependencies.ts +0 -306
  376. package/src/desktop/desktop-feature.test.ts +0 -18
  377. package/src/desktop/desktop-feature.ts +0 -19
  378. package/src/desktop/desktop-panel-config.test.ts +0 -135
  379. package/src/desktop/desktop-panel-config.ts +0 -149
  380. package/src/desktop/desktop-session-manager.test.ts +0 -627
  381. package/src/desktop/desktop-session-manager.ts +0 -814
  382. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  383. package/src/desktop/desktop-stream-bridge.ts +0 -214
  384. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  385. package/src/desktop/desktop-wallpaper.ts +0 -113
  386. package/src/desktop/desktop-window-theme.ts +0 -170
  387. package/src/i18n/AGENTS.md +0 -22
  388. package/src/i18n/__tests__/i18n.test.ts +0 -108
  389. package/src/i18n/index.ts +0 -16
  390. package/src/i18n/locales.ts +0 -93
  391. package/src/i18n/messages.ts +0 -61
  392. package/src/i18n/title.ts +0 -25
  393. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  394. package/src/mcp/spec-schema.ts +0 -111
  395. package/src/mcp/workspace-mcp-config.ts +0 -188
  396. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  397. package/src/persistence/conversation-title-placeholders.ts +0 -23
  398. package/src/persistence/message-cursor.ts +0 -78
  399. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  400. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  401. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  402. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  403. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  404. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  405. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  406. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  407. package/src/providers/gemini-thought-signature.test.ts +0 -63
  408. package/src/providers/gemini-thought-signature.ts +0 -44
  409. package/src/providers/inference/service-account-token.ts +0 -248
  410. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  411. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  412. package/src/providers/openai/google-thought-signature.ts +0 -320
  413. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  414. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  415. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  416. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  417. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  418. package/src/runtime/channel-send.ts +0 -316
  419. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  420. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  421. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  422. package/src/runtime/routes/channel-send-routes.ts +0 -85
  423. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  424. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  425. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  426. package/src/security/ces-http-record-backend.ts +0 -120
  427. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  428. package/src/skills/workspace-skill-md-signature.ts +0 -50
  429. package/src/tools/acp/status.test.ts +0 -94
  430. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
  431. package/src/tools/terminal/sanitized-bash.ts +0 -105
  432. package/src/util/ca-bundle.ts +0 -13
  433. package/src/workspace/git-porcelain.ts +0 -66
  434. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  435. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  436. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  437. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  438. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  439. /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
@@ -1,395 +0,0 @@
1
- import { mkdtempSync, rmSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { afterAll, describe, expect, test } from "bun:test";
5
-
6
- import { newFakeDesktop, newViewer, settle } from "./__tests__/fake-desktop.js";
7
- import { DesktopDependencyInstaller } from "./desktop-dependencies.js";
8
- import {
9
- type DesktopSessionManager,
10
- type DesktopTcpHandlers,
11
- type DesktopTcpSocket,
12
- destroyDesktopSessionManager,
13
- getDesktopSessionManager,
14
- } from "./desktop-session-manager.js";
15
- import { DesktopStreamBridge } from "./desktop-stream-bridge.js";
16
-
17
- const profileDir = mkdtempSync(join(tmpdir(), "desktop-bridge-test-"));
18
- afterAll(() => {
19
- rmSync(profileDir, { recursive: true, force: true });
20
- });
21
-
22
- class FakeWs {
23
- readonly sent: Uint8Array[] = [];
24
- closeCode: number | null = null;
25
- closeReason: string | null = null;
26
- /** What `send` reports back; Bun returns 0 for a dropped frame. */
27
- sendResult: number | null = null;
28
-
29
- send(data: Uint8Array): number {
30
- this.sent.push(data);
31
- return this.sendResult ?? data.byteLength;
32
- }
33
-
34
- close(code?: number, reason?: string): void {
35
- this.closeCode = code ?? null;
36
- this.closeReason = reason ?? null;
37
- }
38
- }
39
-
40
- class FakeTcp implements DesktopTcpSocket {
41
- readonly writes: Uint8Array[] = [];
42
- ended = false;
43
- handlers!: DesktopTcpHandlers;
44
-
45
- write(data: Uint8Array): number {
46
- this.writes.push(data);
47
- return data.byteLength;
48
- }
49
-
50
- end(): void {
51
- this.ended = true;
52
- }
53
- }
54
-
55
- function newBridge(
56
- manager: DesktopSessionManager,
57
- ensureInstalled: () => Promise<void> = async () => {},
58
- isEnabled: () => boolean = () => true,
59
- ) {
60
- const ws = new FakeWs();
61
- const tcp = new FakeTcp();
62
- let release: (() => void) | null = null;
63
- const gate = new Promise<void>((resolve) => {
64
- release = resolve;
65
- });
66
- const bridge = new DesktopStreamBridge(ws, {
67
- manager,
68
- ensureInstalled,
69
- isEnabled,
70
- connect: async (_port, handlers) => {
71
- tcp.handlers = handlers;
72
- await gate;
73
- return tcp;
74
- },
75
- });
76
- return { ws, tcp, bridge, connectNow: () => release!() };
77
- }
78
-
79
- /** Whether the manager's viewer slot is free, observed through acquire. */
80
- function slotIsFree(manager: DesktopSessionManager): boolean {
81
- const { viewer } = newViewer();
82
- const result = manager.acquireViewerSlot(viewer);
83
- if (result.ok) {
84
- manager.releaseViewerSlot(viewer);
85
- }
86
- return result.ok;
87
- }
88
-
89
- describe("DesktopStreamBridge", () => {
90
- test("refuses a disabled desktop before installation or process startup", async () => {
91
- const h = newFakeDesktop({ profileDir });
92
- let installs = 0;
93
- const b = newBridge(
94
- h.manager,
95
- async () => {
96
- installs++;
97
- },
98
- () => false,
99
- );
100
- await b.bridge.start();
101
- expect(b.ws.closeCode).toBe(4008);
102
- expect(installs).toBe(0);
103
- expect(h.spawned).toEqual([]);
104
- expect(slotIsFree(h.manager)).toBe(true);
105
- });
106
-
107
- test("does not start the desktop when disabled during installation", async () => {
108
- const h = newFakeDesktop({ profileDir });
109
- let enabled = true;
110
- const install = Promise.withResolvers<void>();
111
- const b = newBridge(
112
- h.manager,
113
- () => install.promise,
114
- () => enabled,
115
- );
116
- const started = b.bridge.start();
117
- enabled = false;
118
- install.resolve();
119
- await started;
120
- expect(b.ws.closeCode).toBe(4008);
121
- expect(slotIsFree(h.manager)).toBe(true);
122
- expect(b.tcp.handlers).toBeUndefined();
123
- expect(h.spawned).toEqual([]);
124
- });
125
-
126
- for (const direction of ["viewer", "desktop"] as const) {
127
- test(`blocks ${direction} frames when the flag is disabled after connection`, async () => {
128
- const h = newFakeDesktop({ profileDir });
129
- let enabled = true;
130
- const b = newBridge(
131
- h.manager,
132
- async () => {},
133
- () => enabled,
134
- );
135
- b.connectNow();
136
- await b.bridge.start();
137
- enabled = false;
138
- const frame = new Uint8Array([1, 2, 3]);
139
- if (direction === "viewer") {
140
- b.bridge.handleClientFrame(frame);
141
- } else {
142
- b.tcp.handlers.onData(frame);
143
- }
144
- expect(b.ws.closeCode).toBe(4008);
145
- expect(b.ws.sent).toHaveLength(0);
146
- expect(b.tcp.writes).toHaveLength(0);
147
- expect(b.tcp.ended).toBe(true);
148
- expect(slotIsFree(h.manager)).toBe(true);
149
- await h.manager.destroy();
150
- });
151
- }
152
-
153
- test("direct viewers share background setup across a timeout and reconnect", async () => {
154
- const h = newFakeDesktop({ profileDir });
155
- let ready = false;
156
- let installs = 0;
157
- const install = Promise.withResolvers<void>();
158
- const installer = new DesktopDependencyInstaller({
159
- supported: () => true,
160
- ready: () => ready,
161
- install: async () => {
162
- installs++;
163
- await install.promise;
164
- ready = true;
165
- },
166
- notify: async () => {},
167
- });
168
- const ensureInstalled = () => installer.ensureReady();
169
- const first = newBridge(h.manager, ensureInstalled);
170
- const firstStart = first.bridge.start();
171
- await settle();
172
- expect(installs).toBe(1);
173
- expect(h.spawned).toEqual([]);
174
-
175
- const busy = newBridge(h.manager, async () => {
176
- throw new Error("A rejected viewer must not start setup");
177
- });
178
- await busy.bridge.start();
179
- expect(busy.ws.closeCode).toBe(4013);
180
-
181
- first.bridge.handleClose();
182
- expect(slotIsFree(h.manager)).toBe(true);
183
- const retry = newBridge(h.manager, ensureInstalled);
184
- const retryStart = retry.bridge.start();
185
- await settle();
186
- expect(installs).toBe(1);
187
- expect(h.spawned).toEqual([]);
188
-
189
- install.resolve();
190
- retry.connectNow();
191
- await Promise.all([firstStart, retryStart]);
192
- expect(retry.ws.closeCode).toBeNull();
193
- expect(h.spawned.length).toBeGreaterThan(0);
194
- retry.tcp.handlers.onData(new Uint8Array([1, 2, 3]));
195
- expect(retry.ws.sent).toEqual([new Uint8Array([1, 2, 3])]);
196
- expect(first.ws.sent).toEqual([]);
197
- expect(first.tcp.handlers).toBeUndefined();
198
- retry.bridge.handleClose();
199
- });
200
-
201
- test("setup continues after the last viewer closes without starting a desktop", async () => {
202
- const h = newFakeDesktop({ profileDir });
203
- const install = Promise.withResolvers<void>();
204
- const b = newBridge(h.manager, () => install.promise);
205
- const started = b.bridge.start();
206
- b.bridge.handleClose();
207
- install.resolve();
208
- await started;
209
- expect(h.spawned).toEqual([]);
210
- expect(slotIsFree(h.manager)).toBe(true);
211
- });
212
-
213
- test("setup failure releases the slot and permits a successful retry", async () => {
214
- const h = newFakeDesktop({ profileDir });
215
- let ready = false;
216
- let attempts = 0;
217
- const installer = new DesktopDependencyInstaller({
218
- supported: () => true,
219
- ready: () => ready,
220
- install: async () => {
221
- if (++attempts === 1) {
222
- throw new Error("Download failed");
223
- }
224
- ready = true;
225
- },
226
- notify: async () => {},
227
- });
228
- const ensureInstalled = () => installer.ensureReady();
229
- const first = newBridge(h.manager, ensureInstalled);
230
- await first.bridge.start();
231
- expect(first.ws.closeCode).toBe(4011);
232
- expect(slotIsFree(h.manager)).toBe(true);
233
- expect(h.spawned).toEqual([]);
234
-
235
- const retry = newBridge(h.manager, ensureInstalled);
236
- retry.connectNow();
237
- await retry.bridge.start();
238
- expect(attempts).toBe(2);
239
- expect(retry.ws.closeCode).toBeNull();
240
- retry.bridge.handleClose();
241
- });
242
-
243
- test("shutdown during setup closes the viewer without starting a desktop", async () => {
244
- const h = newFakeDesktop({ profileDir });
245
- const install = Promise.withResolvers<void>();
246
- const b = newBridge(h.manager, () => install.promise);
247
- const started = b.bridge.start();
248
- await h.manager.destroy();
249
- expect(b.ws.closeCode).toBe(1001);
250
- install.resolve();
251
- await started;
252
- expect(h.spawned).toEqual([]);
253
- expect(b.tcp.handlers).toBeUndefined();
254
- });
255
-
256
- test("pumps bytes both ways and flushes frames that beat the VNC socket", async () => {
257
- const h = newFakeDesktop({ profileDir });
258
- const b = newBridge(h.manager);
259
- const started = b.bridge.start();
260
- await settle();
261
-
262
- b.bridge.handleClientFrame(new Uint8Array([1, 2, 3]));
263
- b.bridge.handleClientFrame(new Uint8Array([4]).buffer);
264
- b.bridge.handleClientFrame("text frames are not RFB");
265
- expect(b.tcp.writes).toEqual([]);
266
-
267
- b.connectNow();
268
- await started;
269
- expect(b.tcp.writes).toEqual([
270
- new Uint8Array([1, 2, 3]),
271
- new Uint8Array([4]),
272
- ]);
273
-
274
- b.tcp.handlers.onData(new Uint8Array([9, 9]));
275
- expect(b.ws.sent).toEqual([new Uint8Array([9, 9])]);
276
- expect(b.ws.closeCode).toBeNull();
277
- expect(slotIsFree(h.manager)).toBe(false);
278
-
279
- b.bridge.handleClose();
280
- expect(b.tcp.ended).toBe(true);
281
- expect(slotIsFree(h.manager)).toBe(true);
282
- });
283
-
284
- test("a second socket closes 4013 and leaves the first untouched", async () => {
285
- const h = newFakeDesktop({ profileDir });
286
- const first = newBridge(h.manager);
287
- first.connectNow();
288
- await first.bridge.start();
289
-
290
- const second = newBridge(h.manager);
291
- await second.bridge.start();
292
-
293
- expect(second.ws.closeCode).toBe(4013);
294
- expect(first.ws.closeCode).toBeNull();
295
-
296
- second.bridge.handleClose();
297
- expect(slotIsFree(h.manager)).toBe(false);
298
- });
299
-
300
- test("closes 4011 when the desktop fails to start and frees the slot", async () => {
301
- const h = newFakeDesktop({ profileDir });
302
- h.setVncReady(false);
303
- const b = newBridge(h.manager);
304
- await b.bridge.start();
305
-
306
- expect(b.ws.closeCode).toBe(4011);
307
- expect(slotIsFree(h.manager)).toBe(true);
308
- });
309
-
310
- test("closes 4011 and frees the slot when the start rejects with a plain error", async () => {
311
- const h = newFakeDesktop({ profileDir });
312
- h.manager.ensureDesktopRunning = () =>
313
- Promise.reject(new Error("unexpected"));
314
- const b = newBridge(h.manager);
315
- await b.bridge.start();
316
-
317
- expect(b.ws.closeCode).toBe(4011);
318
- expect(b.ws.closeReason).toBe("Desktop failed to start");
319
- expect(slotIsFree(h.manager)).toBe(true);
320
- });
321
-
322
- test("closes 4011 when the desktop dies under the viewer", async () => {
323
- const h = newFakeDesktop({ profileDir });
324
- const b = newBridge(h.manager);
325
- b.connectNow();
326
- await b.bridge.start();
327
- await settle();
328
-
329
- h.child("window-manager").exit(1);
330
- await settle();
331
-
332
- expect(b.ws.closeCode).toBe(4011);
333
- expect(b.ws.closeReason).toContain("window-manager exited");
334
- expect(b.tcp.ended).toBe(true);
335
- });
336
-
337
- test("closes 4011 when the viewer drops a frame", async () => {
338
- const h = newFakeDesktop({ profileDir });
339
- const b = newBridge(h.manager);
340
- b.connectNow();
341
- await b.bridge.start();
342
-
343
- b.ws.sendResult = 0;
344
- b.tcp.handlers.onData(new Uint8Array([1]));
345
-
346
- expect(b.ws.closeCode).toBe(4011);
347
- expect(b.ws.closeReason).toBe("Viewer too slow");
348
- expect(b.tcp.ended).toBe(true);
349
- expect(slotIsFree(h.manager)).toBe(true);
350
- });
351
-
352
- test("closes 1001 for a socket arriving after shutdown began", async () => {
353
- const h = newFakeDesktop({ profileDir });
354
- await h.manager.destroy();
355
- const b = newBridge(h.manager);
356
- await b.bridge.start();
357
-
358
- expect(b.ws.closeCode).toBe(1001);
359
- expect(b.ws.closeReason).toBe("The assistant is shutting down");
360
- expect(h.spawned).toEqual([]);
361
- });
362
-
363
- test("the shared manager is latched by shutdown even when no desktop was ever served", async () => {
364
- await destroyDesktopSessionManager();
365
-
366
- const ws = new FakeWs();
367
- const bridge = new DesktopStreamBridge(ws, {
368
- isEnabled: () => true,
369
- connect: async () => {
370
- throw new Error("must not dial the VNC port");
371
- },
372
- });
373
- await bridge.start();
374
-
375
- expect(ws.closeCode).toBe(1001);
376
- // Nothing is spawned: the start is refused before any binary is looked up.
377
- await expect(
378
- getDesktopSessionManager().ensureDesktopRunning(),
379
- ).rejects.toThrow("Desktop ingress is closed");
380
- });
381
-
382
- test("closes a live viewer with 1001 when the runtime shuts down", async () => {
383
- const h = newFakeDesktop({ profileDir, exitOnTerm: true });
384
- const b = newBridge(h.manager);
385
- b.connectNow();
386
- await b.bridge.start();
387
- await settle();
388
-
389
- await h.manager.destroy();
390
-
391
- expect(b.ws.closeCode).toBe(1001);
392
- expect(b.ws.closeReason).toBe("The assistant is shutting down");
393
- expect(b.tcp.ended).toBe(true);
394
- });
395
- });
@@ -1,214 +0,0 @@
1
- /**
2
- * Pumps raw RFB bytes between one `/v1/desktop/stream` WebSocket and the
3
- * desktop's VNC server on loopback. Every frame is binary RFB data; outcomes
4
- * are signaled with close codes only (see `DESKTOP_CLOSE`).
5
- */
6
-
7
- import { desktopDependencyInstaller } from "./desktop-dependencies.js";
8
- import {
9
- connectLoopback,
10
- DESKTOP_CLOSE,
11
- DESKTOP_VNC_PORT,
12
- type DesktopLoss,
13
- type DesktopSessionManager,
14
- DesktopStartError,
15
- type DesktopTcpHandlers,
16
- type DesktopTcpSocket,
17
- type DesktopViewer,
18
- getDesktopSessionManager,
19
- START_FAILED_LOSS,
20
- } from "./desktop-session-manager.js";
21
-
22
- /** Frames a client may send before the VNC socket is up; RFB handshakes are tiny. */
23
- const MAX_PENDING_BYTES = 1024 * 1024;
24
-
25
- interface DesktopStreamClientSocket {
26
- /** Bun's `ServerWebSocket.send`: `0` means the frame was dropped. */
27
- send(data: Uint8Array): number;
28
- close(code?: number, reason?: string): void;
29
- }
30
-
31
- interface DesktopStreamBridgeOptions {
32
- readonly isEnabled: () => boolean;
33
- readonly manager?: DesktopSessionManager;
34
- readonly ensureInstalled?: () => Promise<void>;
35
- readonly connect?: (
36
- port: number,
37
- handlers: DesktopTcpHandlers,
38
- ) => Promise<DesktopTcpSocket>;
39
- }
40
-
41
- export class DesktopStreamBridge {
42
- private readonly ws: DesktopStreamClientSocket;
43
- private readonly isEnabled: () => boolean;
44
- private readonly manager: DesktopSessionManager;
45
- private readonly ensureInstalled: () => Promise<void>;
46
- private readonly connect: NonNullable<DesktopStreamBridgeOptions["connect"]>;
47
- private readonly viewer: DesktopViewer;
48
-
49
- private tcp: DesktopTcpSocket | null = null;
50
- /** Client bytes waiting on the VNC socket, or on backpressure once it is up. */
51
- private pending: Uint8Array[] = [];
52
- private pendingBytes = 0;
53
- private closed = false;
54
- private ownsSlot = false;
55
-
56
- constructor(
57
- ws: DesktopStreamClientSocket,
58
- options: DesktopStreamBridgeOptions,
59
- ) {
60
- this.ws = ws;
61
- this.isEnabled = options.isEnabled;
62
- this.manager = options.manager ?? getDesktopSessionManager();
63
- this.ensureInstalled =
64
- options.ensureInstalled ??
65
- (() => desktopDependencyInstaller.ensureReady());
66
- this.connect = options.connect ?? connectLoopback;
67
- this.viewer = { onDesktopLost: (loss) => this.lose(loss) };
68
- }
69
-
70
- /** Claim the viewer slot, start the desktop, and dial its VNC port. */
71
- async start(): Promise<void> {
72
- if (!this.checkEnabled()) {
73
- return;
74
- }
75
- const slot = this.manager.acquireViewerSlot(this.viewer);
76
- if (!slot.ok) {
77
- this.lose(slot.loss);
78
- return;
79
- }
80
- this.ownsSlot = true;
81
-
82
- try {
83
- // Direct viewers share setup with the modal, including across reconnects.
84
- await this.ensureInstalled();
85
- if (!this.checkEnabled()) {
86
- return;
87
- }
88
- await this.manager.ensureDesktopRunning();
89
- } catch (err) {
90
- // Usually already closed through onDesktopLost; the error covers a
91
- // start this viewer joined late and never got the callback for.
92
- this.lose(
93
- err instanceof DesktopStartError ? err.loss : START_FAILED_LOSS,
94
- );
95
- return;
96
- }
97
- if (!this.checkEnabled()) {
98
- return;
99
- }
100
-
101
- let tcp: DesktopTcpSocket;
102
- try {
103
- tcp = await this.connect(DESKTOP_VNC_PORT, {
104
- onData: (data) => {
105
- if (!this.checkEnabled()) {
106
- return;
107
- }
108
- if (this.ws.send(data) === 0) {
109
- this.fail(DESKTOP_CLOSE.failed, "Viewer too slow");
110
- }
111
- },
112
- onDrain: () => this.flush(),
113
- onClose: () =>
114
- this.fail(DESKTOP_CLOSE.failed, "Desktop connection closed"),
115
- onError: () =>
116
- this.fail(DESKTOP_CLOSE.failed, "Desktop connection failed"),
117
- });
118
- } catch {
119
- this.fail(DESKTOP_CLOSE.failed, "Desktop connection failed");
120
- return;
121
- }
122
- if (!this.checkEnabled()) {
123
- tcp.end();
124
- return;
125
- }
126
- this.tcp = tcp;
127
- this.flush();
128
- }
129
-
130
- handleClientFrame(message: string | Uint8Array | ArrayBuffer): void {
131
- if (!this.checkEnabled() || typeof message === "string") {
132
- return;
133
- }
134
- const bytes =
135
- message instanceof ArrayBuffer ? new Uint8Array(message) : message;
136
- if (this.pendingBytes + bytes.byteLength > MAX_PENDING_BYTES) {
137
- this.fail(DESKTOP_CLOSE.failed, "Desktop stream backlog exceeded");
138
- return;
139
- }
140
- this.pending.push(bytes);
141
- this.pendingBytes += bytes.byteLength;
142
- this.flush();
143
- }
144
-
145
- /** The client socket closed; the caller has already seen the close frame. */
146
- handleClose(): void {
147
- this.release();
148
- }
149
-
150
- private flush(): void {
151
- if (!this.checkEnabled()) {
152
- return;
153
- }
154
- const tcp = this.tcp;
155
- if (!tcp) {
156
- return;
157
- }
158
- while (this.pending.length > 0) {
159
- const chunk = this.pending[0]!;
160
- const written = tcp.write(chunk);
161
- if (written < 0) {
162
- this.fail(DESKTOP_CLOSE.failed, "Desktop connection closed");
163
- return;
164
- }
165
- this.pendingBytes -= written;
166
- if (written < chunk.byteLength) {
167
- this.pending[0] = chunk.subarray(written);
168
- return;
169
- }
170
- this.pending.shift();
171
- }
172
- }
173
-
174
- private checkEnabled(): boolean {
175
- if (this.closed) {
176
- return false;
177
- }
178
- if (!this.isEnabled()) {
179
- this.fail(
180
- DESKTOP_CLOSE.unavailable,
181
- "Desktop is not available on this assistant",
182
- );
183
- return false;
184
- }
185
- return true;
186
- }
187
-
188
- private lose({ code, reason }: DesktopLoss): void {
189
- this.fail(code, reason);
190
- }
191
-
192
- private fail(code: number, reason: string): void {
193
- if (this.closed) {
194
- return;
195
- }
196
- this.release();
197
- this.ws.close(code, reason);
198
- }
199
-
200
- private release(): void {
201
- if (this.closed) {
202
- return;
203
- }
204
- this.closed = true;
205
- this.pending = [];
206
- this.pendingBytes = 0;
207
- this.tcp?.end();
208
- this.tcp = null;
209
- if (this.ownsSlot) {
210
- this.ownsSlot = false;
211
- this.manager.releaseViewerSlot(this.viewer);
212
- }
213
- }
214
- }