@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
@@ -20,11 +20,24 @@ const sentMessages: unknown[] = [];
20
20
  const sentMessageOptions: unknown[] = [];
21
21
  const resolvedInteractionIds: string[] = [];
22
22
  let mockHasClient = false;
23
- let mockCapableClients: Array<{ clientId: string; capabilities: string[] }> = [];
24
- let mockClientRegistry: Map<string, { clientId: string; capabilities: string[] }> = new Map();
23
+ type MockClient = {
24
+ clientId: string;
25
+ capabilities: string[];
26
+ actorPrincipalId?: string;
27
+ };
28
+ let mockCapableClients: Array<MockClient> = [];
29
+ let mockClientRegistry: Map<string, MockClient> = new Map();
30
+
31
+ // Pre-existing Phase 1 routing tests use a single user identity. The same-user
32
+ // check added in the host_transfer fix is exercised separately below.
33
+ const TEST_PRINCIPAL = "test-user";
25
34
 
26
35
  mock.module("../runtime/assistant-event-hub.js", () => ({
27
- broadcastMessage: (msg: unknown, _conversationId?: string, options?: unknown) => {
36
+ broadcastMessage: (
37
+ msg: unknown,
38
+ _conversationId?: string,
39
+ options?: unknown,
40
+ ) => {
28
41
  sentMessages.push(msg);
29
42
  sentMessageOptions.push(options);
30
43
  },
@@ -33,6 +46,8 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
33
46
  cap === "host_file" && mockHasClient ? { id: "mock-client" } : null,
34
47
  listClientsByCapability: (_cap: string) => mockCapableClients,
35
48
  getClientById: (clientId: string) => mockClientRegistry.get(clientId),
49
+ getActorPrincipalIdForClient: (clientId: string) =>
50
+ mockClientRegistry.get(clientId)?.actorPrincipalId,
36
51
  },
37
52
  }));
38
53
 
