@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,191 +0,0 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { afterEach, beforeEach, expect, test } from "bun:test";
5
-
6
- import sharp from "sharp";
7
-
8
- import {
9
- __resetResvgCacheForTests,
10
- __setResvgCacheForTests,
11
- } from "../avatar/resvg-lazy.js";
12
- import {
13
- renderCurrentDesktopWallpaper,
14
- renderDesktopWallpaper,
15
- } from "./desktop-wallpaper.js";
16
-
17
- let workspace: string;
18
- let previousWorkspace: string | undefined;
19
- beforeEach(() => {
20
- workspace = mkdtempSync(join(tmpdir(), "desktop-wallpaper-test-"));
21
- previousWorkspace = process.env.VELLUM_WORKSPACE_DIR;
22
- process.env.VELLUM_WORKSPACE_DIR = workspace;
23
- mkdirSync(join(workspace, "data/avatar"), { recursive: true });
24
- });
25
- afterEach(() => {
26
- __resetResvgCacheForTests();
27
- if (previousWorkspace === undefined) {
28
- delete process.env.VELLUM_WORKSPACE_DIR;
29
- } else {
30
- process.env.VELLUM_WORKSPACE_DIR = previousWorkspace;
31
- }
32
- rmSync(workspace, { recursive: true, force: true });
33
- });
34
-
35
- function manifest(state: Record<string, unknown>): void {
36
- writeFileSync(
37
- join(workspace, "data/avatar/avatar.json"),
38
- JSON.stringify(state),
39
- );
40
- }
41
-
42
- async function centerPixel(png: Buffer): Promise<number[]> {
43
- const { data, info } = await sharp(png)
44
- .ensureAlpha()
45
- .raw()
46
- .toBuffer({ resolveWithObject: true });
47
- const offset =
48
- (Math.round(info.height * 0.46) * info.width + info.width / 2) * 4;
49
- return [...data.subarray(offset, offset + 4)];
50
- }
51
-
52
- test("renders the current uploaded avatar into an opaque desktop wallpaper", async () => {
53
- const png = await sharp({
54
- create: { width: 32, height: 32, channels: 4, background: "#ff0000" },
55
- })
56
- .png()
57
- .toBuffer();
58
- writeFileSync(join(workspace, "data/avatar/avatar-image.png"), png);
59
- manifest({
60
- kind: "image",
61
- source: "upload",
62
- traits: null,
63
- image: { updatedAt: "2026-01-01T00:00:00Z", etag: "test" },
64
- accent: null,
65
- });
66
- const result = (await renderCurrentDesktopWallpaper(480, 300))!;
67
- expect(await centerPixel(result)).toEqual([255, 0, 0, 255]);
68
- const metadata = await sharp(result).metadata();
69
- expect([metadata.width, metadata.height]).toEqual([480, 300]);
70
- expect((await sharp(result).stats()).isOpaque).toBe(true);
71
- });
72
-
73
- test("regenerates a character from current traits when its raster is missing", async () => {
74
- manifest({
75
- kind: "character",
76
- source: "pool",
77
- traits: { bodyShape: "blob", eyeStyle: "curious", color: "green" },
78
- image: null,
79
- accent: null,
80
- });
81
- const result = (await renderCurrentDesktopWallpaper(480, 300))!;
82
- const empty = renderDesktopWallpaper(480, 300, null, null);
83
- expect(await centerPixel(result)).not.toEqual(await centerPixel(empty));
84
- });
85
-
86
- test("missing and unreadable avatars produce the same neutral background", async () => {
87
- const missing = await renderCurrentDesktopWallpaper(480, 300);
88
- manifest({
89
- kind: "image",
90
- source: "upload",
91
- traits: null,
92
- image: { updatedAt: "2026-01-01T00:00:00Z", etag: "test" },
93
- accent: null,
94
- });
95
- writeFileSync(
96
- join(workspace, "data/avatar/avatar-image.png"),
97
- "not an image",
98
- );
99
- expect(await renderCurrentDesktopWallpaper(480, 300)).toEqual(missing);
100
- expect((await sharp(missing!).stats()).isOpaque).toBe(true);
101
- });
102
-
103
- test("accent changes tint the background and malformed accents fall back safely", async () => {
104
- const neutral = renderDesktopWallpaper(480, 300, null, null);
105
- expect(
106
- renderDesktopWallpaper(480, 300, null, '\"/><image href="file:///test"/>'),
107
- ).toEqual(neutral);
108
- const red = await centerPixel(
109
- renderDesktopWallpaper(480, 300, null, "#ff0000"),
110
- );
111
- const blue = await centerPixel(
112
- renderDesktopWallpaper(480, 300, null, "#0000ff"),
113
- );
114
- expect(red[0]!).toBeGreaterThan(blue[0]!);
115
- expect(blue[2]!).toBeGreaterThan(red[2]!);
116
- });
117
-
118
- test("a missing native renderer leaves the existing desktop background alone", async () => {
119
- __setResvgCacheForTests({
120
- available: false,
121
- error: new Error("unavailable"),
122
- });
123
- expect(await renderCurrentDesktopWallpaper(480, 300)).toBeNull();
124
- });
125
-
126
- async function wordmarkPixels(
127
- png: Buffer,
128
- ): Promise<{ x: number; y: number }[]> {
129
- const { data, info } = await sharp(png)
130
- .removeAlpha()
131
- .raw()
132
- .toBuffer({ resolveWithObject: true });
133
- const pixels: { x: number; y: number }[] = [];
134
- for (let y = 0; y < info.height; y++) {
135
- for (let x = 0; x < info.width; x++) {
136
- const offset = (y * info.width + x) * info.channels;
137
- if (
138
- data[offset]! > 150 &&
139
- data[offset + 1]! > 150 &&
140
- data[offset + 2]! > 150
141
- ) {
142
- pixels.push({ x, y });
143
- }
144
- }
145
- }
146
- return pixels;
147
- }
148
-
149
- test("renders the current identity name and picks up a rename on the next refresh", async () => {
150
- const identity = join(workspace, "IDENTITY.md");
151
- writeFileSync(identity, "- **Name:** Alice\n");
152
- const first = (await renderCurrentDesktopWallpaper(480, 300))!;
153
- expect(first).toEqual(renderDesktopWallpaper(480, 300, null, null, "Alice"));
154
- const pixels = await wordmarkPixels(first);
155
- expect(pixels.length).toBeGreaterThan(100);
156
- expect(pixels.every(({ y }) => y > 300 * 0.6 && y < 300 * 0.72)).toBe(true);
157
-
158
- writeFileSync(identity, "- **Name:** Bob\n");
159
- const renamed = await renderCurrentDesktopWallpaper(480, 300);
160
- expect(renamed).not.toEqual(first);
161
- expect(renamed).toEqual(renderDesktopWallpaper(480, 300, null, null, "Bob"));
162
- });
163
-
164
- test("an unset or template identity displays Vellum OS", async () => {
165
- const fallback = renderDesktopWallpaper(480, 300, null, null, "Vellum");
166
- expect(await renderCurrentDesktopWallpaper(480, 300)).toEqual(fallback);
167
- writeFileSync(
168
- join(workspace, "IDENTITY.md"),
169
- "- **Name:** _(not yet chosen)_\n",
170
- );
171
- expect(await renderCurrentDesktopWallpaper(480, 300)).toEqual(fallback);
172
- });
173
-
174
- test("long names fit inside the wallpaper without losing the wordmark", async () => {
175
- const png = renderDesktopWallpaper(480, 300, null, null, "W".repeat(32));
176
- const pixels = await wordmarkPixels(png);
177
- expect(pixels.length).toBeGreaterThan(100);
178
- expect(pixels.every(({ x }) => x > 480 * 0.13 && x < 480 * 0.87)).toBe(true);
179
- });
180
-
181
- test("names containing XML are rendered as text without injecting SVG shapes", async () => {
182
- const name =
183
- 'Alice & </text><rect width="480" height="300" fill="red"/><text>';
184
- const png = renderDesktopWallpaper(480, 300, null, null, name);
185
- const fallback = renderDesktopWallpaper(480, 300, null, null);
186
- const top = { left: 0, top: 0, width: 480, height: 150 };
187
- expect(await sharp(png).extract(top).raw().toBuffer()).toEqual(
188
- await sharp(fallback).extract(top).raw().toBuffer(),
189
- );
190
- expect((await wordmarkPixels(png)).length).toBeGreaterThan(0);
191
- });
@@ -1,113 +0,0 @@
1
- import { readAvatarState } from "../avatar/avatar-manifest.js";
2
- import {
3
- renderNotificationAvatarPng,
4
- resolveNotificationAccentHex,
5
- } from "../avatar/notification-avatar.js";
6
- import { getResvg, isResvgAvailable } from "../avatar/resvg-lazy.js";
7
- import { isTemplatePlaceholder } from "../daemon/handlers/identity.js";
8
- import { getAssistantName } from "../daemon/identity-helpers.js";
9
- import { escapeXmlContent } from "../util/xml.js";
10
-
11
- function wallpaperWordmark(
12
- width: number,
13
- height: number,
14
- name: string | null,
15
- ): string {
16
- const normalized = name?.replace(/\s+/g, " ").trim();
17
- const label =
18
- normalized && !isTemplatePlaceholder(normalized) ? normalized : "Vellum";
19
- const text = `<text id="wordmark" font-family="Liberation Sans, Arial, sans-serif" font-size="64" font-weight="700" letter-spacing="1.5">${escapeXmlContent(`${label} OS`)}</text>`;
20
- const Resvg = getResvg();
21
- const bounds = new Resvg(
22
- `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">${text}</svg>`,
23
- ).getBBox();
24
- if (!bounds || bounds.width === 0) {
25
- return "";
26
- }
27
- const depth = 6;
28
- const scale = Math.min(
29
- (height * 0.06) / 64,
30
- (width * 0.72) / (bounds.width + depth),
31
- );
32
- const x = width / 2 - (bounds.x + (bounds.width + depth) / 2) * scale;
33
- const layers = Array.from({ length: depth }, (_, index) => {
34
- const offset = depth - index;
35
- return `<use href="#wordmark" transform="translate(${offset} ${offset})" fill="#40535b"/>`;
36
- }).join("");
37
- return `<defs>
38
- ${text}
39
- <linearGradient id="letter-face" x2="0" y2="1">
40
- <stop stop-color="#ffffff"/><stop offset=".48" stop-color="#e0e9e7"/>
41
- <stop offset="1" stop-color="#9eb4ba"/>
42
- </linearGradient>
43
- </defs>
44
- <g transform="translate(${x} ${height * 0.69}) scale(${scale})">
45
- <use href="#wordmark" transform="translate(9 12)" fill="#080f14" opacity=".35"/>
46
- ${layers}
47
- <use href="#wordmark" fill="url(#letter-face)" stroke="#e8f1ed" stroke-opacity=".4" stroke-width=".5"/>
48
- </g>`;
49
- }
50
-
51
- export function renderDesktopWallpaper(
52
- width: number,
53
- height: number,
54
- avatar: Buffer | null,
55
- accentHex: string | null,
56
- assistantName: string | null = null,
57
- ): Buffer {
58
- const accent = /^#[\da-f]{6}$/i.test(accentHex ?? "")
59
- ? accentHex!
60
- : "#91a6b0";
61
- const cx = width / 2;
62
- const cy = height * 0.46;
63
- const size = Math.min(width, height) * 0.24;
64
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
65
- <defs>
66
- <linearGradient id="base" x2="1" y2="1">
67
- <stop stop-color="#202a30"/><stop offset="1" stop-color="#10171c"/>
68
- </linearGradient>
69
- <radialGradient id="glow">
70
- <stop stop-color="${accent}" stop-opacity=".24"/>
71
- <stop offset="1" stop-color="${accent}" stop-opacity="0"/>
72
- </radialGradient>
73
- <radialGradient id="shadow">
74
- <stop stop-color="#000" stop-opacity=".25"/>
75
- <stop offset="1" stop-color="#000" stop-opacity="0"/>
76
- </radialGradient>
77
- </defs>
78
- <path fill="url(#base)" d="M0 0H${width}V${height}H0z"/>
79
- <ellipse cx="${cx}" cy="${cy}" rx="${height * 0.7}" ry="${height * 0.6}" fill="url(#glow)"/>
80
- <g fill="none" stroke="${accent}">
81
- <circle cx="${cx}" cy="${cy}" r="${size * 0.76}" stroke-opacity=".16"/>
82
- <circle cx="${cx}" cy="${cy}" r="${size * 1.25}" stroke-opacity=".09"/>
83
- <circle cx="${cx}" cy="${cy}" r="${size * 1.9}" stroke-opacity=".05"/>
84
- </g>
85
- ${
86
- avatar
87
- ? `<ellipse cx="${cx}" cy="${cy + size * 0.48}" rx="${size * 0.7}" ry="${size * 0.16}" fill="url(#shadow)"/>
88
- <image x="${cx - size / 2}" y="${cy - size / 2}" width="${size}" height="${size}" href="data:image/png;base64,${avatar.toString("base64")}"/>`
89
- : ""
90
- }
91
- ${wallpaperWordmark(width, height, assistantName)}
92
- </svg>`;
93
- const Resvg = getResvg();
94
- return Buffer.from(new Resvg(svg).render().asPng());
95
- }
96
-
97
- export async function renderCurrentDesktopWallpaper(
98
- width: number,
99
- height: number,
100
- ): Promise<Buffer | null> {
101
- if (!isResvgAvailable()) {
102
- return null;
103
- }
104
- const state = readAvatarState();
105
- const name = getAssistantName();
106
- return renderDesktopWallpaper(
107
- width,
108
- height,
109
- await renderNotificationAvatarPng(state),
110
- resolveNotificationAccentHex(state),
111
- name,
112
- );
113
- }
@@ -1,170 +0,0 @@
1
- import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { pathToFileURL } from "node:url";
4
-
5
- import { escapeXmlAttr, escapeXmlContent } from "../util/xml.js";
6
-
7
- const BUTTON_MASKS = {
8
- close: [0x201, 0x102, 0x084, 0x048, 0x030, 0x030, 0x048, 0x084, 0x102, 0x201],
9
- max: [0x3ff, 0x201, 0x201, 0x201, 0x201, 0x201, 0x201, 0x201, 0x201, 0x3ff],
10
- max_toggled: [
11
- 0x3fc, 0x204, 0x27f, 0x241, 0x241, 0x241, 0x3c1, 0x041, 0x041, 0x07f,
12
- ],
13
- iconify: [0, 0, 0, 0, 0x3ff, 0x3ff, 0, 0, 0, 0],
14
- };
15
-
16
- function windowTheme(accentHex: string | null): string {
17
- const tint = (base: string, amount: number): string => {
18
- if (!accentHex || !/^#[\da-f]{6}$/i.test(accentHex)) {
19
- return base;
20
- }
21
- return (
22
- "#" +
23
- [1, 3, 5]
24
- .map((offset) => {
25
- const from = parseInt(base.slice(offset, offset + 2), 16);
26
- const to = parseInt(accentHex.slice(offset, offset + 2), 16);
27
- return Math.round(from + (to - from) * amount)
28
- .toString(16)
29
- .padStart(2, "0");
30
- })
31
- .join("")
32
- );
33
- };
34
- return `
35
- border.width: 1
36
- padding.width: 8
37
- padding.height: 7
38
- window.client.padding.width: 0
39
- window.client.padding.height: 0
40
- window.handle.width: 3
41
- window.label.text.justify: Center
42
- window.*.label.text.font: shadow=n
43
- window.*.title.bg: Flat Solid
44
- window.*.label.bg: Parentrelative
45
- window.*.handle.bg: Flat Solid
46
- window.*.grip.bg: Flat Solid
47
- window.*.button.*.bg: Parentrelative
48
- window.*.button.*.hover.bg: Flat Solid
49
- window.*.button.*.pressed.bg: Flat Solid
50
- window.active.title.bg: Flat Gradient Horizontal
51
- window.active.title.bg.color: ${tint("#292930", 0.28)}
52
- window.active.title.bg.colorTo: #292930
53
- window.active.border.color: #4a4a54
54
- window.active.title.separator.color: #202026
55
- window.active.label.text.color: #f0f0f4
56
- window.active.handle.bg.color: #292930
57
- window.active.grip.bg.color: #292930
58
- window.active.button.*.image.color: #d4d4dc
59
- window.active.button.*.hover.bg.color: ${tint("#45454f", 0.22)}
60
- window.active.button.*.pressed.bg.color: ${tint("#555561", 0.18)}
61
- window.active.button.*.disabled.image.color: #62626e
62
- window.active.button.close.hover.bg.color: #b64150
63
- window.active.button.close.pressed.bg.color: #963442
64
- window.inactive.title.bg.color: #232329
65
- window.inactive.border.color: #36363f
66
- window.inactive.title.separator.color: #202026
67
- window.inactive.label.text.color: #a6a6b2
68
- window.inactive.handle.bg.color: #232329
69
- window.inactive.grip.bg.color: #232329
70
- window.inactive.button.*.image.color: #858592
71
- window.inactive.button.*.hover.bg.color: #3b3b45
72
- window.inactive.button.*.pressed.bg.color: #4a4a54
73
- window.inactive.button.*.disabled.image.color: #565660
74
- menu.border.width: 1
75
- menu.border.color: #4a4a54
76
- menu.title.bg: Flat Solid
77
- menu.title.bg.color: #292930
78
- menu.title.text.color: #f0f0f4
79
- menu.title.text.font: shadow=n
80
- menu.items.bg: Flat Solid
81
- menu.items.bg.color: #232329
82
- menu.items.text.color: #e6e6ee
83
- menu.items.disabled.text.color: #858592
84
- menu.items.active.bg: Flat Solid
85
- menu.items.active.bg.color: #45454f
86
- menu.items.active.text.color: #ffffff
87
- menu.items.active.disabled.text.color: #a6a6b2
88
- menu.separator.color: #45454f
89
- osd.bg: Flat Solid
90
- osd.bg.color: #292930
91
- osd.border.color: #4a4a54
92
- osd.label.text.color: #f0f0f4
93
- `;
94
- }
95
-
96
- /** Keep Openbox bindings and placement rules while styling its decorations. */
97
- export function writeDesktopWindowTheme(
98
- configDir: string,
99
- home?: string,
100
- accentHex: string | null = null,
101
- ): string {
102
- let source: string | undefined;
103
- let sourcePath = "";
104
- const candidates = [
105
- ...(home ? [join(home, ".config", "openbox", "rc.xml")] : []),
106
- "/etc/xdg/openbox/rc.xml",
107
- ];
108
- for (const path of candidates) {
109
- try {
110
- source = readFileSync(path, "utf8");
111
- sourcePath = path;
112
- break;
113
- } catch (err) {
114
- if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
115
- throw err;
116
- }
117
- }
118
- }
119
- if (!source || !/<theme>[^]*?<\/theme>/.test(source)) {
120
- throw new Error("Openbox theme configuration is unavailable");
121
- }
122
- const root = source.match(/<openbox_config\b[^>]*>/)?.[0];
123
- if (!root || /\bxml:base\s*=/.test(root)) {
124
- throw new Error("Openbox configuration requires its original base URI");
125
- }
126
- // Relative XIncludes keep resolving beside the source configuration.
127
- source = source.replace(root, () =>
128
- root.replace(
129
- "<openbox_config",
130
- `<openbox_config xml:base="${escapeXmlAttr(pathToFileURL(sourcePath).href)}"`,
131
- ),
132
- );
133
- const themeDir = join(configDir, "window-theme");
134
- mkdirSync(join(themeDir, "openbox-3"), { recursive: true });
135
- writeFileSync(
136
- join(themeDir, "openbox-3", "themerc"),
137
- windowTheme(accentHex).trimStart(),
138
- );
139
- for (const [name, rows] of Object.entries(BUTTON_MASKS)) {
140
- const bytes = rows.flatMap((row) => [row & 0xff, row >> 8]);
141
- writeFileSync(
142
- join(themeDir, "openbox-3", `${name}.xbm`),
143
- `#define button_width 10\n#define button_height 10\nstatic unsigned char button_bits[] = {\n${bytes.map((byte) => `0x${byte.toString(16)}`).join(",")}};\n`,
144
- );
145
- }
146
- const fonts = [
147
- "ActiveWindow",
148
- "InactiveWindow",
149
- "MenuHeader",
150
- "MenuItem",
151
- "ActiveOnScreenDisplay",
152
- "InactiveOnScreenDisplay",
153
- ].map(
154
- (place) =>
155
- `<font place="${place}"><name>sans</name><size>10</size><weight>normal</weight><slant>normal</slant></font>`,
156
- );
157
- const theme = `<theme>
158
- <name>${escapeXmlContent(themeDir)}</name>
159
- <titleLayout>LIMC</titleLayout>
160
- <keepBorder>yes</keepBorder>
161
- <animateIconify>yes</animateIconify>
162
- ${fonts.join("\n ")}
163
- </theme>`;
164
- const configPath = join(configDir, "window-manager.xml");
165
- writeFileSync(
166
- configPath,
167
- source.replace(/<theme>[^]*?<\/theme>/, () => theme),
168
- );
169
- return configPath;
170
- }
@@ -1,22 +0,0 @@
1
- # Daemon i18n
2
-
3
- User-facing copy the assistant generates as a hardcoded constant goes
4
- through `assistant/src/i18n/`. The web app has its own catalogs. This
5
- module is for constants the daemon writes or emits.
6
-
7
- ## Rules
8
-
9
- 1. Add a key to `MESSAGE_KEYS` and to every locale in `MESSAGE_CATALOGS`.
10
- 2. Persist the key when a column holds a system constant. Never persist a
11
- translated string.
12
- 3. Resolve with `t(key, locale)` or a typed helper (`resolveConversationTitle`)
13
- at the edge that emits the string.
14
- 4. Do not match stored display strings back to keys. A stored title may be
15
- in any language on purpose.
16
- 5. Locale comes from `Accept-Language` (`localeFromAcceptLanguage`) or an
17
- explicit CLI flag. Default `en` lives in this module, not at each caller.
18
-
19
- Conversation titles persist `conversation.title.generating` /
20
- `conversation.title.untitled`. `resolveConversationTitle` turns those keys
21
- into catalog copy. Legacy English placeholders stay replaceable via the
22
- title service patterns and pass through on the wire until they are retitled.
@@ -1,108 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import {
4
- DEFAULT_LOCALE,
5
- isMessageKey,
6
- localeFromAcceptLanguage,
7
- MESSAGE_CATALOGS,
8
- MESSAGE_KEYS,
9
- negotiateLocale,
10
- resolveConversationTitle,
11
- SUPPORTED_LOCALES,
12
- t,
13
- } from "../index.js";
14
-
15
- describe("negotiateLocale", () => {
16
- test("matches a full shipped tag", () => {
17
- expect(negotiateLocale(["es"])).toBe("es");
18
- expect(negotiateLocale(["zh-TW"])).toBe("zh-TW");
19
- });
20
-
21
- test("falls back from a regional tag to the primary language", () => {
22
- expect(negotiateLocale(["es-MX", "en"])).toBe("es");
23
- });
24
-
25
- test("maps Traditional Chinese tags to zh-TW", () => {
26
- expect(negotiateLocale(["zh-Hant"])).toBe("zh-TW");
27
- expect(negotiateLocale(["zh-HK"])).toBe("zh-TW");
28
- });
29
-
30
- test("defaults when nothing matches", () => {
31
- expect(negotiateLocale([])).toBe(DEFAULT_LOCALE);
32
- expect(negotiateLocale(["xx"])).toBe(DEFAULT_LOCALE);
33
- });
34
- });
35
-
36
- describe("localeFromAcceptLanguage", () => {
37
- test("honors quality values", () => {
38
- expect(localeFromAcceptLanguage("fr;q=0.8, es;q=0.9, en;q=0.5")).toBe(
39
- "es",
40
- );
41
- });
42
-
43
- test("defaults on missing or empty", () => {
44
- expect(localeFromAcceptLanguage(null)).toBe(DEFAULT_LOCALE);
45
- expect(localeFromAcceptLanguage("")).toBe(DEFAULT_LOCALE);
46
- });
47
- });
48
-
49
- describe("catalog completeness", () => {
50
- test("every locale has every key", () => {
51
- const keys = Object.values(MESSAGE_KEYS);
52
- for (const locale of SUPPORTED_LOCALES) {
53
- for (const key of keys) {
54
- expect(MESSAGE_CATALOGS[locale][key].length).toBeGreaterThan(0);
55
- }
56
- }
57
- });
58
- });
59
-
60
- describe("t", () => {
61
- test("resolves generating and untitled per locale", () => {
62
- expect(t(MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING, "en")).toBe(
63
- "Generating title...",
64
- );
65
- expect(t(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED, "en")).toBe(
66
- "Untitled Conversation",
67
- );
68
- expect(t(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED, "es")).toBe(
69
- "Sin título",
70
- );
71
- });
72
- });
73
-
74
- describe("resolveConversationTitle", () => {
75
- test("resolves a stored key and an empty title", () => {
76
- expect(
77
- resolveConversationTitle(MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING, "zh"),
78
- ).toBe("标题生成中...");
79
- expect(resolveConversationTitle(null, "es")).toBe("Sin título");
80
- expect(resolveConversationTitle("")).toBe("Untitled Conversation");
81
- });
82
-
83
- test("passes stored display strings through, including English placeholders", () => {
84
- expect(resolveConversationTitle("Untitled Conversation", "es")).toBe(
85
- "Untitled Conversation",
86
- );
87
- expect(resolveConversationTitle("Generating title...", "zh")).toBe(
88
- "Generating title...",
89
- );
90
- expect(resolveConversationTitle("Auth Middleware Rewrite", "es")).toBe(
91
- "Auth Middleware Rewrite",
92
- );
93
- });
94
-
95
- test("defaults locale inside the helper", () => {
96
- expect(
97
- resolveConversationTitle(MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING),
98
- ).toBe("Generating title...");
99
- });
100
- });
101
-
102
- describe("isMessageKey", () => {
103
- test("recognizes catalog keys only", () => {
104
- expect(isMessageKey(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED)).toBe(true);
105
- expect(isMessageKey("Untitled Conversation")).toBe(false);
106
- expect(isMessageKey("Untitled")).toBe(false);
107
- });
108
- });
package/src/i18n/index.ts DELETED
@@ -1,16 +0,0 @@
1
- export {
2
- DEFAULT_LOCALE,
3
- isSupportedLocale,
4
- localeFromAcceptLanguage,
5
- negotiateLocale,
6
- SUPPORTED_LOCALES,
7
- type SupportedLocale,
8
- } from "./locales.js";
9
- export {
10
- isMessageKey,
11
- MESSAGE_CATALOGS,
12
- MESSAGE_KEYS,
13
- type MessageKey,
14
- t,
15
- } from "./messages.js";
16
- export { resolveConversationTitle } from "./title.js";