@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,627 +0,0 @@
1
- import { mkdtempSync, readFileSync, 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 { waitFor } from "../__tests__/helpers/wait-for.js";
7
- import { sleep } from "../util/retry.js";
8
- import {
9
- type FakeDesktopOptions,
10
- KILL_GRACE_MS,
11
- LINGER_MS,
12
- newFakeDesktop,
13
- newViewer,
14
- settle,
15
- } from "./__tests__/fake-desktop.js";
16
- import {
17
- DESKTOP_VNC_PORT,
18
- type DesktopChildRole,
19
- } from "./desktop-session-manager.js";
20
-
21
- const BUSY = {
22
- ok: false,
23
- loss: { code: 4013, reason: "Desktop is in use by another viewer" },
24
- } as const;
25
- const SHUTTING_DOWN = {
26
- ok: false,
27
- loss: { code: 1001, reason: "The assistant is shutting down" },
28
- } as const;
29
-
30
- const profileDir = mkdtempSync(join(tmpdir(), "desktop-session-test-"));
31
- const panelConfigDir = mkdtempSync(join(tmpdir(), "desktop-panel-test-"));
32
- afterAll(() => {
33
- rmSync(profileDir, { recursive: true, force: true });
34
- rmSync(panelConfigDir, { recursive: true, force: true });
35
- });
36
-
37
- function newManager(
38
- options: Omit<FakeDesktopOptions, "profileDir" | "panelConfigDir"> = {},
39
- ) {
40
- return newFakeDesktop({ profileDir, panelConfigDir, ...options });
41
- }
42
-
43
- describe("desktop wallpaper lifecycle", () => {
44
- test("applies the rendered wallpaper and refreshes it on reconnect without restarting X", async () => {
45
- let current = Buffer.from("first wallpaper");
46
- const h = newManager({
47
- renderWallpaper: async () => current,
48
- exitOnTerm: true,
49
- });
50
- await h.manager.ensureDesktopRunning();
51
- await settle();
52
- const path = join(panelConfigDir, "wallpaper.png");
53
- expect(readFileSync(path)).toEqual(current);
54
- expect(h.child("wallpaper").request.cmd).toEqual([
55
- "/usr/bin/feh",
56
- "--no-fehbg",
57
- "--bg-fill",
58
- path,
59
- ]);
60
- expect(h.child("wallpaper").request.env.DISPLAY).toBe(":99");
61
- h.child("wallpaper").exit(0);
62
- await settle();
63
- current = Buffer.from("updated avatar wallpaper");
64
- await h.manager.ensureDesktopRunning();
65
- await settle();
66
- expect(readFileSync(path)).toEqual(current);
67
- expect(h.count("wallpaper")).toBe(2);
68
- expect(h.count("x-server")).toBe(1);
69
- await h.manager.destroy();
70
- });
71
-
72
- test.each(["success", "failure"] as const)(
73
- "reconnects during a render queue the latest avatar after %s",
74
- async (outcome) => {
75
- let current = Buffer.from("first avatar");
76
- const pending: { finish: () => void; fail: () => void }[] = [];
77
- const h = newManager({
78
- renderWallpaper: () => {
79
- const snapshot = current;
80
- return new Promise((resolve, reject) => {
81
- pending.push({
82
- finish: () => resolve(snapshot),
83
- fail: () => reject(new Error("render failed")),
84
- });
85
- });
86
- },
87
- exitOnTerm: true,
88
- });
89
- try {
90
- await h.manager.ensureDesktopRunning();
91
- current = Buffer.from("second avatar");
92
- await h.manager.ensureDesktopRunning();
93
- current = Buffer.from("latest avatar");
94
- await h.manager.ensureDesktopRunning();
95
- expect(pending).toHaveLength(1);
96
-
97
- if (outcome === "success") {
98
- pending[0]!.finish();
99
- } else {
100
- pending[0]!.fail();
101
- }
102
- await settle();
103
- expect(pending).toHaveLength(2);
104
- expect(h.count("wallpaper")).toBe(0);
105
-
106
- pending[1]!.finish();
107
- await settle();
108
- expect(readFileSync(join(panelConfigDir, "wallpaper.png"))).toEqual(
109
- current,
110
- );
111
- expect(h.count("wallpaper")).toBe(1);
112
- expect(h.count("x-server")).toBe(1);
113
- expect(pending).toHaveLength(2);
114
- } finally {
115
- await h.manager.destroy();
116
- }
117
- },
118
- );
119
-
120
- test("pending and queued renders neither delay Chrome nor apply after shutdown", async () => {
121
- let finish!: (png: Buffer) => void;
122
- let renders = 0;
123
- const h = newManager({
124
- renderWallpaper: () => {
125
- renders++;
126
- return new Promise((resolve) => {
127
- finish = resolve;
128
- });
129
- },
130
- exitOnTerm: true,
131
- });
132
- await h.manager.ensureDesktopRunning();
133
- await h.manager.ensureDesktopRunning();
134
- await settle();
135
- expect(h.count("browser")).toBe(1);
136
- expect(renders).toBe(1);
137
- await h.manager.destroy();
138
- finish(Buffer.from("stale render"));
139
- await settle();
140
- expect(h.count("wallpaper")).toBe(0);
141
- expect(renders).toBe(1);
142
- });
143
-
144
- test.each(["render", "spawn", "exit"] as const)(
145
- "wallpaper %s failure leaves the viewer connected",
146
- async (failure) => {
147
- const h = newManager({
148
- renderWallpaper: async () => {
149
- if (failure === "render") {
150
- throw new Error("render failed");
151
- }
152
- return Buffer.from("wallpaper");
153
- },
154
- failSpawn: failure === "spawn" ? ["wallpaper"] : [],
155
- exitOnTerm: true,
156
- });
157
- const { viewer, lost } = newViewer();
158
- h.manager.acquireViewerSlot(viewer);
159
- await h.manager.ensureDesktopRunning();
160
- await settle();
161
- if (failure === "exit") {
162
- h.child("wallpaper").exit(1);
163
- await settle();
164
- }
165
- expect(lost).toEqual([]);
166
- expect(h.count("browser")).toBe(1);
167
- expect(h.terminated()).toEqual([]);
168
- await h.manager.destroy();
169
- },
170
- );
171
- });
172
-
173
- describe("DesktopSessionManager process tree", () => {
174
- test("concurrent callers share one start of the whole tree", async () => {
175
- const h = newManager({
176
- sourceEnv: {
177
- PATH: "/usr/bin",
178
- HOME: "/data",
179
- LANG: "C.UTF-8",
180
- // Neither the kata chroot overlay nor secrets reach the desktop.
181
- LD_LIBRARY_PATH: "/data/system/usr/lib",
182
- VELLUM_SANDBOX_RUNTIME: "kata",
183
- OPENAI_API_KEY: "sk-secret",
184
- },
185
- });
186
- await Promise.all([
187
- h.manager.ensureDesktopRunning(),
188
- h.manager.ensureDesktopRunning(),
189
- ]);
190
- await settle();
191
-
192
- // The dock waits on the Chromium path its launcher points at, so it comes
193
- // up alongside the browser rather than with the rest of the tree.
194
- expect(h.roles()).toEqual([
195
- "x-server",
196
- "window-manager",
197
- "compositor",
198
- "clipboard",
199
- "panel",
200
- "browser",
201
- ]);
202
-
203
- const x = h.child("x-server").request.cmd;
204
- expect(x.slice(0, 2)).toEqual(["/usr/bin/Xtigervnc", ":99"]);
205
- expect(x).toContain("-localhost");
206
- expect(x[x.indexOf("-SecurityTypes") + 1]).toBe("None");
207
- expect(x[x.indexOf("-rfbport") + 1]).toBe(String(DESKTOP_VNC_PORT));
208
- expect(x[x.indexOf("-geometry") + 1]).toBe("1440x900");
209
- for (const role of [
210
- "window-manager",
211
- "compositor",
212
- "clipboard",
213
- "browser",
214
- ] as const) {
215
- expect(h.child(role).request.env).toEqual({
216
- PATH: "/usr/bin",
217
- HOME: "/data",
218
- LANG: "C.UTF-8",
219
- DISPLAY: ":99",
220
- });
221
- }
222
- expect(h.child("window-manager").request.cmd[0]).toBe("/usr/bin/openbox");
223
- // The compositor precedes the dock so it has an ARGB visual.
224
- expect(h.child("compositor").request.cmd).toEqual(["/usr/bin/xcompmgr"]);
225
- expect(h.child("panel").request.cmd).toEqual([
226
- "/usr/bin/dbus-run-session",
227
- "--",
228
- "/usr/bin/plank",
229
- ]);
230
- expect(h.child("panel").request.env).toEqual({
231
- ...h.child("browser").request.env,
232
- XDG_CONFIG_HOME: panelConfigDir,
233
- XDG_DATA_HOME: panelConfigDir,
234
- GSETTINGS_BACKEND: "keyfile",
235
- });
236
- expect(h.child("clipboard").request.cmd).toEqual([
237
- "/usr/bin/tigervncconfig",
238
- "-nowin",
239
- ]);
240
- const browser = h.child("browser").request.cmd;
241
- expect(browser[0]).toBe("/fake/chromium");
242
- expect(browser).toContain(`--user-data-dir=${profileDir}`);
243
- // Explicit geometry matching the X server, so the window does not depend
244
- // on openbox being up to honor --start-maximized.
245
- expect(browser).toContain("--window-position=0,0");
246
- expect(browser).toContain("--window-size=1440,900");
247
-
248
- // A running tree is reused rather than started again.
249
- await h.manager.ensureDesktopRunning();
250
- expect(h.count("x-server")).toBe(1);
251
- });
252
-
253
- test("falls back to vncconfig when tigervncconfig is absent", async () => {
254
- const h = newManager({ missingBinaries: ["tigervncconfig"] });
255
- await h.manager.ensureDesktopRunning();
256
- expect(h.child("clipboard").request.cmd).toEqual([
257
- "/usr/bin/vncconfig",
258
- "-nowin",
259
- ]);
260
- });
261
-
262
- test("a missing binary fails the start before anything is spawned", async () => {
263
- const h = newManager({
264
- missingBinaries: [
265
- "Xtigervnc",
266
- "xcompmgr",
267
- "plank",
268
- "tigervncconfig",
269
- "vncconfig",
270
- "xterm",
271
- ],
272
- });
273
- await expect(h.manager.ensureDesktopRunning()).rejects.toThrow(
274
- "Desktop component is missing: Xtigervnc",
275
- );
276
- expect(h.spawned).toEqual([]);
277
- });
278
-
279
- test("the dock is generated with the resolved Chrome and terminal", async () => {
280
- const h = newManager();
281
- await h.manager.ensureDesktopRunning();
282
- await settle();
283
-
284
- const chromium = readFileSync(
285
- join(panelConfigDir, "applications", "google-chrome.desktop"),
286
- "utf8",
287
- );
288
- expect(chromium).toContain(`Exec="/fake/chromium"`);
289
- expect(chromium).toContain(`"--user-data-dir=${profileDir}"`);
290
- expect(chromium).toContain("Icon=/fake/product_logo_64.png");
291
-
292
- const terminal = readFileSync(
293
- join(panelConfigDir, "applications", "xterm.desktop"),
294
- "utf8",
295
- );
296
- expect(terminal).toContain(`Exec="/usr/bin/xterm"`);
297
- expect(terminal).toContain(`Icon=${join(panelConfigDir, "terminal.png")}`);
298
-
299
- const bytes = readFileSync(join(panelConfigDir, "terminal.png"));
300
- expect([...bytes.subarray(0, 8)]).toEqual([
301
- 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
302
- ]);
303
- // A complete IEND chunk catches a truncated icon.
304
- expect([...bytes.subarray(-12)]).toEqual([
305
- 0, 0, 0, 0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
306
- ]);
307
- });
308
-
309
- test("a VNC port that never opens fails the start and kills the X server, and a retry works", async () => {
310
- const h = newManager();
311
- h.setVncReady(false);
312
-
313
- await expect(h.manager.ensureDesktopRunning()).rejects.toThrow(/not ready/);
314
- expect(h.terminated()).toEqual([h.child("x-server")]);
315
-
316
- h.setVncReady(true);
317
- await h.manager.ensureDesktopRunning();
318
- expect(h.count("x-server")).toBe(2);
319
- });
320
-
321
- test("a retry spawns its X server only after the failed one is gone", async () => {
322
- const h = newManager();
323
- h.setVncReady(false);
324
- const { viewer, lost } = newViewer();
325
- h.manager.acquireViewerSlot(viewer);
326
-
327
- await expect(h.manager.ensureDesktopRunning()).rejects.toThrow(/not ready/);
328
- // The viewer hears about it before the kill grace, not after.
329
- expect(lost).toEqual([{ code: 4011, reason: "Desktop failed to start" }]);
330
- const first = h.child("x-server");
331
- expect(h.killed).toEqual([{ child: first, signal: "SIGTERM" }]);
332
-
333
- // Reconnect while the first X server is still ignoring its SIGTERM.
334
- h.setVncReady(true);
335
- const retry = h.manager.ensureDesktopRunning();
336
- await sleep(KILL_GRACE_MS / 2);
337
- expect(h.count("x-server")).toBe(1);
338
-
339
- // The SIGKILL alone does not free the display and port; the exit does.
340
- await waitFor(() => h.killed.length === 2, { intervalMs: 1 });
341
- expect(h.killed[1]).toEqual({ child: first, signal: "SIGKILL" });
342
- expect(h.count("x-server")).toBe(1);
343
-
344
- first.exit(0);
345
- await retry;
346
- expect(h.count("x-server")).toBe(2);
347
- });
348
-
349
- test("a retry stops waiting on an X server that survives SIGKILL", async () => {
350
- const h = newManager();
351
- h.setVncReady(false);
352
- await expect(h.manager.ensureDesktopRunning()).rejects.toThrow(/not ready/);
353
-
354
- h.setVncReady(true);
355
- const started = Date.now();
356
- await h.manager.ensureDesktopRunning();
357
- expect(Date.now() - started).toBeGreaterThanOrEqual(KILL_GRACE_MS * 2 - 4);
358
- expect(h.killed.map((k) => k.signal)).toEqual(["SIGTERM", "SIGKILL"]);
359
- expect(h.count("x-server")).toBe(2);
360
- });
361
-
362
- test("a spawn that fails after the X server is up rolls the tree back, and a retry starts clean", async () => {
363
- const failSpawn: DesktopChildRole[] = ["clipboard"];
364
- const h = newManager({ failSpawn });
365
- const { viewer, lost } = newViewer();
366
- h.manager.acquireViewerSlot(viewer);
367
-
368
- await expect(h.manager.ensureDesktopRunning()).rejects.toThrow(
369
- "spawn clipboard failed",
370
- );
371
- expect(
372
- h
373
- .terminated()
374
- .map((c) => c.role)
375
- .sort(),
376
- ).toEqual(["compositor", "window-manager", "x-server"]);
377
- expect(lost).toEqual([{ code: 4011, reason: "Desktop failed to start" }]);
378
-
379
- failSpawn.length = 0;
380
- await h.manager.ensureDesktopRunning();
381
- await settle();
382
- expect(h.roles().slice(3)).toEqual([
383
- "x-server",
384
- "window-manager",
385
- "compositor",
386
- "clipboard",
387
- "panel",
388
- "browser",
389
- ]);
390
- expect(h.terminated()).toHaveLength(3);
391
- });
392
-
393
- test("a dock or compositor that will not spawn leaves the desktop up", async () => {
394
- const h = newManager({ failSpawn: ["compositor", "panel"] });
395
- const { viewer, lost } = newViewer();
396
- h.manager.acquireViewerSlot(viewer);
397
-
398
- await h.manager.ensureDesktopRunning();
399
- await settle();
400
-
401
- expect(lost).toEqual([]);
402
- expect(h.killed).toEqual([]);
403
- expect(h.roles()).toEqual([
404
- "x-server",
405
- "window-manager",
406
- "clipboard",
407
- "browser",
408
- ]);
409
- });
410
-
411
- test("an X server exit tears the tree down and tells the viewer", async () => {
412
- const h = newManager();
413
- const { viewer, lost } = newViewer();
414
- expect(h.manager.acquireViewerSlot(viewer)).toEqual({ ok: true });
415
- await h.manager.ensureDesktopRunning();
416
- await settle();
417
-
418
- h.child("x-server").exit(1);
419
- await settle();
420
-
421
- expect(lost).toEqual([
422
- { code: 4011, reason: "Desktop stopped: x-server exited" },
423
- ]);
424
- expect(
425
- h
426
- .terminated()
427
- .map((c) => c.role)
428
- .sort(),
429
- ).toEqual([
430
- "browser",
431
- "clipboard",
432
- "compositor",
433
- "panel",
434
- "window-manager",
435
- ]);
436
- // The slot is free and the next start builds a fresh tree.
437
- expect(h.manager.acquireViewerSlot(newViewer().viewer)).toEqual({
438
- ok: true,
439
- });
440
- await h.manager.ensureDesktopRunning();
441
- expect(h.count("x-server")).toBe(2);
442
- });
443
-
444
- test("a panel exit keeps applications alive until their group is cleaned up at teardown", async () => {
445
- const h = newManager({ exitOnTerm: true });
446
- const { viewer, lost } = newViewer();
447
- h.manager.acquireViewerSlot(viewer);
448
- await h.manager.ensureDesktopRunning();
449
- await settle();
450
-
451
- h.child("panel").exit(1);
452
- await settle();
453
-
454
- expect(lost).toEqual([]);
455
- expect(h.killed).toEqual([]);
456
- // Not relaunched, and the tree it belonged to is untouched.
457
- expect(h.count("panel")).toBe(1);
458
- await h.manager.ensureDesktopRunning();
459
- expect(h.count("x-server")).toBe(1);
460
-
461
- await h.manager.destroy();
462
- expect(h.killed.filter((k) => k.child === h.child("panel"))).toEqual([
463
- { child: h.child("panel"), signal: "SIGTERM" },
464
- { child: h.child("panel"), signal: "SIGKILL" },
465
- ]);
466
- });
467
-
468
- test("a browser exit with nobody watching keeps the desktop and the next viewer gets a fresh one", async () => {
469
- const h = newManager();
470
- await h.manager.ensureDesktopRunning();
471
- await settle();
472
-
473
- h.child("browser").exit(0);
474
- await settle();
475
- expect(h.killed).toEqual([]);
476
- expect(h.count("browser")).toBe(1);
477
-
478
- await h.manager.ensureDesktopRunning();
479
- await settle();
480
- expect(h.count("x-server")).toBe(1);
481
- expect(h.count("browser")).toBe(2);
482
- });
483
-
484
- test("a browser exit under a viewer relaunches it, until it crash loops", async () => {
485
- const h = newManager();
486
- const { viewer, lost } = newViewer();
487
- h.manager.acquireViewerSlot(viewer);
488
- await h.manager.ensureDesktopRunning();
489
- await settle();
490
-
491
- for (let exits = 1; exits <= 3; exits += 1) {
492
- h.child("browser").exit(1);
493
- await settle();
494
- expect(h.count("browser")).toBe(exits + 1);
495
- expect(lost).toEqual([]);
496
- }
497
-
498
- h.child("browser").exit(1);
499
- await settle();
500
- expect(h.count("browser")).toBe(4);
501
- expect(lost).toEqual([
502
- { code: 4011, reason: "Desktop browser keeps crashing" },
503
- ]);
504
- expect(
505
- h
506
- .terminated()
507
- .map((c) => c.role)
508
- .sort(),
509
- ).toEqual([
510
- "clipboard",
511
- "compositor",
512
- "panel",
513
- "window-manager",
514
- "x-server",
515
- ]);
516
- });
517
-
518
- test("a browser that cannot be resolved takes the desktop down", async () => {
519
- const h = newManager();
520
- h.setChromiumPath(async () => {
521
- throw new Error("playwright install failed");
522
- });
523
- const { viewer, lost } = newViewer();
524
- h.manager.acquireViewerSlot(viewer);
525
- await h.manager.ensureDesktopRunning();
526
- await settle();
527
-
528
- expect(lost).toEqual([
529
- { code: 4011, reason: "Desktop browser failed to start" },
530
- ]);
531
- // The dock never got its Chromium path, so only the three fatal children
532
- // and no panel are killed.
533
- expect(
534
- h
535
- .terminated()
536
- .map((c) => c.role)
537
- .sort(),
538
- ).toEqual(["clipboard", "compositor", "window-manager", "x-server"]);
539
- });
540
-
541
- test("destroy terminates, hard-kills stragglers after the grace, and refuses what comes after", async () => {
542
- const h = newManager({ exitOnKill: true });
543
- const { viewer, lost } = newViewer();
544
- h.manager.acquireViewerSlot(viewer);
545
- await h.manager.ensureDesktopRunning();
546
- await settle();
547
-
548
- const started = Date.now();
549
- await Promise.all([h.manager.destroy(), h.manager.destroy()]);
550
-
551
- expect(Date.now() - started).toBeGreaterThanOrEqual(KILL_GRACE_MS - 2);
552
- expect(lost).toEqual([
553
- { code: 1001, reason: "The assistant is shutting down" },
554
- ]);
555
- expect(h.killed.map((k) => k.signal)).toEqual([
556
- ...Array(6).fill("SIGTERM"),
557
- ...Array(6).fill("SIGKILL"),
558
- ]);
559
- expect(h.manager.acquireViewerSlot(newViewer().viewer)).toEqual(
560
- SHUTTING_DOWN,
561
- );
562
- await expect(h.manager.ensureDesktopRunning()).rejects.toMatchObject({
563
- loss: SHUTTING_DOWN.loss,
564
- });
565
- });
566
-
567
- test("destroy still clears dock descendants when every direct child exits on SIGTERM", async () => {
568
- const h = newManager({ exitOnTerm: true });
569
- await h.manager.ensureDesktopRunning();
570
- await settle();
571
-
572
- await h.manager.destroy();
573
- expect(h.killed.filter((k) => k.signal === "SIGTERM")).toHaveLength(6);
574
- expect(h.killed.filter((k) => k.signal === "SIGKILL")).toEqual([
575
- { child: h.child("panel"), signal: "SIGKILL" },
576
- ]);
577
- });
578
- });
579
-
580
- describe("DesktopSessionManager viewer slot", () => {
581
- test("holds one viewer and only the holder can release it", () => {
582
- const h = newManager();
583
- const first = newViewer().viewer;
584
- const second = newViewer().viewer;
585
-
586
- expect(h.manager.acquireViewerSlot(first)).toEqual({ ok: true });
587
- expect(h.manager.acquireViewerSlot(second)).toEqual(BUSY);
588
-
589
- h.manager.releaseViewerSlot(second);
590
- expect(h.manager.acquireViewerSlot(second)).toEqual(BUSY);
591
-
592
- h.manager.releaseViewerSlot(first);
593
- expect(h.manager.acquireViewerSlot(second)).toEqual({ ok: true });
594
- });
595
-
596
- test("the tree lingers after the last viewer leaves, then is torn down", async () => {
597
- const h = newManager();
598
- const { viewer } = newViewer();
599
- h.manager.acquireViewerSlot(viewer);
600
- await h.manager.ensureDesktopRunning();
601
- await settle();
602
-
603
- h.manager.releaseViewerSlot(viewer);
604
- expect(h.killed).toEqual([]);
605
-
606
- await sleep(LINGER_MS * 2);
607
- expect(h.terminated()).toHaveLength(6);
608
- await h.manager.ensureDesktopRunning();
609
- expect(h.count("x-server")).toBe(2);
610
- });
611
-
612
- test("a viewer returning inside the linger window keeps the tree", async () => {
613
- const h = newManager();
614
- const { viewer } = newViewer();
615
- h.manager.acquireViewerSlot(viewer);
616
- await h.manager.ensureDesktopRunning();
617
- await settle();
618
-
619
- h.manager.releaseViewerSlot(viewer);
620
- h.manager.acquireViewerSlot(viewer);
621
- await sleep(LINGER_MS * 2);
622
-
623
- expect(h.killed).toEqual([]);
624
- await h.manager.ensureDesktopRunning();
625
- expect(h.count("x-server")).toBe(1);
626
- });
627
- });