@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,59 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- import { RuntimeHttpServer } from "../http-server.js";
4
- import {
5
- mintGatewayToken,
6
- requireHttpAuth,
7
- upgradeHeaders,
8
- } from "./runtime-ws-test-utils.js";
9
-
10
- describe("Watch capture target through the shared runtime upgrade", () => {
11
- let restoreAuth: () => void;
12
- beforeEach(() => {
13
- restoreAuth = requireHttpAuth();
14
- });
15
- afterEach(() => restoreAuth());
16
-
17
- function upgrade(params: string) {
18
- const runtime = new RuntimeHttpServer();
19
- const server = {
20
- requestIP: () => ({ address: "127.0.0.1" }),
21
- upgrade: mock(() => true),
22
- };
23
- const response = runtime["handleWatchStreamUpgrade"](
24
- new Request(
25
- `http://127.0.0.1/v1/watch/stream?token=${mintGatewayToken()}&mimeType=audio/pcm&sampleRate=16000&${params}`,
26
- { headers: upgradeHeaders },
27
- ),
28
- server as unknown as ReturnType<typeof Bun.serve>,
29
- );
30
- return { response, server };
31
- }
32
-
33
- test.each([
34
- ["captureWindowId=4242", { kind: "window", windowId: 4242 }],
35
- ["captureDisplayId=7", { kind: "display", displayId: 7 }],
36
- ["", undefined],
37
- ])("preserves the selected target: %s", (params, captureTarget) => {
38
- const { response, server } = upgrade(params);
39
- expect(response).toBeUndefined();
40
- expect(server.upgrade).toHaveBeenCalledWith(expect.any(Request), {
41
- data: expect.objectContaining({
42
- wsType: "watch-stream",
43
- mimeType: "audio/pcm",
44
- sampleRate: 16000,
45
- captureTarget,
46
- }),
47
- });
48
- });
49
-
50
- test.each([
51
- "captureWindowId=abc",
52
- "captureDisplayId=-1",
53
- "captureWindowId=1&captureDisplayId=2",
54
- ])("refuses an invalid target before upgrading: %s", (params) => {
55
- const { response, server } = upgrade(params);
56
- expect(response?.status).toBe(400);
57
- expect(server.upgrade).not.toHaveBeenCalled();
58
- });
59
- });
@@ -1,316 +0,0 @@
1
- /**
2
- * A model-directed text send to a channel chat, through the channel's
3
- * transport, recorded after the channel acknowledges it.
4
- *
5
- * This is the one send implementation per addressable channel. The
6
- * messaging tool's channel branch and the `channels send` route both call
7
- * it, so a send costs the same and leaves the same record whichever door it
8
- * came through. The transport owns the routing: the caller names a neutral
9
- * target, the transport resolves it into its own callback context, and
10
- * nothing here switches on a channel's name.
11
- *
12
- * Nothing is written before the channel acknowledges. The record carries
13
- * every id the text became, so a failed send leaves no row that reads as
14
- * something the assistant said, and a reaction, edit, or delete naming any
15
- * chunk resolves to the row; the inbound binding a transport asks for is
16
- * written after the same acknowledgement, so a failed send does not move
17
- * where the chat's next message lands. A send into the sending turn's own
18
- * chat and thread writes no row: its tool call and result already sit in
19
- * that conversation's history, and a second assistant row beside the tool
20
- * pair would break history repair.
21
- */
22
-
23
- import { type ChannelId, isChannelId } from "../channels/types.js";
24
- import type {
25
- ProactiveAddress,
26
- ProactiveTarget,
27
- } from "../messaging/providers/channel-transport.js";
28
- import { getTransportForChannel } from "../messaging/providers/index.js";
29
- import { resolveProactiveHomeConversation } from "../notifications/conversation-pairing.js";
30
- import { recordDeliveredChannelPost } from "../notifications/delivered-post-record.js";
31
- import { getConversation } from "../persistence/conversation-crud.js";
32
- import { syncMessageToDisk } from "../persistence/conversation-disk-view.js";
33
- import { getOrCreateConversation } from "../persistence/conversation-key-store.js";
34
- import { buildScopedConversationKey } from "../persistence/delivery-crud.js";
35
- import {
36
- normalizeExternalThreadId,
37
- upsertOutboundBinding,
38
- } from "../persistence/external-conversation-store.js";
39
- import { getLogger } from "../util/logger.js";
40
- import { DAEMON_INTERNAL_ASSISTANT_ID } from "./assistant-scope.js";
41
- import { publishConversationListChanged } from "./sync/resource-sync-events.js";
42
-
43
- const log = getLogger("channel-send");
44
-
45
- /**
46
- * The turn a send is made from, as a snapshot taken when the turn started:
47
- * the channel it arrived on, the chat, and the thread. Read from the turn
48
- * rather than from the live binding, which a concurrent inbound can rewrite
49
- * while the send runs. A caller with no turn (a script) names only the
50
- * conversation it acts for, and the home comparison decides on its own.
51
- */
52
- export interface ChannelSendSender {
53
- readonly conversationId: string;
54
- readonly executionChannel?: string;
55
- readonly requesterChatId?: string;
56
- readonly sourceThreadId?: string;
57
- }
58
-
59
- export interface ChannelSendParams {
60
- readonly channel: string;
61
- readonly target: ProactiveTarget;
62
- readonly text: string;
63
- /** Ask the channel for its rich rendering of the text, where it has one. */
64
- readonly renderRichly?: boolean;
65
- /** The assistant the send acts for; absent or `self` is this assistant. */
66
- readonly assistantId?: string;
67
- readonly sender?: ChannelSendSender;
68
- }
69
-
70
- export interface ChannelSendResult {
71
- readonly channel: ChannelId;
72
- /** The chat the post landed in, in the channel's own id space. */
73
- readonly chatId: string;
74
- /** The thread the post landed in, absent when it is not in one. */
75
- readonly threadId?: string;
76
- /** Every id the channel acknowledged for the text, in send order; never empty. */
77
- readonly messageIds: readonly string[];
78
- /** The id of the final post, the one a later edit or withdrawal addresses. */
79
- readonly lastMessageId: string;
80
- /** The conversation the post was recorded in, absent when it was not. */
81
- readonly recordedIn?: string;
82
- }
83
-
84
- /** The channel cannot be reached from a named target. */
85
- export class ChannelNotAddressableError extends Error {
86
- constructor(
87
- readonly channel: string,
88
- reason: string,
89
- ) {
90
- super(`Channel "${channel}" ${reason}.`);
91
- this.name = "ChannelNotAddressableError";
92
- }
93
- }
94
-
95
- /**
96
- * The transport ran but the channel did not acknowledge the send. A success
97
- * that names no message id counts: an id is what makes the post addressable
98
- * afterwards, and without one there is nothing to record.
99
- */
100
- export class ChannelSendFailedError extends Error {
101
- constructor(
102
- readonly channel: ChannelId,
103
- reason = "did not acknowledge the send",
104
- ) {
105
- super(`Channel "${channel}" ${reason}.`);
106
- this.name = "ChannelSendFailedError";
107
- }
108
- }
109
-
110
- /**
111
- * Whether a channel's transport declares it can be addressed from a named
112
- * chat. A channel with no transport, or one that only answers on an inbound
113
- * callback, is not.
114
- */
115
- export function isProactivelyAddressable(channel: string): boolean {
116
- return getTransportForChannel(channel)?.addressFor !== undefined;
117
- }
118
-
119
- /**
120
- * Deliver `text` to `target` on `channel` and record what was sent.
121
- *
122
- * Throws {@link ChannelNotAddressableError} before any delivery when the
123
- * channel or the target shape cannot be addressed, and
124
- * {@link ChannelSendFailedError} when the transport reports no
125
- * acknowledgement or no message id. A binding or recording failure never
126
- * fails the send: the message is already out.
127
- */
128
- export async function sendChannelText(
129
- params: ChannelSendParams,
130
- ): Promise<ChannelSendResult> {
131
- const { channel, target, text } = params;
132
- if (!isChannelId(channel)) {
133
- throw new ChannelNotAddressableError(channel, "is not a channel");
134
- }
135
- const transport = getTransportForChannel(channel);
136
- if (!transport?.addressFor) {
137
- throw new ChannelNotAddressableError(
138
- channel,
139
- "cannot be addressed from a named chat",
140
- );
141
- }
142
- const address = transport.addressFor(target);
143
- if (!address) {
144
- throw new ChannelNotAddressableError(channel, "cannot address that chat");
145
- }
146
-
147
- const result = await transport.deliver(address.ctx, {
148
- chatId: address.chatId,
149
- text,
150
- ...(params.renderRichly ? { renderRichly: true } : {}),
151
- ...(params.assistantId ? { assistantId: params.assistantId } : {}),
152
- });
153
- if (!result.ok) {
154
- throw new ChannelSendFailedError(channel);
155
- }
156
- const messageIds = result.messageIds ?? (result.ts ? [result.ts] : []);
157
- const lastMessageId = result.ts ?? messageIds[messageIds.length - 1];
158
- if (!lastMessageId) {
159
- throw new ChannelSendFailedError(channel, "acknowledged no message id");
160
- }
161
-
162
- const actsForSelf =
163
- !params.assistantId || params.assistantId === DAEMON_INTERNAL_ASSISTANT_ID;
164
- if (transport.bindsChatOnProactiveSend && actsForSelf) {
165
- bindChatForNextInbound(channel, address);
166
- }
167
-
168
- const recordedIn = await recordProactivePost({
169
- channel,
170
- address,
171
- text,
172
- messageIds,
173
- sender: params.sender,
174
- });
175
-
176
- return {
177
- channel,
178
- chatId: address.chatId,
179
- ...(address.threadId ? { threadId: address.threadId } : {}),
180
- messageIds,
181
- lastMessageId,
182
- ...(recordedIn ? { recordedIn } : {}),
183
- };
184
- }
185
-
186
- /**
187
- * Bind the chat's inbound conversation to the delivered chat and thread, so
188
- * the next message from there resolves to the conversation ingress uses for
189
- * it. The key is the one ingress builds, thread-scoped where the channel
190
- * scopes conversations by thread, so a topic's replies continue the topic's
191
- * conversation. Runs after acknowledgement: the message is out, so the
192
- * binding it asks for is owed. Best effort: a binding failure must not fail
193
- * the send.
194
- */
195
- function bindChatForNextInbound(
196
- channel: ChannelId,
197
- address: ProactiveAddress,
198
- ): void {
199
- try {
200
- const { conversationId, created } = getOrCreateConversation(
201
- buildScopedConversationKey(channel, address.chatId, address.threadId),
202
- );
203
- // Published before the binding, not after: the conversation exists as
204
- // soon as it is minted, the record that follows finds it rather than
205
- // minting it, and a binding that throws must not leave a conversation
206
- // holding a real message out of the list.
207
- if (created) {
208
- publishConversationListChanged("created");
209
- }
210
- upsertOutboundBinding({
211
- conversationId,
212
- sourceChannel: channel,
213
- externalChatId: address.chatId,
214
- externalThreadId: address.threadId ?? null,
215
- });
216
- } catch (e) {
217
- log.warn(
218
- {
219
- err: e,
220
- channel,
221
- externalChatId: address.chatId,
222
- externalThreadId: address.threadId,
223
- },
224
- "Failed to bind the chat's inbound conversation after the send",
225
- );
226
- }
227
- }
228
-
229
- /**
230
- * Record the acknowledged post where the chat's proactive posts live, so
231
- * the chat's own conversation and `recall` can see what was sent from
232
- * elsewhere. The row names the thread the post landed in, as the transport
233
- * resolved it. Returns the conversation it was recorded in, or `undefined`
234
- * when nothing was recorded: the send went into the sending turn's own chat
235
- * and thread, or the home is the sender.
236
- */
237
- async function recordProactivePost(params: {
238
- channel: ChannelId;
239
- address: ProactiveAddress;
240
- text: string;
241
- messageIds: readonly string[];
242
- sender: ChannelSendSender | undefined;
243
- }): Promise<string | undefined> {
244
- const { channel, address, sender } = params;
245
- const [firstId, ...restIds] = params.messageIds;
246
- if (!firstId) {
247
- return undefined;
248
- }
249
- try {
250
- if (
251
- sender &&
252
- isSendIntoOwnChat(sender, channel, address.chatId, address.threadId)
253
- ) {
254
- return undefined;
255
- }
256
- const home = await resolveProactiveHomeConversation({
257
- sourceChannel: channel,
258
- externalChatId: address.chatId,
259
- threadId: address.threadId,
260
- source: "notification",
261
- conversationType: "background",
262
- title: `Messages to ${address.chatId}`,
263
- });
264
- if (home.conversationId === sender?.conversationId) {
265
- return undefined;
266
- }
267
- const recorded = await recordDeliveredChannelPost({
268
- conversationId: home.conversationId,
269
- channel,
270
- externalChatId: address.chatId,
271
- ...(address.threadId ? { threadId: address.threadId } : {}),
272
- text: params.text,
273
- providerMessageId: firstId,
274
- ...(restIds.length > 0 ? { additionalProviderMessageIds: restIds } : {}),
275
- ...(sender ? { crossPostedFrom: sender.conversationId } : {}),
276
- });
277
- const homeConversation = getConversation(home.conversationId);
278
- if (homeConversation) {
279
- syncMessageToDisk(
280
- home.conversationId,
281
- recorded.messageId,
282
- homeConversation.createdAt,
283
- );
284
- }
285
- return home.conversationId;
286
- } catch (e) {
287
- log.warn(
288
- { err: e, channel, externalChatId: address.chatId },
289
- "Failed to record the sent message in the chat's conversation",
290
- );
291
- return undefined;
292
- }
293
- }
294
-
295
- /**
296
- * True when the delivered chat and thread are the ones the sending turn
297
- * arrived in. Thread ids compare in the binding store's normalized form, so
298
- * an absent thread on both sides matches and a thread-less delivery never
299
- * matches a turn that arrived in a thread.
300
- */
301
- function isSendIntoOwnChat(
302
- sender: ChannelSendSender,
303
- channel: ChannelId,
304
- chatId: string,
305
- deliveredThreadId: string | undefined,
306
- ): boolean {
307
- if (!sender.executionChannel || !sender.requesterChatId) {
308
- return false;
309
- }
310
- return (
311
- sender.executionChannel === channel &&
312
- sender.requesterChatId === chatId &&
313
- normalizeExternalThreadId(sender.sourceThreadId) ===
314
- normalizeExternalThreadId(deliveredThreadId)
315
- );
316
- }
@@ -1,205 +0,0 @@
1
- /**
2
- * Tests for POST apps/:id/refresh (apps_refresh) in the shared route table.
3
- *
4
- * `mock.module` is process-global in Bun. Stubs delegate to the real
5
- * implementations unless this file's tests are running.
6
- */
7
-
8
- import { mkdirSync, rmSync, writeFileSync } from "node:fs";
9
- import { tmpdir } from "node:os";
10
- import { join } from "node:path";
11
- import {
12
- afterAll,
13
- afterEach,
14
- beforeAll,
15
- beforeEach,
16
- describe,
17
- expect,
18
- mock,
19
- test,
20
- } from "bun:test";
21
-
22
- import type { CompileResult } from "../../../bundler/app-compiler.js";
23
-
24
- let mockActive = false;
25
- beforeAll(() => {
26
- mockActive = true;
27
- });
28
- afterAll(() => {
29
- mockActive = false;
30
- });
31
-
32
- const realCompiler = { ...(await import("../../../bundler/app-compiler.js")) };
33
- const realNotify = {
34
- ...(await import("../../../daemon/app-change-notify.js")),
35
- };
36
-
37
- const compileApp = mock(
38
- async (
39
- _appDir: Parameters<typeof realCompiler.compileApp>[0],
40
- ): Promise<CompileResult> => ({
41
- ok: true,
42
- errors: [],
43
- warnings: [],
44
- durationMs: 11,
45
- }),
46
- );
47
- const notifyAppSurfacesChanged = mock(
48
- (..._args: Parameters<typeof realNotify.notifyAppSurfacesChanged>) => {},
49
- );
50
-
51
- mock.module("../../../bundler/app-compiler.js", () => ({
52
- ...realCompiler,
53
- compileApp: (...args: Parameters<typeof realCompiler.compileApp>) => {
54
- if (!mockActive) {
55
- return realCompiler.compileApp(...args);
56
- }
57
- return compileApp(...args);
58
- },
59
- }));
60
-
61
- mock.module("../../../daemon/app-change-notify.js", () => ({
62
- ...realNotify,
63
- notifyAppSurfacesChanged: (
64
- ...args: Parameters<typeof realNotify.notifyAppSurfacesChanged>
65
- ) => {
66
- if (!mockActive) {
67
- return realNotify.notifyAppSurfacesChanged(...args);
68
- }
69
- return notifyAppSurfacesChanged(...args);
70
- },
71
- }));
72
-
73
- const { createApp } = await import("../../../apps/app-store.js");
74
- const { getWorkspacePluginsDir } = await import("../../../util/platform.js");
75
- const { ROUTES } = await import("../app-management-routes.js");
76
-
77
- function findHandler(operationId: string) {
78
- const route = ROUTES.find((r) => r.operationId === operationId);
79
- if (!route) {
80
- throw new Error(`Route ${operationId} not found`);
81
- }
82
- return route.handler;
83
- }
84
-
85
- const handleRefreshApp = findHandler("apps_refresh");
86
-
87
- let workspaceDir: string;
88
- let previousWorkspaceDir: string | undefined;
89
-
90
- beforeEach(() => {
91
- previousWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
92
- workspaceDir = join(
93
- tmpdir(),
94
- `vellum-apps-refresh-${Date.now()}-${Math.random().toString(36).slice(2)}`,
95
- );
96
- process.env.VELLUM_WORKSPACE_DIR = workspaceDir;
97
- compileApp.mockClear();
98
- notifyAppSurfacesChanged.mockClear();
99
- compileApp.mockResolvedValue({
100
- ok: true,
101
- errors: [],
102
- warnings: [],
103
- durationMs: 11,
104
- });
105
- });
106
-
107
- afterEach(() => {
108
- rmSync(workspaceDir, { recursive: true, force: true });
109
- if (previousWorkspaceDir === undefined) {
110
- delete process.env.VELLUM_WORKSPACE_DIR;
111
- } else {
112
- process.env.VELLUM_WORKSPACE_DIR = previousWorkspaceDir;
113
- }
114
- });
115
-
116
- describe("apps_refresh route", () => {
117
- test("is registered on the shared app-management ROUTES table", () => {
118
- expect(typeof handleRefreshApp).toBe("function");
119
- });
120
-
121
- test("compiles a workspace app and notifies surfaces", async () => {
122
- const created = createApp({
123
- name: "Budget",
124
- schemaJson: "{}",
125
- htmlDefinition: "<h1>Budget</h1>",
126
- });
127
-
128
- const result = await handleRefreshApp({
129
- pathParams: { id: created.id },
130
- });
131
-
132
- expect(compileApp).toHaveBeenCalled();
133
- expect(notifyAppSurfacesChanged).toHaveBeenCalledWith(created.id, {
134
- fileChange: true,
135
- });
136
- expect(result).toEqual({
137
- ok: true,
138
- appId: created.id,
139
- name: "Budget",
140
- compiled: true,
141
- compile_duration_ms: 11,
142
- });
143
- });
144
-
145
- test("compiles a plugin app in place", async () => {
146
- const pluginName = `charts-${Math.random().toString(36).slice(2, 8)}`;
147
- const pluginDir = join(getWorkspacePluginsDir(), pluginName);
148
- mkdirSync(join(pluginDir, "apps", "viewer"), { recursive: true });
149
- writeFileSync(
150
- join(pluginDir, "package.json"),
151
- JSON.stringify({ name: pluginName, version: "1.0.0" }),
152
- );
153
- writeFileSync(join(pluginDir, "apps", "viewer", "index.html"), "<h1></h1>");
154
- const pluginAppId = `plugins~${pluginName}~viewer`;
155
-
156
- const result = await handleRefreshApp({
157
- pathParams: { id: pluginAppId },
158
- });
159
-
160
- expect(compileApp).toHaveBeenCalledWith(
161
- join(pluginDir, "apps", "viewer"),
162
- );
163
- expect(notifyAppSurfacesChanged).toHaveBeenCalledWith(pluginAppId, {
164
- fileChange: true,
165
- });
166
- expect(result).toMatchObject({
167
- ok: true,
168
- appId: pluginAppId,
169
- name: "viewer",
170
- compiled: true,
171
- });
172
- });
173
-
174
- test("returns compile errors without throwing", async () => {
175
- const created = createApp({
176
- name: "Budget",
177
- schemaJson: "{}",
178
- htmlDefinition: "<h1>Budget</h1>",
179
- });
180
- compileApp.mockImplementationOnce(async () => ({
181
- ok: false,
182
- errors: [{ text: "Could not resolve foo" }],
183
- warnings: [],
184
- durationMs: 4,
185
- }));
186
-
187
- const result = await handleRefreshApp({
188
- pathParams: { id: created.id },
189
- });
190
-
191
- expect(result).toMatchObject({
192
- compiled: false,
193
- compile_errors: [{ text: "Could not resolve foo" }],
194
- });
195
- expect(notifyAppSurfacesChanged).toHaveBeenCalled();
196
- });
197
-
198
- test("throws not found for an unknown app", async () => {
199
- await expect(
200
- handleRefreshApp({ pathParams: { id: "missing" } }),
201
- ).rejects.toMatchObject({
202
- statusCode: 404,
203
- });
204
- });
205
- });
@@ -1,127 +0,0 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- // The route is a thin door over the send function: it validates the body
4
- // against its declared schema and maps the function's refusals onto route
5
- // errors, so the CLI and the messaging tool share one seam.
6
-
7
- const sendCalls: Array<Record<string, unknown>> = [];
8
- let sendImpl: (params: Record<string, unknown>) => Promise<unknown> = async (
9
- params,
10
- ) => ({
11
- channel: params.channel,
12
- chatId: "C123",
13
- messageIds: ["1700000000.000100"],
14
- lastMessageId: "1700000000.000100",
15
- recordedIn: "home-1",
16
- });
17
-
18
- class ChannelNotAddressableError extends Error {}
19
- class ChannelSendFailedError extends Error {}
20
-
21
- mock.module("../../channel-send.js", () => ({
22
- ChannelNotAddressableError,
23
- ChannelSendFailedError,
24
- sendChannelText: async (params: Record<string, unknown>) => {
25
- sendCalls.push(params);
26
- return sendImpl(params);
27
- },
28
- }));
29
-
30
- const { ROUTES } = await import("../channel-send-routes.js");
31
- const { BadGatewayError, BadRequestError } = await import("../errors.js");
32
-
33
- const route = ROUTES.find((r) => r.operationId === "channels_send_post")!;
34
-
35
- beforeEach(() => {
36
- sendCalls.length = 0;
37
- });
38
-
39
- describe("POST channels/send", () => {
40
- test("is registered as a chat write for actor principals", () => {
41
- expect(route).toBeDefined();
42
- expect(route.endpoint).toBe("channels/send");
43
- expect(route.method).toBe("POST");
44
- expect(route.policy?.requiredScopes).toEqual(["chat.write"]);
45
- });
46
-
47
- test("hands a valid body to the send function and returns its result", async () => {
48
- const body = {
49
- channel: "slack",
50
- target: { kind: "chat", chatId: "C123", threadId: "1690000000.000001" },
51
- text: "hello",
52
- renderRichly: true,
53
- };
54
- const result = await route.handler({ body });
55
- expect(sendCalls).toEqual([body]);
56
- expect(result).toMatchObject({
57
- channel: "slack",
58
- messageIds: ["1700000000.000100"],
59
- recordedIn: "home-1",
60
- });
61
- });
62
-
63
- test("names no sending turn, so a caller cannot claim a send was its own chat's", async () => {
64
- await route.handler({
65
- body: {
66
- channel: "slack",
67
- target: { kind: "chat", chatId: "C123" },
68
- text: "hello",
69
- sender: {
70
- conversationId: "conv-A",
71
- executionChannel: "slack",
72
- requesterChatId: "C123",
73
- },
74
- },
75
- });
76
- expect(sendCalls).toHaveLength(1);
77
- expect(sendCalls[0]).not.toHaveProperty("sender");
78
- });
79
-
80
- test("rejects a malformed body before calling the send function", async () => {
81
- await expect(
82
- route.handler({
83
- body: { channel: "slack", target: { kind: "chat" }, text: "" },
84
- }),
85
- ).rejects.toBeInstanceOf(BadRequestError);
86
- await expect(
87
- route.handler({
88
- body: {
89
- channel: "not-a-channel",
90
- target: { kind: "chat", chatId: "C1" },
91
- text: "hi",
92
- },
93
- }),
94
- ).rejects.toBeInstanceOf(BadRequestError);
95
- expect(sendCalls).toEqual([]);
96
- });
97
-
98
- test("maps the send function's refusals onto route errors", async () => {
99
- sendImpl = async () => {
100
- throw new ChannelNotAddressableError(
101
- 'Channel "email" is not addressable',
102
- );
103
- };
104
- await expect(
105
- route.handler({
106
- body: {
107
- channel: "email",
108
- target: { kind: "chat", chatId: "x" },
109
- text: "hi",
110
- },
111
- }),
112
- ).rejects.toBeInstanceOf(BadRequestError);
113
-
114
- sendImpl = async () => {
115
- throw new ChannelSendFailedError("not acknowledged");
116
- };
117
- await expect(
118
- route.handler({
119
- body: {
120
- channel: "slack",
121
- target: { kind: "chat", chatId: "C1" },
122
- text: "hi",
123
- },
124
- }),
125
- ).rejects.toBeInstanceOf(BadGatewayError);
126
- });
127
- });