@vellumai/assistant 0.7.2 → 0.7.3

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 (347) hide show
  1. package/ARCHITECTURE.md +16 -1
  2. package/docs/architecture/memory.md +5 -2
  3. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
  4. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
  5. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  6. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  7. package/openapi.yaml +449 -22
  8. package/package.json +1 -1
  9. package/src/__tests__/app-control-flow.test.ts +21 -11
  10. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  11. package/src/__tests__/assistant-event.test.ts +0 -10
  12. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
  13. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
  14. package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
  15. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  16. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  18. package/src/__tests__/channel-readiness-service.test.ts +4 -2
  19. package/src/__tests__/config-loader-backfill.test.ts +379 -0
  20. package/src/__tests__/config-schema.test.ts +1 -0
  21. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
  22. package/src/__tests__/config-watcher.test.ts +140 -69
  23. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  24. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  25. package/src/__tests__/context-search-fanout.test.ts +0 -1
  26. package/src/__tests__/context-search-memory-source.test.ts +3 -7
  27. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  28. package/src/__tests__/context-search-pkb-source.test.ts +0 -1
  29. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  30. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  31. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  32. package/src/__tests__/conversation-agent-loop.test.ts +454 -5
  33. package/src/__tests__/conversation-error.test.ts +150 -3
  34. package/src/__tests__/conversation-process-callsite.test.ts +43 -0
  35. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  36. package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
  37. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  38. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  39. package/src/__tests__/conversation-store.test.ts +0 -18
  40. package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
  41. package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
  42. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
  43. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  44. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  45. package/src/__tests__/credentials-cli.test.ts +7 -0
  46. package/src/__tests__/cu-unified-flow.test.ts +176 -10
  47. package/src/__tests__/date-context.test.ts +164 -2
  48. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  49. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  50. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  51. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  52. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  53. package/src/__tests__/disk-usage.test.ts +150 -0
  54. package/src/__tests__/events-client-registration.test.ts +52 -0
  55. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  56. package/src/__tests__/file-write-tool.test.ts +4 -10
  57. package/src/__tests__/filing-service.test.ts +3 -4
  58. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  59. package/src/__tests__/heartbeat-service.test.ts +260 -11
  60. package/src/__tests__/host-app-control-proxy.test.ts +195 -25
  61. package/src/__tests__/host-bash-proxy.test.ts +227 -34
  62. package/src/__tests__/host-bash-routes.test.ts +178 -13
  63. package/src/__tests__/host-cu-proxy.test.ts +210 -3
  64. package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
  65. package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
  66. package/src/__tests__/host-file-proxy.test.ts +268 -6
  67. package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
  68. package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
  69. package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
  70. package/src/__tests__/http-user-message-parity.test.ts +107 -1
  71. package/src/__tests__/injector-chain.test.ts +18 -6
  72. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  73. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  74. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  75. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  77. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  78. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  79. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  80. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  81. package/src/__tests__/openai-provider.test.ts +45 -8
  82. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  83. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  84. package/src/__tests__/platform.test.ts +2 -1
  85. package/src/__tests__/playbook-execution.test.ts +0 -43
  86. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  87. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
  88. package/src/__tests__/provider-tool-name.test.ts +23 -0
  89. package/src/__tests__/relay-server.test.ts +15 -4
  90. package/src/__tests__/runtime-events-sse.test.ts +4 -8
  91. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  92. package/src/__tests__/secret-ingress-http.test.ts +0 -1
  93. package/src/__tests__/suggestion-routes.test.ts +46 -0
  94. package/src/__tests__/twilio-validation.test.ts +2 -2
  95. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  96. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  97. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  98. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
  99. package/src/approvals/guardian-decision-primitive.ts +13 -0
  100. package/src/approvals/guardian-request-resolvers.ts +16 -17
  101. package/src/backup/snapshot-lock.ts +2 -27
  102. package/src/bundler/compiler-tools.ts +3 -2
  103. package/src/calls/call-conversation-messages.ts +46 -10
  104. package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
  105. package/src/cli/commands/bash.ts +35 -108
  106. package/src/cli/commands/contacts.ts +64 -25
  107. package/src/cli/commands/credentials.ts +56 -0
  108. package/src/cli/commands/memory-v2.ts +7 -6
  109. package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
  110. package/src/cli/commands/oauth/connect.ts +127 -1
  111. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
  112. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
  113. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  114. package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
  115. package/src/cli/commands/platform/index.ts +16 -7
  116. package/src/cli/commands/status.ts +57 -0
  117. package/src/cli/program.ts +4 -2
  118. package/src/config/assistant-feature-flags.ts +13 -3
  119. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  120. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
  121. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  122. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  123. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  124. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  125. package/src/config/env.ts +0 -8
  126. package/src/config/feature-flag-registry.json +27 -3
  127. package/src/config/loader.ts +127 -8
  128. package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
  129. package/src/config/schemas/call-site-catalog.ts +14 -0
  130. package/src/config/schemas/channels.ts +0 -5
  131. package/src/config/schemas/heartbeat.ts +1 -1
  132. package/src/config/schemas/llm.ts +2 -0
  133. package/src/config/schemas/memory-lifecycle.ts +13 -0
  134. package/src/config/schemas/memory-v2.ts +75 -11
  135. package/src/config/schemas/platform.ts +43 -3
  136. package/src/config/schemas/services.ts +28 -0
  137. package/src/config/seed-inference-profiles.ts +230 -33
  138. package/src/contacts/contact-store.ts +0 -25
  139. package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
  140. package/src/daemon/assistant-attachments.ts +4 -4
  141. package/src/daemon/config-watcher.ts +85 -57
  142. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  143. package/src/daemon/conversation-agent-loop.ts +170 -33
  144. package/src/daemon/conversation-error.ts +87 -15
  145. package/src/daemon/conversation-lifecycle.ts +1 -3
  146. package/src/daemon/conversation-process.ts +8 -0
  147. package/src/daemon/conversation-runtime-assembly.ts +26 -0
  148. package/src/daemon/conversation-store.ts +2 -2
  149. package/src/daemon/conversation-surfaces.ts +195 -15
  150. package/src/daemon/conversation-tool-setup.ts +57 -14
  151. package/src/daemon/conversation.ts +17 -22
  152. package/src/daemon/date-context.ts +71 -22
  153. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  154. package/src/daemon/disk-pressure-guard.ts +343 -0
  155. package/src/daemon/disk-pressure-policy.ts +163 -0
  156. package/src/daemon/handlers/shared.ts +0 -1
  157. package/src/daemon/handlers/skills.ts +3 -4
  158. package/src/daemon/host-app-control-proxy.ts +137 -41
  159. package/src/daemon/host-bash-proxy.ts +46 -21
  160. package/src/daemon/host-cu-proxy.ts +49 -3
  161. package/src/daemon/host-file-proxy.ts +43 -7
  162. package/src/daemon/host-transfer-proxy.ts +95 -4
  163. package/src/daemon/lifecycle.ts +79 -28
  164. package/src/daemon/meet-host-supervisor.ts +4 -4
  165. package/src/daemon/meet-manifest-loader.ts +0 -1
  166. package/src/daemon/memory-v2-startup.ts +14 -4
  167. package/src/daemon/message-protocol.ts +3 -0
  168. package/src/daemon/message-types/conversations.ts +4 -0
  169. package/src/daemon/message-types/disk-pressure.ts +9 -0
  170. package/src/daemon/message-types/messages.ts +3 -0
  171. package/src/daemon/profiler-run-store.ts +5 -5
  172. package/src/daemon/tool-setup-types.ts +2 -2
  173. package/src/documents/document-store.ts +85 -0
  174. package/src/filing/filing-service.ts +30 -5
  175. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
  176. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
  177. package/src/heartbeat/heartbeat-run-store.ts +13 -0
  178. package/src/heartbeat/heartbeat-service.ts +205 -31
  179. package/src/home/feed-scheduler.ts +18 -0
  180. package/src/inbound/platform-callback-registration.ts +8 -15
  181. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  182. package/src/ipc/assistant-server.ts +56 -2
  183. package/src/ipc/gateway-client.ts +37 -3
  184. package/src/live-voice/live-voice-archive.ts +4 -4
  185. package/src/live-voice/protocol.ts +5 -7
  186. package/src/media/image-service.ts +1 -7
  187. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  188. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
  189. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  190. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  191. package/src/memory/admin.ts +5 -9
  192. package/src/memory/context-search/agent-runner.ts +19 -2
  193. package/src/memory/context-search/sources/conversations.ts +2 -11
  194. package/src/memory/context-search/sources/memory-v2.ts +5 -4
  195. package/src/memory/context-search/sources/memory.ts +0 -1
  196. package/src/memory/context-search/types.ts +0 -1
  197. package/src/memory/conversation-crud.ts +4 -12
  198. package/src/memory/db-init.ts +2 -0
  199. package/src/memory/embedding-runtime-manager.ts +119 -5
  200. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +32 -21
  201. package/src/memory/graph/conversation-graph-memory.ts +42 -54
  202. package/src/memory/graph/extraction.ts +1 -3
  203. package/src/memory/graph/graph-search.test.ts +10 -67
  204. package/src/memory/graph/graph-search.ts +1 -20
  205. package/src/memory/graph/retriever.test.ts +6 -0
  206. package/src/memory/graph/retriever.ts +6 -10
  207. package/src/memory/indexer.ts +54 -45
  208. package/src/memory/job-handlers/backfill.ts +2 -11
  209. package/src/memory/job-handlers/cleanup.ts +43 -0
  210. package/src/memory/job-handlers/embedding.ts +6 -8
  211. package/src/memory/job-handlers/summarization.ts +2 -7
  212. package/src/memory/jobs-store.ts +48 -0
  213. package/src/memory/jobs-worker.ts +81 -43
  214. package/src/memory/memory-v2-activation-log-store.ts +32 -14
  215. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  216. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  217. package/src/memory/migrations/index.ts +1 -0
  218. package/src/memory/pkb/pkb-search.test.ts +6 -0
  219. package/src/memory/qdrant-client.ts +0 -13
  220. package/src/memory/rerank-local.ts +374 -0
  221. package/src/memory/search/semantic.ts +6 -67
  222. package/src/memory/trace-event-store.ts +1 -17
  223. package/src/memory/v2/__tests__/activation.test.ts +311 -250
  224. package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
  225. package/src/memory/v2/__tests__/injection.test.ts +157 -167
  226. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  227. package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
  228. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  229. package/src/memory/v2/__tests__/sim.test.ts +5 -199
  230. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  231. package/src/memory/v2/__tests__/static-context.test.ts +76 -1
  232. package/src/memory/v2/activation.ts +149 -156
  233. package/src/memory/v2/consolidation-job.ts +62 -12
  234. package/src/memory/v2/injection.ts +47 -60
  235. package/src/memory/v2/prompts/consolidation.ts +36 -1
  236. package/src/memory/v2/qdrant.ts +99 -0
  237. package/src/memory/v2/reranker.ts +177 -0
  238. package/src/memory/v2/sim.ts +10 -84
  239. package/src/memory/v2/skill-content.ts +4 -3
  240. package/src/memory/v2/skill-store.ts +82 -59
  241. package/src/memory/v2/static-context.ts +22 -0
  242. package/src/memory/v2/types.ts +10 -10
  243. package/src/notifications/copy-composer.ts +13 -0
  244. package/src/notifications/signal.ts +4 -0
  245. package/src/oauth/AGENTS.md +3 -1
  246. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  247. package/src/oauth/connect-orchestrator.ts +2 -0
  248. package/src/oauth/connection-resolver.test.ts +66 -1
  249. package/src/oauth/connection-resolver.ts +55 -1
  250. package/src/oauth/oauth-connect-state.ts +77 -0
  251. package/src/oauth/seed-providers.ts +58 -1
  252. package/src/plugins/defaults/injectors.ts +35 -2
  253. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  254. package/src/plugins/types.ts +7 -0
  255. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  256. package/src/proactive-artifact/decision.test.ts +226 -0
  257. package/src/proactive-artifact/decision.ts +165 -0
  258. package/src/proactive-artifact/index.ts +7 -0
  259. package/src/proactive-artifact/job.test.ts +867 -0
  260. package/src/proactive-artifact/job.ts +352 -0
  261. package/src/proactive-artifact/message-copy.ts +41 -0
  262. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  263. package/src/proactive-artifact/trigger-state.ts +119 -0
  264. package/src/prompts/normalize-onboarding.ts +80 -0
  265. package/src/prompts/persona-resolver.ts +101 -9
  266. package/src/prompts/system-prompt.ts +21 -7
  267. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  268. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  269. package/src/providers/model-intents.ts +7 -0
  270. package/src/providers/openrouter/client.ts +8 -0
  271. package/src/providers/retry.ts +50 -0
  272. package/src/providers/types.ts +1 -0
  273. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  274. package/src/runtime/agent-wake.ts +238 -100
  275. package/src/runtime/assistant-event-hub.ts +36 -6
  276. package/src/runtime/assistant-event.ts +0 -1
  277. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  278. package/src/runtime/auth/route-policy.ts +14 -1
  279. package/src/runtime/auth/same-actor.ts +216 -0
  280. package/src/runtime/channel-retry-sweep.ts +65 -1
  281. package/src/runtime/guardian-reply-router.ts +10 -0
  282. package/src/runtime/local-actor-identity.ts +52 -11
  283. package/src/runtime/pending-interactions.ts +8 -0
  284. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
  286. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  287. package/src/runtime/routes/client-routes.ts +20 -2
  288. package/src/runtime/routes/contact-routes.ts +0 -25
  289. package/src/runtime/routes/conversation-routes.ts +35 -26
  290. package/src/runtime/routes/debug-bash-routes.ts +163 -0
  291. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  292. package/src/runtime/routes/document-pdf-renderer.ts +6 -2
  293. package/src/runtime/routes/documents-routes.ts +2 -75
  294. package/src/runtime/routes/events-routes.ts +41 -9
  295. package/src/runtime/routes/host-bash-routes.ts +23 -3
  296. package/src/runtime/routes/host-cu-routes.ts +33 -6
  297. package/src/runtime/routes/host-file-routes.ts +32 -6
  298. package/src/runtime/routes/host-transfer-routes.ts +79 -16
  299. package/src/runtime/routes/identity-routes.ts +7 -138
  300. package/src/runtime/routes/inbound-message-handler.ts +77 -12
  301. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
  302. package/src/runtime/routes/index.ts +6 -0
  303. package/src/runtime/routes/memory-item-routes.test.ts +41 -15
  304. package/src/runtime/routes/memory-v2-routes.ts +33 -0
  305. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  306. package/src/runtime/verification-outbound-actions.ts +4 -4
  307. package/src/schedule/run-script.ts +37 -5
  308. package/src/schedule/scheduler.ts +20 -1
  309. package/src/security/encrypted-store.ts +2 -0
  310. package/src/security/secure-keys.ts +55 -0
  311. package/src/skills/remote-skill-policy.ts +4 -10
  312. package/src/subagent/index.ts +1 -7
  313. package/src/subagent/manager.ts +1 -15
  314. package/src/tasks/task-runner.ts +0 -1
  315. package/src/tasks/task-store.ts +0 -3
  316. package/src/tools/background-tool-registry.ts +17 -3
  317. package/src/tools/host-filesystem/edit.test.ts +151 -0
  318. package/src/tools/host-filesystem/edit.ts +43 -1
  319. package/src/tools/host-filesystem/read.test.ts +129 -0
  320. package/src/tools/host-filesystem/read.ts +43 -1
  321. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  322. package/src/tools/host-filesystem/transfer.ts +56 -11
  323. package/src/tools/host-filesystem/write.test.ts +134 -0
  324. package/src/tools/host-filesystem/write.ts +43 -1
  325. package/src/tools/host-terminal/host-shell.ts +13 -6
  326. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  327. package/src/tools/memory/register.test.ts +12 -9
  328. package/src/tools/memory/register.ts +1 -2
  329. package/src/tools/provider-tool-name.ts +28 -0
  330. package/src/tools/registry.ts +30 -9
  331. package/src/tools/terminal/shell.ts +9 -1
  332. package/src/tools/tool-approval-handler.ts +31 -6
  333. package/src/tools/types.ts +24 -2
  334. package/src/tts/provider-catalog.ts +3 -5
  335. package/src/util/disk-usage.ts +138 -0
  336. package/src/util/platform.ts +21 -11
  337. package/src/util/process-liveness.ts +26 -0
  338. package/src/workspace/heartbeat-service.ts +19 -0
  339. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  340. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  341. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
  342. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  343. package/src/workspace/migrations/registry.ts +8 -0
  344. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  345. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  346. package/src/memory/v2/skill-qdrant.ts +0 -404
  347. package/src/signals/bash.ts +0 -198
