@vellumai/assistant 0.12.1-staging.1 → 0.12.1

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 (404) 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/openapi.yaml +1 -291
  5. package/package.json +1 -1
  6. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  7. package/src/__tests__/app-compiler.test.ts +0 -6
  8. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  9. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  10. package/src/__tests__/app-source-watcher.test.ts +218 -0
  11. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  12. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  13. package/src/__tests__/computer-use-tools.test.ts +5 -17
  14. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  15. package/src/__tests__/config-schema.test.ts +29 -28
  16. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  17. package/src/__tests__/config-watcher.test.ts +8 -23
  18. package/src/__tests__/connection-model-compat.test.ts +2 -2
  19. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  20. package/src/__tests__/conversation-pairing.test.ts +0 -26
  21. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  22. package/src/__tests__/conversation-queue.test.ts +207 -48
  23. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  24. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  25. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  26. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  27. package/src/__tests__/conversation-title-service.test.ts +10 -14
  28. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  29. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  30. package/src/__tests__/credential-record-retire.test.ts +1 -171
  31. package/src/__tests__/credential-routes.test.ts +3 -212
  32. package/src/__tests__/credential-security-invariants.test.ts +1 -2
  33. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  34. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  35. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  36. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  37. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  38. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  39. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  40. package/src/__tests__/host-file-proxy.test.ts +23 -11
  41. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  42. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  43. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  44. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  45. package/src/__tests__/mcp-health-check.test.ts +11 -8
  46. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  47. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  48. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  49. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  51. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  52. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  53. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  54. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  55. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  56. package/src/__tests__/model-intents.test.ts +0 -3
  57. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  58. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  59. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  60. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  61. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  62. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  63. package/src/__tests__/speaker-identification.test.ts +56 -0
  64. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  65. package/src/__tests__/subagent-tools.test.ts +2 -3
  66. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  67. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  68. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  69. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  70. package/src/__tests__/workspace-git-service.test.ts +0 -26
  71. package/src/acp/__tests__/agent-process.test.ts +4 -258
  72. package/src/acp/__tests__/auth-required.test.ts +0 -96
  73. package/src/acp/__tests__/client-handler.test.ts +2 -90
  74. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  75. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  76. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  77. package/src/acp/__tests__/session-manager.test.ts +4 -895
  78. package/src/acp/agent-process.test.ts +1 -4
  79. package/src/acp/agent-process.ts +9 -94
  80. package/src/acp/auth-required.ts +1 -50
  81. package/src/acp/auto-install.test.ts +7 -9
  82. package/src/acp/client-handler.ts +3 -16
  83. package/src/acp/resolve-agent.test.ts +6 -220
  84. package/src/acp/resolve-agent.ts +23 -88
  85. package/src/acp/session-manager.test.ts +3 -196
  86. package/src/acp/session-manager.ts +18 -441
  87. package/src/acp/types.ts +5 -50
  88. package/src/api/events/app-files-changed.ts +3 -4
  89. package/src/api/index.ts +0 -6
  90. package/src/apps/app-store.ts +1 -1
  91. package/src/bundler/app-compiler.ts +0 -15
  92. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  93. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  94. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  95. package/src/calls/call-constants.ts +4 -24
  96. package/src/calls/call-controller.ts +199 -499
  97. package/src/calls/call-transport.ts +0 -16
  98. package/src/calls/media-stream-output.ts +0 -32
  99. package/src/calls/media-stream-server.ts +30 -82
  100. package/src/calls/media-stream-stt-session.ts +0 -15
  101. package/src/calls/resolve-call-tts-provider.ts +2 -33
  102. package/src/calls/speaker-identification.ts +233 -0
  103. package/src/calls/tts-call-strategy.ts +5 -9
  104. package/src/calls/types.ts +0 -2
  105. package/src/calls/voice-control-protocol.ts +21 -53
  106. package/src/calls/voice-session-bridge.ts +46 -59
  107. package/src/calls/voice-triage-escalate.ts +56 -175
  108. package/src/cli/commands/apps.help.ts +13 -67
  109. package/src/cli/commands/apps.ts +0 -183
  110. package/src/cli/commands/channels/index.help.ts +2 -70
  111. package/src/cli/commands/channels/index.ts +3 -16
  112. package/src/cli/commands/credentials.help.ts +2 -6
  113. package/src/cli/commands/credentials.ts +2 -2
  114. package/src/cli/commands/mcp.help.ts +8 -8
  115. package/src/cli/commands/mcp.ts +1 -1
  116. package/src/cli/commands/roadmap.help.ts +1 -5
  117. package/src/cli/commands/roadmap.ts +1 -1
  118. package/src/cli/lib/bundled-marketplace.json +90 -103
  119. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  120. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  121. package/src/config/__tests__/platform-rehydration.test.ts +0 -93
  122. package/src/config/acp-defaults.test.ts +3 -13
  123. package/src/config/acp-defaults.ts +6 -23
  124. package/src/config/acp-schema.ts +2 -30
  125. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  126. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  127. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  128. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  129. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  130. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  131. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  132. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  133. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  134. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  135. package/src/config/default-profile-catalog.ts +1 -1
  136. package/src/config/env.ts +2 -2
  137. package/src/config/feature-flag-registry.json +12 -8
  138. package/src/config/memory-v3-gate.test.ts +0 -31
  139. package/src/config/memory-v3-gate.ts +16 -20
  140. package/src/config/platform-rehydration.ts +21 -75
  141. package/src/config/schema.ts +2 -2
  142. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  143. package/src/config/schemas/calls.ts +28 -1
  144. package/src/config/schemas/live-voice.ts +173 -1
  145. package/src/config/schemas/mcp.ts +1 -1
  146. package/src/config/schemas/memory-retrospective.ts +0 -10
  147. package/src/config/schemas/skills.ts +14 -0
  148. package/src/credential-execution/process-manager.test.ts +19 -18
  149. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  150. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  151. package/src/daemon/app-source-watcher.ts +218 -0
  152. package/src/daemon/config-watcher.ts +194 -13
  153. package/src/daemon/conversation-process.ts +64 -35
  154. package/src/daemon/conversation-skill-tools.ts +31 -13
  155. package/src/daemon/conversation-store.ts +1 -1
  156. package/src/daemon/conversation-surfaces.ts +8 -10
  157. package/src/daemon/conversation-tool-setup.ts +43 -37
  158. package/src/daemon/conversation.ts +3 -0
  159. package/src/daemon/host-bash-proxy.ts +12 -8
  160. package/src/daemon/host-browser-proxy.ts +1 -15
  161. package/src/daemon/host-file-proxy.ts +10 -8
  162. package/src/daemon/host-transfer-proxy.ts +18 -14
  163. package/src/daemon/lifecycle.ts +5 -0
  164. package/src/daemon/mcp-reload-service.ts +5 -5
  165. package/src/daemon/message-types/sync.ts +0 -1
  166. package/src/daemon/providers-setup.ts +1 -1
  167. package/src/daemon/shutdown-handlers.ts +9 -1
  168. package/src/daemon/skill-memory-refresh.ts +0 -64
  169. package/src/daemon/tool-side-effects.ts +18 -7
  170. package/src/hooks/types.ts +0 -33
  171. package/src/inbound/platform-callback-registration.ts +5 -4
  172. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  173. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  174. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  175. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  176. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  177. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  178. package/src/live-voice/live-voice-session.ts +693 -213
  179. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  180. package/src/live-voice/protocol.ts +2 -0
  181. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  182. package/src/mcp/effective-config.ts +3 -3
  183. package/src/mcp/mcp-header-store.ts +45 -29
  184. package/src/mcp/startup.ts +4 -8
  185. package/src/messaging/provider.ts +3 -10
  186. package/src/messaging/providers/callback-routing.ts +0 -18
  187. package/src/messaging/providers/channel-transport.ts +0 -56
  188. package/src/messaging/providers/discord/transport.ts +0 -17
  189. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  190. package/src/messaging/providers/slack/adapter.ts +44 -7
  191. package/src/messaging/providers/slack/transport.ts +0 -16
  192. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  193. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  194. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  195. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  196. package/src/messaging/providers/whatsapp/client.ts +29 -0
  197. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  198. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  199. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  200. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  201. package/src/notifications/assistant-reply-producer.ts +3 -8
  202. package/src/notifications/conversation-pairing.ts +16 -36
  203. package/src/notifications/delivered-post-record.ts +1 -11
  204. package/src/notifications/home-feed-side-effect.ts +7 -55
  205. package/src/outbound-proxy/certs.ts +5 -3
  206. package/src/permissions/checker.test.ts +6 -11
  207. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  208. package/src/persistence/conversation-crud.ts +63 -71
  209. package/src/persistence/conversation-lineage.ts +4 -12
  210. package/src/persistence/conversation-title-service.ts +26 -9
  211. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  212. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  213. package/src/persistence/message-reads.ts +0 -30
  214. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  215. package/src/persistence/schema/memory-core.ts +0 -5
  216. package/src/platform/client.test.ts +0 -2
  217. package/src/platform/client.ts +10 -2
  218. package/src/plugin-api/constants.ts +0 -2
  219. package/src/plugin-api/index.ts +0 -1
  220. package/src/plugin-api/types.ts +0 -2
  221. package/src/plugins/defaults/index.ts +0 -2
  222. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  223. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  224. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  225. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  226. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  227. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  228. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  229. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  230. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  231. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  232. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  233. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  234. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  235. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  236. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  237. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  238. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  239. package/src/plugins/defaults/memory/startup.ts +1 -6
  240. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  241. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  242. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  243. package/src/plugins/defaults/memory/worker.ts +0 -6
  244. package/src/plugins/mcp-servers.ts +75 -15
  245. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  246. package/src/providers/gemini/client.ts +23 -9
  247. package/src/providers/model-catalog.ts +0 -34
  248. package/src/providers/model-intents.ts +5 -10
  249. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  250. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  251. package/src/providers/openai/chat-completions-provider.ts +78 -192
  252. package/src/providers/openai/codex-models.ts +4 -0
  253. package/src/runtime/AGENTS.md +0 -2
  254. package/src/runtime/auth/same-actor.ts +3 -38
  255. package/src/runtime/http-server.ts +151 -160
  256. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  257. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  258. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  259. package/src/runtime/routes/acp-routes.test.ts +0 -15
  260. package/src/runtime/routes/acp-routes.ts +9 -72
  261. package/src/runtime/routes/app-management-routes.ts +0 -73
  262. package/src/runtime/routes/app-routes.ts +1 -1
  263. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  264. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  265. package/src/runtime/routes/credential-routes.ts +10 -19
  266. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  267. package/src/runtime/routes/index.ts +0 -4
  268. package/src/runtime/routes/log-export-routes.ts +0 -2
  269. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  270. package/src/runtime/routes/migration-routes.ts +21 -8
  271. package/src/runtime/routes/platform-routes.ts +15 -18
  272. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  273. package/src/runtime/services/conversation-serializer.ts +3 -16
  274. package/src/runtime/sync/resource-sync-events.ts +1 -2
  275. package/src/schedule/worker.ts +1 -1
  276. package/src/security/ces-rpc-record-backend.ts +0 -7
  277. package/src/subagent/consult-context.ts +25 -12
  278. package/src/tools/acp/list-agents.test.ts +1 -3
  279. package/src/tools/acp/spawn.test.ts +11 -146
  280. package/src/tools/acp/spawn.ts +3 -21
  281. package/src/tools/acp/status.ts +3 -40
  282. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  283. package/src/tools/capability-offer.test.ts +2 -22
  284. package/src/tools/capability-offer.ts +1 -16
  285. package/src/tools/computer-use/definitions.ts +2 -4
  286. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  287. package/src/tools/credentials/metadata-store.ts +0 -100
  288. package/src/tools/host-filesystem/edit.test.ts +1 -2
  289. package/src/tools/host-filesystem/edit.ts +2 -2
  290. package/src/tools/host-filesystem/read.test.ts +1 -2
  291. package/src/tools/host-filesystem/read.ts +2 -2
  292. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  293. package/src/tools/host-filesystem/transfer.ts +2 -2
  294. package/src/tools/host-filesystem/write.test.ts +1 -2
  295. package/src/tools/host-filesystem/write.ts +2 -2
  296. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  297. package/src/tools/host-terminal/host-shell.ts +20 -4
  298. package/src/tools/policy-context.ts +7 -6
  299. package/src/tools/subagent/spawn.ts +8 -5
  300. package/src/tools/watch/watch-retro-report.ts +8 -0
  301. package/src/util/__tests__/language-subtag.test.ts +1 -23
  302. package/src/util/fs-watcher-error.ts +36 -0
  303. package/src/util/language-subtag.ts +0 -16
  304. package/src/util/platform.ts +0 -8
  305. package/src/workspace/git-service.ts +54 -5
  306. package/src/workspace/migrations/registry.ts +0 -6
  307. package/scripts/smoke-desktop-window-theme.ts +0 -186
  308. package/src/__tests__/git-porcelain.test.ts +0 -47
  309. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  310. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  311. package/src/__tests__/resolve-app.test.ts +0 -135
  312. package/src/__tests__/source-fingerprint.test.ts +0 -184
  313. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  314. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  315. package/src/acp/model-config.test.ts +0 -166
  316. package/src/acp/model-config.ts +0 -138
  317. package/src/api/events/acp-session-model-update.test.ts +0 -88
  318. package/src/api/events/acp-session-model-update.ts +0 -46
  319. package/src/apps/resolve-app.ts +0 -81
  320. package/src/apps/source-fingerprint.ts +0 -266
  321. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  322. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  323. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  324. package/src/calls/barge-in-guard.ts +0 -118
  325. package/src/calls/voice-leg-coordinator.ts +0 -189
  326. package/src/calls/voice-progress-cadence.ts +0 -423
  327. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  328. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  329. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  330. package/src/cli/commands/channels/send.ts +0 -128
  331. package/src/config/__tests__/platform-identity.test.ts +0 -117
  332. package/src/config/acp-agent-write.ts +0 -43
  333. package/src/config/acp-schema.test.ts +0 -81
  334. package/src/config/platform-identity.ts +0 -144
  335. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  336. package/src/config/schemas/voice.ts +0 -150
  337. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  338. package/src/daemon/app-change-notify.ts +0 -45
  339. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  340. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  341. package/src/desktop/desktop-chrome-policy.ts +0 -42
  342. package/src/desktop/desktop-dependencies.test.ts +0 -102
  343. package/src/desktop/desktop-dependencies.ts +0 -306
  344. package/src/desktop/desktop-feature.test.ts +0 -18
  345. package/src/desktop/desktop-feature.ts +0 -19
  346. package/src/desktop/desktop-panel-config.test.ts +0 -135
  347. package/src/desktop/desktop-panel-config.ts +0 -149
  348. package/src/desktop/desktop-session-manager.test.ts +0 -627
  349. package/src/desktop/desktop-session-manager.ts +0 -814
  350. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  351. package/src/desktop/desktop-stream-bridge.ts +0 -214
  352. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  353. package/src/desktop/desktop-wallpaper.ts +0 -113
  354. package/src/desktop/desktop-window-theme.ts +0 -170
  355. package/src/i18n/AGENTS.md +0 -22
  356. package/src/i18n/__tests__/i18n.test.ts +0 -108
  357. package/src/i18n/index.ts +0 -16
  358. package/src/i18n/locales.ts +0 -93
  359. package/src/i18n/messages.ts +0 -61
  360. package/src/i18n/title.ts +0 -25
  361. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  362. package/src/mcp/spec-schema.ts +0 -111
  363. package/src/mcp/workspace-mcp-config.ts +0 -188
  364. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  365. package/src/persistence/conversation-title-placeholders.ts +0 -23
  366. package/src/persistence/message-cursor.ts +0 -78
  367. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  368. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  369. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  370. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  371. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  372. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  373. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  374. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  375. package/src/providers/gemini-thought-signature.test.ts +0 -63
  376. package/src/providers/gemini-thought-signature.ts +0 -44
  377. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  378. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  379. package/src/providers/openai/google-thought-signature.ts +0 -320
  380. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  381. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  382. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  383. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  384. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  385. package/src/runtime/channel-send.ts +0 -316
  386. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  387. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  388. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  389. package/src/runtime/routes/channel-send-routes.ts +0 -85
  390. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  391. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  392. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  393. package/src/security/ces-http-record-backend.ts +0 -120
  394. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  395. package/src/skills/workspace-skill-md-signature.ts +0 -50
  396. package/src/tools/acp/status.test.ts +0 -94
  397. package/src/util/ca-bundle.ts +0 -13
  398. package/src/workspace/git-porcelain.ts +0 -66
  399. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  400. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  401. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  402. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  403. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  404. /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
- }