@@ -95,7 +110,11 @@ describe("HostTransferProxy — targetClientId", () => {
95
110
  }
96
111
 
97
112
  function setupSingleClient(clientId = "client-1") {
98
- const entry = { clientId, capabilities: ["host_file"] };
113
+ const entry: MockClient = {
114
+ clientId,
115
+ capabilities: ["host_file"],
116
+ actorPrincipalId: TEST_PRINCIPAL,
117
+ };
99
118
  mockCapableClients = [entry];
100
119
  mockClientRegistry.set(clientId, entry);
101
120
  }
@@ -117,13 +136,17 @@ describe("HostTransferProxy — targetClientId", () => {
117
136
  const srcPath = `/tmp/htp-targeted-${Date.now()}.txt`;
118
137
  await globalThis.Bun.write(srcPath, fileContent);
119
138
 
120
- const resultPromise = proxy.requestToHost({
121
- sourcePath: srcPath,
122
- destPath: "/host/dest.txt",
123
- overwrite: false,
124
- conversationId: "conv-1",
125
- targetClientId: "client-mac",
126
- });
139
+ const resultPromise = proxy.requestToHost(
140
+ {
141
+ sourcePath: srcPath,
142
+ destPath: "/host/dest.txt",
143
+ overwrite: false,
144
+ conversationId: "conv-1",
145
+ targetClientId: "client-mac",
146
+ },
147
+ undefined,
148
+ TEST_PRINCIPAL,
149
+ );
127
150
 
128
151
  await waitForMessages(sentMessages, 1);
129
152
 
@@ -135,7 +158,10 @@ describe("HostTransferProxy — targetClientId", () => {
135
158
  expect(opts?.targetClientId).toBe("client-mac");
136
159
 
137
160
  const requestId = sent.requestId as string;
138
- proxy.resolveTransferResult(requestId, { isError: false, bytesWritten: 5 });
161
+ proxy.resolveTransferResult(requestId, {
162
+ isError: false,
163
+ bytesWritten: 5,
164
+ });
139
165
 
140
166
  const result = await resultPromise;
141
167
  expect(result.isError).toBe(false);
@@ -152,12 +178,16 @@ describe("HostTransferProxy — targetClientId", () => {
152
178
  const srcPath = `/tmp/htp-targeted-solo-${Date.now()}.txt`;
153
179
  await globalThis.Bun.write(srcPath, "content");
154
180
 
155
- const resultPromise = proxy.requestToHost({
156
- sourcePath: srcPath,
157
- destPath: "/host/dest.txt",
158
- overwrite: false,
159
- conversationId: "conv-2",
160
- });
181
+ const resultPromise = proxy.requestToHost(
182
+ {
183
+ sourcePath: srcPath,
184
+ destPath: "/host/dest.txt",
185
+ overwrite: false,
186
+ conversationId: "conv-2",
187
+ },
188
+ undefined,
189
+ TEST_PRINCIPAL,
190
+ );
161
191
 
162
192
  await waitForMessages(sentMessages, 1);
163
193
 
@@ -191,7 +221,9 @@ describe("HostTransferProxy — targetClientId", () => {
191
221
 
192
222
  expect(result.isError).toBe(true);
193
223
  expect(result.content).toContain("client-ghost");
194
- expect(result.content).toContain("assistant clients list --capability host_file");
224
+ expect(result.content).toContain(
225
+ "assistant clients list --capability host_file",
226
+ );
195
227
  expect(sentMessages).toHaveLength(0);
196
228
  });
197
229
  });
@@ -228,12 +260,16 @@ describe("HostTransferProxy — targetClientId", () => {
228
260
  setup();
229
261
  setupSingleClient("client-mac");
230
262
 
231
- const resultPromise = proxy.requestToSandbox({
232
- sourcePath: "/host/source.txt",
233
- destPath: "/sandbox/dest.txt",
234
- conversationId: "conv-5",
235
- targetClientId: "client-mac",
236
- });
263
+ const resultPromise = proxy.requestToSandbox(
264
+ {
265
+ sourcePath: "/host/source.txt",
266
+ destPath: "/sandbox/dest.txt",
267
+ conversationId: "conv-5",
268
+ targetClientId: "client-mac",
269
+ },
270
+ undefined,
271
+ TEST_PRINCIPAL,
272
+ );
237
273
 
238
274
  expect(sentMessages).toHaveLength(1);
239
275
  const sent = sentMessages[0] as Record<string, unknown>;
@@ -256,11 +292,15 @@ describe("HostTransferProxy — targetClientId", () => {
256
292
  setup();
257
293
  setupSingleClient("client-solo");
258
294
 
259
- const resultPromise = proxy.requestToSandbox({
260
- sourcePath: "/host/source.txt",
261
- destPath: "/sandbox/dest.txt",
262
- conversationId: "conv-6",
263
- });
295
+ const resultPromise = proxy.requestToSandbox(
296
+ {
297
+ sourcePath: "/host/source.txt",
298
+ destPath: "/sandbox/dest.txt",
299
+ conversationId: "conv-6",
300
+ },
301
+ undefined,
302
+ TEST_PRINCIPAL,
303
+ );
264
304
 
265
305
  expect(sentMessages).toHaveLength(1);
266
306
  const sent = sentMessages[0] as Record<string, unknown>;
@@ -333,6 +373,7 @@ describe("HostTransferProxy — targetClientId", () => {
333
373
  targetClientId: "client-abc",
334
374
  },
335
375
  controller.signal,
376
+ TEST_PRINCIPAL,
336
377
  );
337
378
 
338
379
  await waitForMessages(sentMessages, 1);
@@ -350,7 +391,9 @@ describe("HostTransferProxy — targetClientId", () => {
350
391
  expect(cancelMsg.type).toBe("host_transfer_cancel");
351
392
  expect(cancelMsg.targetClientId).toBe("client-abc");
352
393
 
353
- const cancelOpts = sentMessageOptions[1] as Record<string, unknown> | undefined;
394
+ const cancelOpts = sentMessageOptions[1] as
395
+ | Record<string, unknown>
396
+ | undefined;
354
397
  expect(cancelOpts?.targetClientId).toBe("client-abc");
355
398
  });
356
399
  });
@@ -362,12 +405,16 @@ describe("HostTransferProxy — targetClientId", () => {
362
405
  setup();
363
406
  setupSingleClient("client-xyz");
364
407
 
365
- const resultPromise = proxy.requestToSandbox({
366
- sourcePath: "/host/source.txt",
367
- destPath: "/sandbox/dest.txt",
368
- conversationId: "conv-10",
369
- targetClientId: "client-xyz",
370
- });
408
+ const resultPromise = proxy.requestToSandbox(
409
+ {
410
+ sourcePath: "/host/source.txt",
411
+ destPath: "/sandbox/dest.txt",
412
+ conversationId: "conv-10",
413
+ targetClientId: "client-xyz",
414
+ },
415
+ undefined,
416
+ TEST_PRINCIPAL,
417
+ );
371
418
 
372
419
  const sent = sentMessages[0] as Record<string, unknown>;
373
420
  const requestId = sent.requestId as string;
@@ -384,7 +431,9 @@ describe("HostTransferProxy — targetClientId", () => {
384
431
  expect(cancelMsg.type).toBe("host_transfer_cancel");
385
432
  expect(cancelMsg.targetClientId).toBe("client-xyz");
386
433
 
387
- const cancelOpts = sentMessageOptions[1] as Record<string, unknown> | undefined;
434
+ const cancelOpts = sentMessageOptions[1] as
435
+ | Record<string, unknown>
436
+ | undefined;
388
437
  expect(cancelOpts?.targetClientId).toBe("client-xyz");
389
438
  });
390
439
  });
@@ -396,12 +445,16 @@ describe("HostTransferProxy — targetClientId", () => {
396
445
  setup();
397
446
  setupSingleClient("client-dispose");
398
447
 
399
- const p = proxy.requestToSandbox({
400
- sourcePath: "/host/source.txt",
401
- destPath: "/sandbox/dest.txt",
402
- conversationId: "conv-11",
403
- targetClientId: "client-dispose",
404
- });
448
+ const p = proxy.requestToSandbox(
449
+ {
450
+ sourcePath: "/host/source.txt",
451
+ destPath: "/sandbox/dest.txt",
452
+ conversationId: "conv-11",
453
+ targetClientId: "client-dispose",
454
+ },
455
+ undefined,
456
+ TEST_PRINCIPAL,
457
+ );
405
458
  p.catch(() => {}); // expected rejection on dispose
406
459
 
407
460
  const sent = sentMessages[0] as Record<string, unknown>;
@@ -431,20 +484,26 @@ describe("HostTransferProxy — targetClientId", () => {
431
484
  const srcPath = `/tmp/htp-targeted-peek-${Date.now()}.txt`;
432
485
  await globalThis.Bun.write(srcPath, "content");
433
486
 
434
- const resultPromise = proxy.requestToHost({
435
- sourcePath: srcPath,
436
- destPath: "/host/dest.txt",
437
- overwrite: false,
438
- conversationId: "conv-12",
439
- targetClientId: "client-peek",
440
- });
487
+ const resultPromise = proxy.requestToHost(
488
+ {
489
+ sourcePath: srcPath,
490
+ destPath: "/host/dest.txt",
491
+ overwrite: false,
492
+ conversationId: "conv-12",
493
+ targetClientId: "client-peek",
494
+ },
495
+ undefined,
496
+ TEST_PRINCIPAL,
497
+ );
441
498
 
442
499
  await waitForMessages(sentMessages, 1);
443
500
 
444
501
  const sent = sentMessages[0] as Record<string, unknown>;
445
502
  const transferId = sent.transferId as string;
446
503
 
447
- expect(proxy.getTargetClientIdForTransfer(transferId)).toBe("client-peek");
504
+ expect(proxy.getTargetClientIdForTransfer(transferId)).toBe(
505
+ "client-peek",
506
+ );
448
507
 
449
508
  // Clean up
450
509
  const requestId = sent.requestId as string;
@@ -493,12 +552,16 @@ describe("HostTransferProxy — targetClientId", () => {
493
552
 
494
553
  jest.useFakeTimers();
495
554
  try {
496
- const resultPromise = proxy.requestToSandbox({
497
- sourcePath: "/host/source.txt",
498
- destPath: "/sandbox/dest.txt",
499
- conversationId: "conv-14",
500
- targetClientId: "client-timeout",
501
- });
555
+ const resultPromise = proxy.requestToSandbox(
556
+ {
557
+ sourcePath: "/host/source.txt",
558
+ destPath: "/sandbox/dest.txt",
559
+ conversationId: "conv-14",
560
+ targetClientId: "client-timeout",
561
+ },
562
+ undefined,
563
+ TEST_PRINCIPAL,
564
+ );
502
565
 
503
566
  const sent = sentMessages[0] as Record<string, unknown>;
504
567
  expect(sent.targetClientId).toBe("client-timeout");
@@ -546,7 +609,10 @@ describe("HostTransferProxy — targetClientId", () => {
546
609
  expect(opts?.targetClientId).toBeUndefined();
547
610
 
548
611
  const requestId = sent.requestId as string;
549
- proxy.resolveTransferResult(requestId, { isError: false, bytesWritten: 18 });
612
+ proxy.resolveTransferResult(requestId, {
613
+ isError: false,
614
+ bytesWritten: 18,
615
+ });
550
616
 
551
617
  const result = await resultPromise;
552
618
  expect(result.isError).toBe(false);
@@ -580,4 +646,287 @@ describe("HostTransferProxy — targetClientId", () => {
580
646
  expect(result.content).toBe("Transfer cancelled");
581
647
  });
582
648
  });
649
+
650
+ // ── Same-user binding (sourceActorPrincipalId) ───────────────────────
651
+
652
+ describe("same-user binding (sourceActorPrincipalId)", () => {
653
+ test("requestToHost: targeted request from same user reaches pendingInteractions", async () => {
654
+ setup();
655
+ mockClientRegistry.set("client-A", {
656
+ clientId: "client-A",
657
+ capabilities: ["host_file"],
658
+ actorPrincipalId: "user-A",
659
+ });
660
+ mockCapableClients = [mockClientRegistry.get("client-A")!];
661
+
662
+ const srcPath = `/tmp/htp-same-user-ok-${Date.now()}.txt`;
663
+ await globalThis.Bun.write(srcPath, "ok");
664
+
665
+ const resultPromise = proxy.requestToHost(
666
+ {
667
+ sourcePath: srcPath,
668
+ destPath: "/host/dest.txt",
669
+ overwrite: false,
670
+ conversationId: "conv-same-1",
671
+ targetClientId: "client-A",
672
+ },
673
+ undefined,
674
+ "user-A",
675
+ );
676
+
677
+ await waitForMessages(sentMessages, 1);
678
+ const sent = sentMessages[0] as Record<string, unknown>;
679
+ expect(sent.type).toBe("host_transfer_request");
680
+ expect(sent.targetClientId).toBe("client-A");
681
+
682
+ const requestId = sent.requestId as string;
683
+ proxy.resolveTransferResult(requestId, { isError: false });
684
+ await resultPromise;
685
+ });
686
+
687
+ test("requestToHost: targeted request from a different user is rejected", async () => {
688
+ setup();
689
+ mockClientRegistry.set("client-A", {
690
+ clientId: "client-A",
691
+ capabilities: ["host_file"],
692
+ actorPrincipalId: "user-A",
693
+ });
694
+ mockCapableClients = [mockClientRegistry.get("client-A")!];
695
+
696
+ const srcPath = `/tmp/htp-cross-user-${Date.now()}.txt`;
697
+ await globalThis.Bun.write(srcPath, "data");
698
+
699
+ const result = await proxy.requestToHost(
700
+ {
701
+ sourcePath: srcPath,
702
+ destPath: "/host/dest.txt",
703
+ overwrite: false,
704
+ conversationId: "conv-same-2",
705
+ targetClientId: "client-A",
706
+ },
707
+ undefined,
708
+ "user-B",
709
+ );
710
+
711
+ expect(result.isError).toBe(true);
712
+ expect(result.content).toContain("does not match");
713
+ expect(sentMessages).toHaveLength(0);
714
+ });
715
+
716
+ test("requestToHost: targeted request without source principal is rejected", async () => {
717
+ setup();
718
+ mockClientRegistry.set("client-A", {
719
+ clientId: "client-A",
720
+ capabilities: ["host_file"],
721
+ actorPrincipalId: "user-A",
722
+ });
723
+ mockCapableClients = [mockClientRegistry.get("client-A")!];
724
+
725
+ const srcPath = `/tmp/htp-no-principal-${Date.now()}.txt`;
726
+ await globalThis.Bun.write(srcPath, "data");
727
+
728
+ const result = await proxy.requestToHost(
729
+ {
730
+ sourcePath: srcPath,
731
+ destPath: "/host/dest.txt",
732
+ overwrite: false,
733
+ conversationId: "conv-same-3",
734
+ targetClientId: "client-A",
735
+ },
736
+ undefined,
737
+ undefined,
738
+ );
739
+
740
+ expect(result.isError).toBe(true);
741
+ expect(result.content).toContain("does not match");
742
+ expect(sentMessages).toHaveLength(0);
743
+ });
744
+
745
+ test("requestToHost: targeted request to a client with no actor principal is rejected", async () => {
746
+ setup();
747
+ mockClientRegistry.set("client-A", {
748
+ clientId: "client-A",
749
+ capabilities: ["host_file"],
750
+ // actorPrincipalId omitted (legacy/service-token client).
751
+ });
752
+ mockCapableClients = [mockClientRegistry.get("client-A")!];
753
+
754
+ const srcPath = `/tmp/htp-target-no-principal-${Date.now()}.txt`;
755
+ await globalThis.Bun.write(srcPath, "data");
756
+
757
+ const result = await proxy.requestToHost(
758
+ {
759
+ sourcePath: srcPath,
760
+ destPath: "/host/dest.txt",
761
+ overwrite: false,
762
+ conversationId: "conv-same-4",
763
+ targetClientId: "client-A",
764
+ },
765
+ undefined,
766
+ "user-A",
767
+ );
768
+
769
+ expect(result.isError).toBe(true);
770
+ expect(result.content).toContain("does not match");
771
+ expect(sentMessages).toHaveLength(0);
772
+ });
773
+
774
+ test("requestToHost: auto-resolve picks the same-user client when there's exactly one", async () => {
775
+ setup();
776
+ const a: MockClient = {
777
+ clientId: "client-A",
778
+ capabilities: ["host_file"],
779
+ actorPrincipalId: "user-A",
780
+ };
781
+ const b: MockClient = {
782
+ clientId: "client-B",
783
+ capabilities: ["host_file"],
784
+ actorPrincipalId: "user-B",
785
+ };
786
+ mockCapableClients = [a, b];
787
+ mockClientRegistry.set("client-A", a);
788
+ mockClientRegistry.set("client-B", b);
789
+
790
+ const srcPath = `/tmp/htp-auto-same-user-${Date.now()}.txt`;
791
+ await globalThis.Bun.write(srcPath, "data");
792
+
793
+ const resultPromise = proxy.requestToHost(
794
+ {
795
+ sourcePath: srcPath,
796
+ destPath: "/host/dest.txt",
797
+ overwrite: false,
798
+ conversationId: "conv-same-5",
799
+ },
800
+ undefined,
801
+ "user-A",
802
+ );
803
+
804
+ await waitForMessages(sentMessages, 1);
805
+ const sent = sentMessages[0] as Record<string, unknown>;
806
+ expect(sent.targetClientId).toBe("client-A");
807
+
808
+ const requestId = sent.requestId as string;
809
+ proxy.resolveTransferResult(requestId, { isError: false });
810
+ await resultPromise;
811
+ });
812
+
813
+ test("requestToHost: auto-resolve falls through when no client matches the source user", async () => {
814
+ setup();
815
+ mockClientRegistry.set("client-A", {
816
+ clientId: "client-A",
817
+ capabilities: ["host_file"],
818
+ actorPrincipalId: "user-A",
819
+ });
820
+ mockCapableClients = [mockClientRegistry.get("client-A")!];
821
+
822
+ const srcPath = `/tmp/htp-auto-no-match-${Date.now()}.txt`;
823
+ await globalThis.Bun.write(srcPath, "data");
824
+
825
+ const resultPromise = proxy.requestToHost(
826
+ {
827
+ sourcePath: srcPath,
828
+ destPath: "/host/dest.txt",
829
+ overwrite: false,
830
+ conversationId: "conv-same-6",
831
+ },
832
+ undefined,
833
+ "user-C",
834
+ );
835
+
836
+ await waitForMessages(sentMessages, 1);
837
+ const sent = sentMessages[0] as Record<string, unknown>;
838
+ expect(sent.targetClientId).toBeUndefined();
839
+
840
+ const requestId = sent.requestId as string;
841
+ proxy.resolveTransferResult(requestId, { isError: false });
842
+ await resultPromise;
843
+ });
844
+
845
+ test("requestToSandbox: targeted request from a different user is rejected", async () => {
846
+ setup();
847
+ mockClientRegistry.set("client-A", {
848
+ clientId: "client-A",
849
+ capabilities: ["host_file"],
850
+ actorPrincipalId: "user-A",
851
+ });
852
+ mockCapableClients = [mockClientRegistry.get("client-A")!];
853
+
854
+ const result = await proxy.requestToSandbox(
855
+ {
856
+ sourcePath: "/host/source.txt",
857
+ destPath: "/sandbox/dest.txt",
858
+ conversationId: "conv-same-7",
859
+ targetClientId: "client-A",
860
+ },
861
+ undefined,
862
+ "user-B",
863
+ );
864
+
865
+ expect(result.isError).toBe(true);
866
+ expect(result.content).toContain("does not match");
867
+ expect(sentMessages).toHaveLength(0);
868
+ });
869
+
870
+ test("requestToSandbox: targeted request from same user proceeds", async () => {
871
+ setup();
872
+ mockClientRegistry.set("client-A", {
873
+ clientId: "client-A",
874
+ capabilities: ["host_file"],
875
+ actorPrincipalId: "user-A",
876
+ });
877
+ mockCapableClients = [mockClientRegistry.get("client-A")!];
878
+
879
+ const resultPromise = proxy.requestToSandbox(
880
+ {
881
+ sourcePath: "/host/source.txt",
882
+ destPath: "/sandbox/dest.txt",
883
+ conversationId: "conv-same-8",
884
+ targetClientId: "client-A",
885
+ },
886
+ undefined,
887
+ "user-A",
888
+ );
889
+
890
+ expect(sentMessages).toHaveLength(1);
891
+ const sent = sentMessages[0] as Record<string, unknown>;
892
+ expect(sent.targetClientId).toBe("client-A");
893
+
894
+ proxy.cancel(sent.requestId as string);
895
+ await resultPromise;
896
+ });
897
+
898
+ test("requestToSandbox: auto-resolve picks the same-user client when there's exactly one", async () => {
899
+ setup();
900
+ const a: MockClient = {
901
+ clientId: "client-A",
902
+ capabilities: ["host_file"],
903
+ actorPrincipalId: "user-A",
904
+ };
905
+ const b: MockClient = {
906
+ clientId: "client-B",
907
+ capabilities: ["host_file"],
908
+ actorPrincipalId: "user-B",
909
+ };
910
+ mockCapableClients = [a, b];
911
+ mockClientRegistry.set("client-A", a);
912
+ mockClientRegistry.set("client-B", b);
913
+
914
+ const resultPromise = proxy.requestToSandbox(
915
+ {
916
+ sourcePath: "/host/source.txt",
917
+ destPath: "/sandbox/dest.txt",
918
+ conversationId: "conv-same-9",
919
+ },
920
+ undefined,
921
+ "user-A",
922
+ );
923
+
924
+ expect(sentMessages).toHaveLength(1);
925
+ const sent = sentMessages[0] as Record<string, unknown>;
926
+ expect(sent.targetClientId).toBe("client-A");
927
+
928
+ proxy.cancel(sent.requestId as string);
929
+ await resultPromise;
930
+ });
931
+ });
583
932
  });