@@ -17,11 +17,20 @@ mock.module("../config/loader.js", () => ({
17
17
  const sentMessages: unknown[] = [];
18
18
  const sentMessageOptions: unknown[] = [];
19
19
  let mockHasClient = false;
20
- let mockCapableClients: Array<{ clientId: string; capabilities: string[] }> = [];
21
- let mockClientRegistry: Map<string, { clientId: string; capabilities: string[] }> = new Map();
20
+ type MockClient = {
21
+ clientId: string;
22
+ capabilities: string[];
23
+ actorPrincipalId?: string;
24
+ };
25
+ let mockCapableClients: Array<MockClient> = [];
26
+ let mockClientRegistry: Map<string, MockClient> = new Map();
22
27
 
23
28
  mock.module("../runtime/assistant-event-hub.js", () => ({
24
- broadcastMessage: (msg: unknown, _conversationId?: string, options?: unknown) => {
29
+ broadcastMessage: (
30
+ msg: unknown,
31
+ _conversationId?: string,
32
+ options?: unknown,
33
+ ) => {
25
34
  sentMessages.push(msg);
26
35
  sentMessageOptions.push(options);
27
36
  },
@@ -30,6 +39,8 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
30
39
  cap === "host_bash" && mockHasClient ? { id: "mock-client" } : null,
31
40
  listClientsByCapability: (_cap: string) => mockCapableClients,
32
41
  getClientById: (clientId: string) => mockClientRegistry.get(clientId),
42
+ getActorPrincipalIdForClient: (clientId: string) =>
43
+ mockClientRegistry.get(clientId)?.actorPrincipalId,
33
44
  },
34
45
  }));
35
46
 
@@ -50,8 +61,15 @@ describe("HostBashProxy", () => {
50
61
  proxy = new (HostBashProxy as any)();
51
62
  }
52
63
 
53
- function setupSingleClient(clientId = "client-1") {
54
- const entry = { clientId, capabilities: ["host_bash"] };
64
+ function setupSingleClient(
65
+ clientId: string = "client-1",
66
+ actorPrincipalId: string = "user-A",
67
+ ) {
68
+ const entry: MockClient = {
69
+ clientId,
70
+ capabilities: ["host_bash"],
71
+ actorPrincipalId,
72
+ };
55
73
  mockCapableClients = [entry];
56
74
  mockClientRegistry.set(clientId, entry);
57
75
  }
@@ -60,6 +78,7 @@ describe("HostBashProxy", () => {
60
78
  mockCapableClients = clientIds.map((id) => ({
61
79
  clientId: id,
62
80
  capabilities: ["host_bash"],
81
+ actorPrincipalId: "user-A",
63
82
  }));
64
83
  for (const entry of mockCapableClients) {
65
84
  mockClientRegistry.set(entry.clientId, entry);
@@ -551,11 +570,13 @@ describe("HostBashProxy", () => {
551
570
  describe("target client routing", () => {
552
571
  test("auto-resolves when exactly one capable client is connected", async () => {
553
572
  setup();
554
- setupSingleClient("client-abc");
573
+ setupSingleClient("client-abc", "user-A");
555
574
 
556
575
  const resultPromise = proxy.request(
557
576
  { command: "echo hello" },
558
577
  "session-1",
578
+ undefined,
579
+ "user-A",
559
580
  );
560
581
 
561
582
  expect(sentMessages).toHaveLength(1);
@@ -580,15 +601,21 @@ describe("HostBashProxy", () => {
580
601
 
581
602
  test("uses explicit targetClientId when it is valid", async () => {
582
603
  setup();
583
- setupSingleClient("client-abc");
604
+ setupSingleClient("client-abc", "user-A");
584
605
  // Also register a second client so we're sure explicit targeting works
585
- const entry2 = { clientId: "client-xyz", capabilities: ["host_bash"] };
606
+ const entry2: MockClient = {
607
+ clientId: "client-xyz",
608
+ capabilities: ["host_bash"],
609
+ actorPrincipalId: "user-A",
610
+ };
586
611
  mockCapableClients.push(entry2);
587
612
  mockClientRegistry.set("client-xyz", entry2);
588
613
 
589
614
  const resultPromise = proxy.request(
590
615
  { command: "echo hello", targetClientId: "client-abc" },
591
616
  "session-1",
617
+ undefined,
618
+ "user-A",
592
619
  );
593
620
 
594
621
  expect(sentMessages).toHaveLength(1);
@@ -612,17 +639,21 @@ describe("HostBashProxy", () => {
612
639
 
613
640
  test("returns error for explicit targetClientId that is not connected", async () => {
614
641
  setup();
615
- setupSingleClient("client-abc");
642
+ setupSingleClient("client-abc", "user-A");
616
643
 
617
644
  const result = await proxy.request(
618
645
  { command: "echo hello", targetClientId: "client-unknown" },
619
646
  "session-1",
647
+ undefined,
648
+ "user-A",
620
649
  );
621
650
 
622
651
  // Should return error without broadcasting
623
652
  expect(result.isError).toBe(true);
624
653
  expect(result.content).toContain("client-unknown");
625
- expect(result.content).toContain("assistant clients list --capability host_bash");
654
+ expect(result.content).toContain(
655
+ "assistant clients list --capability host_bash",
656
+ );
626
657
  expect(sentMessages).toHaveLength(0);
627
658
  });
628
659
 
@@ -632,11 +663,14 @@ describe("HostBashProxy", () => {
632
663
  mockClientRegistry.set("client-no-bash", {
633
664
  clientId: "client-no-bash",
634
665
  capabilities: [],
666
+ actorPrincipalId: "user-A",
635
667
  });
636
668
 
637
669
  const result = await proxy.request(
638
670
  { command: "echo hello", targetClientId: "client-no-bash" },
639
671
  "session-1",
672
+ undefined,
673
+ "user-A",
640
674
  );
641
675
 
642
676
  expect(result.isError).toBe(true);
@@ -645,36 +679,23 @@ describe("HostBashProxy", () => {
645
679
  expect(sentMessages).toHaveLength(0);
646
680
  });
647
681
 
648
- test("falls through to untargeted broadcast when multiple capable clients are connected and no targetClientId", async () => {
682
+ test("rejects ambiguously when multiple same-user capable clients are connected and no targetClientId", async () => {
683
+ // Regression: previously fell through to untargeted broadcast, fanning
684
+ // a single targeted-style request out across every same-user machine.
649
685
  setup();
650
686
  setupMultipleClients(["client-1", "client-2", "client-3"]);
651
687
 
652
- const resultPromise = proxy.request(
688
+ const result = await proxy.request(
653
689
  { command: "echo hello" },
654
690
  "session-1",
691
+ undefined,
692
+ "user-A",
655
693
  );
656
694
 
657
- // Should broadcast without an early error return
658
- expect(sentMessages).toHaveLength(1);
659
- const sent = sentMessages[0] as Record<string, unknown>;
660
- expect(sent.type).toBe("host_bash_request");
661
- // No target client resolved — untargeted broadcast
662
- expect(sent.targetClientId).toBeUndefined();
663
-
664
- const opts = sentMessageOptions[0] as Record<string, unknown> | undefined;
665
- expect(opts?.targetClientId).toBeUndefined();
666
-
667
- // Manually resolve to clean up
668
- const requestId = sent.requestId as string;
669
- proxy.resolveResult(requestId, {
670
- stdout: "hello\n",
671
- stderr: "",
672
- exitCode: 0,
673
- timedOut: false,
674
- });
675
-
676
- const result = await resultPromise;
677
- expect(result.isError).toBe(false);
695
+ expect(result.isError).toBe(true);
696
+ expect(result.content).toContain("target_client_id");
697
+ // No broadcast happened
698
+ expect(sentMessages).toHaveLength(0);
678
699
  });
679
700
 
680
701
  test("falls through to broadcast when zero capable clients (existing timeout path)", async () => {
@@ -707,13 +728,15 @@ describe("HostBashProxy", () => {
707
728
 
708
729
  test("includes targetClientId in timeout error message when client was resolved", async () => {
709
730
  setup();
710
- setupSingleClient("client-mac");
731
+ setupSingleClient("client-mac", "user-A");
711
732
 
712
733
  jest.useFakeTimers();
713
734
  try {
714
735
  const resultPromise = proxy.request(
715
736
  { command: "echo slow", timeout_seconds: 30 },
716
737
  "session-1",
738
+ undefined,
739
+ "user-A",
717
740
  );
718
741
 
719
742
  // Proxy timeout = 33s; advance past it
@@ -727,4 +750,174 @@ describe("HostBashProxy", () => {
727
750
  }
728
751
  });
729
752
  });
753
+
754
+ describe("same-user binding (sourceActorPrincipalId)", () => {
755
+ const SAME_USER_REJECTION =
756
+ "Submitting actor does not match the target client's actor for this request. The targeted client's authenticated user must submit the result.";
757
+
758
+ test("same-user targeted request succeeds", async () => {
759
+ setup();
760
+ setupSingleClient("client-abc", "user-A");
761
+
762
+ const resultPromise = proxy.request(
763
+ { command: "echo hello", targetClientId: "client-abc" },
764
+ "session-1",
765
+ undefined,
766
+ "user-A",
767
+ );
768
+
769
+ expect(sentMessages).toHaveLength(1);
770
+ const sent = sentMessages[0] as Record<string, unknown>;
771
+ expect(sent.type).toBe("host_bash_request");
772
+ expect(sent.targetClientId).toBe("client-abc");
773
+ const requestId = sent.requestId as string;
774
+ expect(pendingInteractions.get(requestId)).toBeDefined();
775
+
776
+ proxy.resolveResult(requestId, {
777
+ stdout: "hello\n",
778
+ stderr: "",
779
+ exitCode: 0,
780
+ timedOut: false,
781
+ });
782
+ await resultPromise;
783
+ });
784
+
785
+ test("cross-user targeted request rejected", async () => {
786
+ setup();
787
+ setupSingleClient("client-abc", "user-A");
788
+
789
+ const result = await proxy.request(
790
+ { command: "echo hello", targetClientId: "client-abc" },
791
+ "session-1",
792
+ undefined,
793
+ "user-B",
794
+ );
795
+
796
+ expect(result.isError).toBe(true);
797
+ expect(result.content).toBe(SAME_USER_REJECTION);
798
+ // No broadcast and no pending registration
799
+ expect(sentMessages).toHaveLength(0);
800
+ });
801
+
802
+ test("target client missing actorPrincipalId rejected", async () => {
803
+ setup();
804
+ // Register a client without an actorPrincipalId (legacy/service-token).
805
+ const entry: MockClient = {
806
+ clientId: "client-abc",
807
+ capabilities: ["host_bash"],
808
+ };
809
+ mockCapableClients = [entry];
810
+ mockClientRegistry.set("client-abc", entry);
811
+
812
+ const result = await proxy.request(
813
+ { command: "echo hello", targetClientId: "client-abc" },
814
+ "session-1",
815
+ undefined,
816
+ "user-A",
817
+ );
818
+
819
+ expect(result.isError).toBe(true);
820
+ expect(result.content).toBe(SAME_USER_REJECTION);
821
+ expect(sentMessages).toHaveLength(0);
822
+ });
823
+
824
+ test("source missing actorPrincipalId rejected when targeting", async () => {
825
+ setup();
826
+ setupSingleClient("client-abc", "user-A");
827
+
828
+ const result = await proxy.request(
829
+ { command: "echo hello", targetClientId: "client-abc" },
830
+ "session-1",
831
+ undefined,
832
+ undefined,
833
+ );
834
+
835
+ expect(result.isError).toBe(true);
836
+ expect(result.content).toBe(SAME_USER_REJECTION);
837
+ expect(sentMessages).toHaveLength(0);
838
+ });
839
+
840
+ test("untargeted local flow unchanged when no auto-resolve match", async () => {
841
+ setup();
842
+ // No capable clients connected — untargeted path runs.
843
+
844
+ const resultPromise = proxy.request(
845
+ { command: "echo hello" },
846
+ "session-1",
847
+ undefined,
848
+ "user-A",
849
+ );
850
+
851
+ expect(sentMessages).toHaveLength(1);
852
+ const sent = sentMessages[0] as Record<string, unknown>;
853
+ expect(sent.type).toBe("host_bash_request");
854
+ expect(sent.targetClientId).toBeUndefined();
855
+
856
+ const requestId = sent.requestId as string;
857
+ proxy.resolveResult(requestId, {
858
+ stdout: "hello\n",
859
+ stderr: "",
860
+ exitCode: 0,
861
+ timedOut: false,
862
+ });
863
+ await resultPromise;
864
+ });
865
+
866
+ test("auto-resolve to same-user client succeeds", async () => {
867
+ setup();
868
+ setupSingleClient("client-abc", "user-A");
869
+
870
+ const resultPromise = proxy.request(
871
+ { command: "echo hello" },
872
+ "session-1",
873
+ undefined,
874
+ "user-A",
875
+ );
876
+
877
+ expect(sentMessages).toHaveLength(1);
878
+ const sent = sentMessages[0] as Record<string, unknown>;
879
+ expect(sent.targetClientId).toBe("client-abc");
880
+
881
+ const requestId = sent.requestId as string;
882
+ proxy.resolveResult(requestId, {
883
+ stdout: "hello\n",
884
+ stderr: "",
885
+ exitCode: 0,
886
+ timedOut: false,
887
+ });
888
+ await resultPromise;
889
+ });
890
+
891
+ test("auto-resolve to different-user client falls through to untargeted", async () => {
892
+ setup();
893
+ // Single capable client owned by user-B; caller is user-A.
894
+ setupSingleClient("client-abc", "user-B");
895
+
896
+ const resultPromise = proxy.request(
897
+ { command: "echo hello" },
898
+ "session-1",
899
+ undefined,
900
+ "user-A",
901
+ );
902
+
903
+ // Auto-resolve must NOT pick the cross-user client; the untargeted
904
+ // broadcast path runs instead.
905
+ expect(sentMessages).toHaveLength(1);
906
+ const sent = sentMessages[0] as Record<string, unknown>;
907
+ expect(sent.type).toBe("host_bash_request");
908
+ expect(sent.targetClientId).toBeUndefined();
909
+
910
+ const opts = sentMessageOptions[0] as Record<string, unknown> | undefined;
911
+ expect(opts?.targetClientId).toBeUndefined();
912
+
913
+ const requestId = sent.requestId as string;
914
+ proxy.resolveResult(requestId, {
915
+ stdout: "hello\n",
916
+ stderr: "",
917
+ exitCode: 0,
918
+ timedOut: false,
919
+ });
920
+ await resultPromise;
921
+ });
922
+ });
730
923
  });
@@ -35,7 +35,12 @@ mock.module("../runtime/pending-interactions.js", () => ({
35
35
 
36
36
  interface ResolveCall {
37
37
  requestId: string;
38
- result: { stdout: string; stderr: string; exitCode: number | null; timedOut: boolean };
38
+ result: {
39
+ stdout: string;
40
+ stderr: string;
41
+ exitCode: number | null;
42
+ timedOut: boolean;
43
+ };
39
44
  }
40
45
 
41
46
  const resolveSpy: ResolveCall[] = [];
@@ -46,7 +51,12 @@ mock.module("../daemon/host-bash-proxy.js", () => ({
46
51
  return {
47
52
  resolveResult(
48
53
  requestId: string,
49
- result: { stdout: string; stderr: string; exitCode: number | null; timedOut: boolean },
54
+ result: {
55
+ stdout: string;
56
+ stderr: string;
57
+ exitCode: number | null;
58
+ timedOut: boolean;
59
+ },
50
60
  ) {
51
61
  // resolveResult() internally calls pendingInteractions.resolve() in the real
52
62
  // implementation; simulate that here so resolvedIds assertions still hold.
@@ -58,6 +68,16 @@ mock.module("../daemon/host-bash-proxy.js", () => ({
58
68
  },
59
69
  }));
60
70
 
71
+ // Stored actor-principal-id keyed by clientId, populated by tests.
72
+ const clientActorPrincipals = new Map<string, string>();
73
+
74
+ mock.module("../runtime/assistant-event-hub.js", () => ({
75
+ assistantEventHub: {
76
+ getActorPrincipalIdForClient: (clientId: string) =>
77
+ clientActorPrincipals.get(clientId),
78
+ },
79
+ }));
80
+
61
81
  // ── Real imports (after mocks) ───────────────────────────────────────
62
82
 
63
83
  import {
@@ -82,10 +102,19 @@ function registerPending(
82
102
  requestId: string,
83
103
  overrides: Partial<PendingInteraction> = {},
84
104
  ): void {
105
+ // Mirror the production proxy behavior: capture the target's actor
106
+ // principal at registration time so the result-route check compares
107
+ // against the persisted value rather than a live hub lookup.
108
+ const targetActorPrincipalId =
109
+ overrides.targetActorPrincipalId ??
110
+ (overrides.targetClientId
111
+ ? clientActorPrincipals.get(overrides.targetClientId)
112
+ : undefined);
85
113
  pendingStore.set(requestId, {
86
114
  conversationId: "conv-1",
87
115
  kind: "host_bash",
88
116
  ...overrides,
117
+ targetActorPrincipalId,
89
118
  });
90
119
  }
91
120
 
@@ -106,6 +135,7 @@ describe("handleHostBashResult", () => {
106
135
  pendingStore.clear();
107
136
  resolvedIds.length = 0;
108
137
  resolveSpy.length = 0;
138
+ clientActorPrincipals.clear();
109
139
  });
110
140
 
111
141
  // ── Happy paths ────────────────────────────────────────────────────
@@ -142,11 +172,15 @@ describe("handleHostBashResult", () => {
142
172
  describe("targeted request (targetClientId set)", () => {
143
173
  test("accepts when x-vellum-client-id matches targetClientId", async () => {
144
174
  const requestId = "req-targeted-match";
175
+ clientActorPrincipals.set("client-abc", "principal-1");
145
176
  registerPending(requestId, { targetClientId: "client-abc" });
146
177
 
147
178
  const result = await handleHostBashResult({
148
179
  body: bashBody(requestId),
149
- headers: { "x-vellum-client-id": "client-abc" },
180
+ headers: {
181
+ "x-vellum-client-id": "client-abc",
182
+ "x-vellum-actor-principal-id": "principal-1",
183
+ },
150
184
  });
151
185
 
152
186
  expect(result).toEqual({ accepted: true });
@@ -157,14 +191,145 @@ describe("handleHostBashResult", () => {
157
191
 
158
192
  test("trims whitespace from x-vellum-client-id before comparing", async () => {
159
193
  const requestId = "req-targeted-trim";
194
+ clientActorPrincipals.set("client-abc", "principal-1");
195
+ registerPending(requestId, { targetClientId: "client-abc" });
196
+
197
+ const result = await handleHostBashResult({
198
+ body: bashBody(requestId),
199
+ headers: {
200
+ "x-vellum-client-id": " client-abc ",
201
+ "x-vellum-actor-principal-id": "principal-1",
202
+ },
203
+ });
204
+
205
+ expect(result).toEqual({ accepted: true });
206
+ });
207
+ });
208
+
209
+ // ── Same-user actor binding (defense-in-depth) ─────────────────────
210
+
211
+ describe("targeted request — actor principal binding", () => {
212
+ test("accepts when submitting actor matches target client's actor", async () => {
213
+ const requestId = "req-actor-match";
214
+ clientActorPrincipals.set("client-abc", "principal-shared");
215
+ registerPending(requestId, { targetClientId: "client-abc" });
216
+
217
+ const result = await handleHostBashResult({
218
+ body: bashBody(requestId),
219
+ headers: {
220
+ "x-vellum-client-id": "client-abc",
221
+ "x-vellum-actor-principal-id": "principal-shared",
222
+ },
223
+ });
224
+
225
+ expect(result).toEqual({ accepted: true });
226
+ expect(resolveSpy).toHaveLength(1);
227
+ expect(resolvedIds).toContain(requestId);
228
+ });
229
+
230
+ test("throws ForbiddenError (403) when submitting actor does not match target client's actor", () => {
231
+ const requestId = "req-actor-mismatch";
232
+ clientActorPrincipals.set("client-abc", "principal-victim");
233
+ registerPending(requestId, { targetClientId: "client-abc" });
234
+
235
+ expect(() =>
236
+ handleHostBashResult({
237
+ body: bashBody(requestId),
238
+ headers: {
239
+ "x-vellum-client-id": "client-abc",
240
+ "x-vellum-actor-principal-id": "principal-attacker",
241
+ },
242
+ }),
243
+ ).toThrow(ForbiddenError);
244
+ });
245
+
246
+ test("interaction is NOT resolved on cross-actor 403 (still pending)", () => {
247
+ const requestId = "req-actor-mismatch-stays";
248
+ clientActorPrincipals.set("client-abc", "principal-victim");
249
+ registerPending(requestId, { targetClientId: "client-abc" });
250
+
251
+ try {
252
+ handleHostBashResult({
253
+ body: bashBody(requestId),
254
+ headers: {
255
+ "x-vellum-client-id": "client-abc",
256
+ "x-vellum-actor-principal-id": "principal-attacker",
257
+ },
258
+ });
259
+ } catch {
260
+ // expected
261
+ }
262
+
263
+ expect(resolvedIds).not.toContain(requestId);
264
+ expect(pendingStore.has(requestId)).toBe(true);
265
+ });
266
+
267
+ test("throws ForbiddenError (403) when x-vellum-actor-principal-id header is missing entirely", () => {
268
+ const requestId = "req-actor-missing";
269
+ clientActorPrincipals.set("client-abc", "principal-victim");
270
+ registerPending(requestId, { targetClientId: "client-abc" });
271
+
272
+ expect(() =>
273
+ handleHostBashResult({
274
+ body: bashBody(requestId),
275
+ headers: { "x-vellum-client-id": "client-abc" },
276
+ }),
277
+ ).toThrow(ForbiddenError);
278
+ });
279
+
280
+ test("interaction is NOT resolved when submitting actor is missing (still pending)", () => {
281
+ const requestId = "req-actor-missing-stays";
282
+ clientActorPrincipals.set("client-abc", "principal-victim");
160
283
  registerPending(requestId, { targetClientId: "client-abc" });
161
284
 
285
+ try {
286
+ handleHostBashResult({
287
+ body: bashBody(requestId),
288
+ headers: { "x-vellum-client-id": "client-abc" },
289
+ });
290
+ } catch {
291
+ // expected
292
+ }
293
+
294
+ expect(resolvedIds).not.toContain(requestId);
295
+ expect(pendingStore.has(requestId)).toBe(true);
296
+ });
297
+
298
+ test("throws ForbiddenError (403) when target client has no stored actor principal", () => {
299
+ // Target client connected without a verified principal (e.g. legacy
300
+ // service token) — refuse the submission rather than silently allow
301
+ // any actor through.
302
+ const requestId = "req-actor-target-missing";
303
+ registerPending(requestId, { targetClientId: "client-abc" });
304
+ // Note: no entry in clientActorPrincipals for "client-abc".
305
+
306
+ expect(() =>
307
+ handleHostBashResult({
308
+ body: bashBody(requestId),
309
+ headers: {
310
+ "x-vellum-client-id": "client-abc",
311
+ "x-vellum-actor-principal-id": "principal-1",
312
+ },
313
+ }),
314
+ ).toThrow(ForbiddenError);
315
+ });
316
+ });
317
+
318
+ // ── Untargeted-request behavior (regression for new check) ─────────
319
+
320
+ describe("untargeted request — actor principal check is skipped", () => {
321
+ test("accepts even when submitting actor is absent and no target client is set", async () => {
322
+ const requestId = "req-untargeted-no-actor";
323
+ registerPending(requestId);
324
+
162
325
  const result = await handleHostBashResult({
163
326
  body: bashBody(requestId),
164
- headers: { "x-vellum-client-id": " client-abc " },
327
+ headers: {},
165
328
  });
166
329
 
167
330
  expect(result).toEqual({ accepted: true });
331
+ expect(resolveSpy).toHaveLength(1);
332
+ expect(resolvedIds).toContain(requestId);
168
333
  });
169
334
  });
170
335
 
@@ -175,9 +340,9 @@ describe("handleHostBashResult", () => {
175
340
  const requestId = "req-targeted-no-header";
176
341
  registerPending(requestId, { targetClientId: "client-abc" });
177
342
 
178
- expect(() =>
179
- handleHostBashResult({ body: bashBody(requestId) }),
180
- ).toThrow(BadRequestError);
343
+ expect(() => handleHostBashResult({ body: bashBody(requestId) })).toThrow(
344
+ BadRequestError,
345
+ );
181
346
  });
182
347
 
183
348
  test("throws BadRequestError (400) when header is empty string", () => {
@@ -267,9 +432,9 @@ describe("handleHostBashResult", () => {
267
432
  });
268
433
 
269
434
  test("throws BadRequestError when requestId is missing", () => {
270
- expect(() =>
271
- handleHostBashResult({ body: { stdout: "x" } }),
272
- ).toThrow(BadRequestError);
435
+ expect(() => handleHostBashResult({ body: { stdout: "x" } })).toThrow(
436
+ BadRequestError,
437
+ );
273
438
  });
274
439
 
275
440
  test("throws NotFoundError for unknown requestId", () => {
@@ -287,8 +452,8 @@ describe("handleHostBashResult", () => {
287
452
  kind: "confirmation",
288
453
  });
289
454
 
290
- expect(() =>
291
- handleHostBashResult({ body: bashBody(requestId) }),
292
- ).toThrow(ConflictError);
455
+ expect(() => handleHostBashResult({ body: bashBody(requestId) })).toThrow(
456
+ ConflictError,
457
+ );
293
458
  });
294
459
  });