@vellumai/assistant 0.7.1 → 0.7.2

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 (535) hide show
  1. package/ARCHITECTURE.md +32 -49
  2. package/Dockerfile +1 -0
  3. package/README.md +1 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
  5. package/bun.lock +26 -26
  6. package/docs/architecture/security.md +20 -0
  7. package/docs/plugins.md +7 -9
  8. package/knip.json +1 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
  11. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  12. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  13. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  15. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  16. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  17. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
  19. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  20. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  21. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  22. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  23. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  24. package/openapi.yaml +565 -12
  25. package/package.json +6 -3
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  27. package/src/__tests__/app-bundler.test.ts +170 -1
  28. package/src/__tests__/app-control-flow.test.ts +374 -0
  29. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  30. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  31. package/src/__tests__/app-executors.test.ts +30 -43
  32. package/src/__tests__/approval-routes-http.test.ts +23 -6
  33. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  34. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  35. package/src/__tests__/assistant-event-hub.test.ts +109 -2
  36. package/src/__tests__/assistant-event.test.ts +10 -0
  37. package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
  38. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
  39. package/src/__tests__/background-shell-host-bash.test.ts +14 -15
  40. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  41. package/src/__tests__/btw-routes.test.ts +13 -4
  42. package/src/__tests__/call-controller.test.ts +49 -1
  43. package/src/__tests__/call-domain.test.ts +0 -2
  44. package/src/__tests__/call-routes-http.test.ts +0 -2
  45. package/src/__tests__/channel-readiness-service.test.ts +59 -1
  46. package/src/__tests__/checker.test.ts +3 -4
  47. package/src/__tests__/config-loader-backfill.test.ts +90 -155
  48. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  49. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  50. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  51. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  54. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  55. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  56. package/src/__tests__/conversation-lifecycle.test.ts +36 -0
  57. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  58. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
  59. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
  60. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  61. package/src/__tests__/conversation-slash-commands.test.ts +0 -4
  62. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
  64. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  65. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  66. package/src/__tests__/credentials-cli.test.ts +5 -12
  67. package/src/__tests__/cu-unified-flow.test.ts +185 -23
  68. package/src/__tests__/daemon-credential-client.test.ts +101 -19
  69. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  70. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  71. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  72. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  73. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  74. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
  75. package/src/__tests__/heartbeat-service.test.ts +718 -1
  76. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  77. package/src/__tests__/host-app-control-proxy.test.ts +602 -0
  78. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  79. package/src/__tests__/host-bash-proxy.test.ts +246 -47
  80. package/src/__tests__/host-bash-routes.test.ts +294 -0
  81. package/src/__tests__/host-browser-proxy.test.ts +24 -22
  82. package/src/__tests__/host-browser-routes.test.ts +39 -13
  83. package/src/__tests__/host-cu-proxy.test.ts +41 -52
  84. package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
  85. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  86. package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
  87. package/src/__tests__/host-file-proxy.test.ts +37 -43
  88. package/src/__tests__/host-file-read-tool.test.ts +17 -0
  89. package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
  90. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  91. package/src/__tests__/host-proxy-base.test.ts +312 -0
  92. package/src/__tests__/host-shell-tool.test.ts +22 -4
  93. package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
  94. package/src/__tests__/host-transfer-proxy.test.ts +121 -22
  95. package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
  96. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  97. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  98. package/src/__tests__/identity-routes.test.ts +103 -1
  99. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  100. package/src/__tests__/inline-command-runner.test.ts +0 -1
  101. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
  102. package/src/__tests__/integration-status.test.ts +85 -5
  103. package/src/__tests__/intent-routing.test.ts +0 -1
  104. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  105. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  106. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  107. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  108. package/src/__tests__/mcp-cli.test.ts +338 -2
  109. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  110. package/src/__tests__/migration-import-commit-http.test.ts +108 -2
  111. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  112. package/src/__tests__/oauth-cli.test.ts +0 -2
  113. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  114. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  115. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
  116. package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
  117. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  118. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  119. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  120. package/src/__tests__/require-fresh-approval.test.ts +0 -1
  121. package/src/__tests__/retry-backoff.test.ts +87 -0
  122. package/src/__tests__/runtime-events-sse.test.ts +10 -6
  123. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  124. package/src/__tests__/schedule-retry.test.ts +715 -0
  125. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  126. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  127. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  128. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  129. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  130. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  131. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  132. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  133. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  134. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  135. package/src/__tests__/slack-channel-config.test.ts +9 -14
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  137. package/src/__tests__/system-prompt.test.ts +0 -1
  138. package/src/__tests__/telegram-config.test.ts +0 -1
  139. package/src/__tests__/test-preload.ts +8 -0
  140. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  141. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  142. package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
  143. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  145. package/src/__tests__/tool-executor.test.ts +0 -1
  146. package/src/__tests__/twilio-config.test.ts +3 -16
  147. package/src/__tests__/twilio-routes.test.ts +3 -5
  148. package/src/__tests__/twilio-validation.test.ts +93 -0
  149. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
  150. package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
  151. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  152. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  153. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  154. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  155. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  156. package/src/backup/__tests__/paths.test.ts +0 -22
  157. package/src/backup/__tests__/restore.test.ts +51 -151
  158. package/src/backup/paths.ts +2 -18
  159. package/src/backup/restore.ts +107 -231
  160. package/src/bundler/app-bundler.ts +51 -3
  161. package/src/calls/relay-server.ts +4 -44
  162. package/src/calls/twilio-config.ts +2 -17
  163. package/src/calls/twilio-rest.ts +33 -105
  164. package/src/calls/twilio-routes.ts +11 -12
  165. package/src/channels/types.ts +8 -7
  166. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  167. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  168. package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
  169. package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
  170. package/src/cli/commands/backup.ts +6 -331
  171. package/src/cli/commands/clients.ts +36 -37
  172. package/src/cli/commands/contacts.ts +73 -0
  173. package/src/cli/commands/conversations.ts +2 -5
  174. package/src/cli/commands/credentials.ts +15 -7
  175. package/src/cli/commands/domain.ts +66 -15
  176. package/src/cli/commands/gateway.ts +183 -0
  177. package/src/cli/commands/keys.ts +9 -6
  178. package/src/cli/commands/mcp.ts +116 -156
  179. package/src/cli/commands/memory-v2.ts +296 -1
  180. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  181. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
  182. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
  183. package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
  184. package/src/cli/commands/platform/disconnect.ts +5 -4
  185. package/src/cli/commands/platform/index.ts +0 -18
  186. package/src/cli/lib/daemon-credential-client.ts +110 -28
  187. package/src/cli/program.ts +2 -0
  188. package/src/config/assistant-feature-flags.ts +67 -10
  189. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  190. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  191. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  192. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  193. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  194. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  195. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  196. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  197. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  198. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  199. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  200. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  201. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  202. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  203. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  204. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  205. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  206. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  207. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  208. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  209. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  210. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  211. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  212. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  213. package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
  214. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  215. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
  216. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  217. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  218. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  219. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  220. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  221. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  222. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  223. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  224. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  225. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  226. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  227. package/src/config/bundled-tool-registry.ts +21 -0
  228. package/src/config/env-registry.ts +0 -2
  229. package/src/config/env.ts +19 -12
  230. package/src/config/feature-flag-registry.json +21 -133
  231. package/src/config/loader.ts +73 -99
  232. package/src/config/sanitize-for-transfer.ts +2 -0
  233. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  234. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
  235. package/src/config/schemas/calls.ts +0 -9
  236. package/src/config/schemas/heartbeat.ts +63 -0
  237. package/src/config/schemas/ingress.ts +10 -6
  238. package/src/config/schemas/llm.ts +5 -10
  239. package/src/config/schemas/memory-lifecycle.ts +77 -24
  240. package/src/config/schemas/memory-v2.ts +48 -4
  241. package/src/config/schemas/platform.ts +6 -0
  242. package/src/config/schemas/services.ts +1 -15
  243. package/src/config/schemas/skills.ts +0 -6
  244. package/src/config/seed-inference-profiles.ts +1 -1
  245. package/src/contacts/contact-store.ts +0 -30
  246. package/src/contacts/contacts-write.ts +0 -27
  247. package/src/context/window-manager.ts +1 -2
  248. package/src/credential-execution/feature-gates.ts +10 -10
  249. package/src/credential-execution/process-manager.ts +12 -41
  250. package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
  251. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  252. package/src/daemon/config-watcher.ts +4 -3
  253. package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
  254. package/src/daemon/conversation-agent-loop.ts +32 -28
  255. package/src/daemon/conversation-lifecycle.ts +8 -1
  256. package/src/daemon/conversation-process.ts +16 -11
  257. package/src/daemon/conversation-runtime-assembly.ts +2 -2
  258. package/src/daemon/conversation-surfaces.ts +125 -4
  259. package/src/daemon/conversation-tool-setup.ts +16 -55
  260. package/src/daemon/conversation.ts +21 -2
  261. package/src/daemon/doordash-steps.ts +1 -1
  262. package/src/daemon/handlers/shared.ts +4 -1
  263. package/src/daemon/host-app-control-proxy.ts +293 -0
  264. package/src/daemon/host-bash-proxy.ts +84 -74
  265. package/src/daemon/host-browser-proxy.ts +67 -82
  266. package/src/daemon/host-cu-proxy.ts +81 -86
  267. package/src/daemon/host-file-proxy.ts +93 -69
  268. package/src/daemon/host-proxy-base.ts +294 -0
  269. package/src/daemon/host-proxy-preactivation.ts +82 -0
  270. package/src/daemon/host-transfer-proxy.ts +247 -129
  271. package/src/daemon/lifecycle.ts +115 -117
  272. package/src/daemon/message-protocol.ts +3 -8
  273. package/src/daemon/message-types/contacts.ts +23 -1
  274. package/src/daemon/message-types/conversations.ts +11 -8
  275. package/src/daemon/message-types/host-app-control.ts +150 -0
  276. package/src/daemon/message-types/host-bash.ts +4 -0
  277. package/src/daemon/message-types/host-cu.ts +2 -0
  278. package/src/daemon/message-types/host-file.ts +4 -0
  279. package/src/daemon/message-types/host-transfer.ts +3 -0
  280. package/src/daemon/message-types/schedules.ts +8 -3
  281. package/src/daemon/message-types/skills.ts +2 -2
  282. package/src/daemon/process-message.ts +18 -1
  283. package/src/daemon/shutdown-handlers.ts +0 -3
  284. package/src/daemon/tool-setup-types.ts +51 -0
  285. package/src/daemon/tool-side-effects.ts +1 -1
  286. package/src/events/tool-audit-listener.ts +2 -1
  287. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
  288. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
  289. package/src/heartbeat/heartbeat-run-store.ts +236 -0
  290. package/src/heartbeat/heartbeat-service.ts +280 -49
  291. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  292. package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
  293. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  294. package/src/home/post-connect-feed.ts +68 -0
  295. package/src/home/relationship-state-writer.ts +17 -92
  296. package/src/home/suggested-prompts.ts +46 -10
  297. package/src/inbound/public-ingress-urls.ts +32 -34
  298. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  299. package/src/ipc/assistant-server.ts +14 -1
  300. package/src/ipc/cli-client.ts +32 -1
  301. package/src/live-voice/live-voice-metrics.ts +10 -10
  302. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  303. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  304. package/src/mcp/mcp-auth-state.ts +133 -0
  305. package/src/mcp/mcp-oauth-provider.ts +19 -0
  306. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  307. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  308. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  309. package/src/memory/anisotropy.test.ts +247 -0
  310. package/src/memory/anisotropy.ts +443 -0
  311. package/src/memory/auto-analysis-constants.ts +17 -0
  312. package/src/memory/auto-analysis-guard.ts +5 -15
  313. package/src/memory/canonical-guardian-store.ts +7 -7
  314. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  315. package/src/memory/context-search/agent-protocol.ts +6 -6
  316. package/src/memory/context-search/agent-runner.ts +32 -7
  317. package/src/memory/context-search/sources/memory-v2.ts +17 -5
  318. package/src/memory/conversation-crud.ts +1 -1
  319. package/src/memory/conversation-key-store.ts +2 -15
  320. package/src/memory/db-init.ts +4 -0
  321. package/src/memory/embedding-backend.ts +9 -21
  322. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
  323. package/src/memory/graph/conversation-graph-memory.ts +1 -24
  324. package/src/memory/graph/graph-search.ts +8 -0
  325. package/src/memory/graph/retriever.ts +28 -0
  326. package/src/memory/graph/tools.ts +1 -1
  327. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  328. package/src/memory/jobs/embed-concept-page.ts +28 -2
  329. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  330. package/src/memory/jobs-store.ts +66 -22
  331. package/src/memory/jobs-worker.ts +112 -63
  332. package/src/memory/memory-v2-activation-log-store.ts +1 -1
  333. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  334. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  335. package/src/memory/migrations/index.ts +5 -0
  336. package/src/memory/migrations/registry.ts +8 -0
  337. package/src/memory/pkb/pkb-search.ts +7 -0
  338. package/src/memory/qdrant-client.ts +50 -20
  339. package/src/memory/schema/infrastructure.ts +15 -0
  340. package/src/memory/search/semantic.ts +7 -0
  341. package/src/memory/sparse-tokenize.ts +49 -0
  342. package/src/memory/v2/__tests__/activation.test.ts +77 -95
  343. package/src/memory/v2/__tests__/injection.test.ts +43 -21
  344. package/src/memory/v2/__tests__/sim.test.ts +166 -6
  345. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  346. package/src/memory/v2/__tests__/static-context.test.ts +0 -1
  347. package/src/memory/v2/activation.ts +69 -88
  348. package/src/memory/v2/consolidation-job.ts +3 -5
  349. package/src/memory/v2/constants.ts +7 -0
  350. package/src/memory/v2/injection.ts +86 -53
  351. package/src/memory/v2/prompts/consolidation.ts +312 -91
  352. package/src/memory/v2/qdrant.ts +99 -1
  353. package/src/memory/v2/sim.ts +126 -16
  354. package/src/memory/v2/skill-qdrant.ts +12 -3
  355. package/src/memory/v2/skill-store.ts +16 -1
  356. package/src/memory/v2/sparse-bm25.ts +245 -0
  357. package/src/memory/v2/static-context.ts +6 -5
  358. package/src/messaging/providers/gmail/types.ts +0 -49
  359. package/src/messaging/providers/slack/adapter.ts +1 -31
  360. package/src/messaging/providers/slack/types.ts +0 -32
  361. package/src/notifications/README.md +10 -10
  362. package/src/notifications/broadcaster.ts +1 -1
  363. package/src/notifications/guardian-question-mode.ts +5 -5
  364. package/src/oauth/connect-orchestrator.ts +4 -0
  365. package/src/oauth/credential-token-resolver.ts +1 -3
  366. package/src/oauth/manual-token-connection.ts +0 -4
  367. package/src/outbound-proxy/index.ts +1 -37
  368. package/src/outbound-proxy/logging.ts +1 -1
  369. package/src/outbound-proxy/policy.ts +6 -5
  370. package/src/outbound-proxy/router.ts +2 -1
  371. package/src/permissions/approval-policy.test.ts +6 -275
  372. package/src/permissions/approval-policy.ts +0 -51
  373. package/src/permissions/checker.test.ts +0 -1
  374. package/src/permissions/checker.ts +3 -17
  375. package/src/permissions/gateway-threshold-reader.ts +2 -0
  376. package/src/permissions/prompter.ts +34 -1
  377. package/src/permissions/secret-prompter.ts +6 -2
  378. package/src/prompts/bootstrap-cleanup.ts +27 -0
  379. package/src/prompts/system-prompt.ts +3 -18
  380. package/src/prompts/templates/SOUL.md +13 -1
  381. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  382. package/src/runtime/assistant-event-hub.ts +118 -96
  383. package/src/runtime/assistant-event.ts +1 -0
  384. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  385. package/src/runtime/auth/middleware.ts +0 -96
  386. package/src/runtime/auth/route-policy.ts +19 -0
  387. package/src/runtime/btw-sidechain.ts +2 -3
  388. package/src/runtime/channel-invite-transport.ts +2 -48
  389. package/src/runtime/channel-invite-transports/email.ts +1 -1
  390. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  391. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  392. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  393. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  394. package/src/runtime/channel-invite-types.ts +54 -0
  395. package/src/runtime/channel-readiness-service.ts +32 -13
  396. package/src/runtime/http-server.ts +3 -329
  397. package/src/runtime/http-types.ts +0 -5
  398. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  399. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  400. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  401. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
  402. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  403. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  404. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  405. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  406. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  407. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
  408. package/src/runtime/migrations/migration-transport.ts +7 -7
  409. package/src/runtime/migrations/vbundle-builder.ts +327 -60
  410. package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
  411. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  412. package/src/runtime/migrations/vbundle-importer.ts +245 -68
  413. package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
  414. package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
  415. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  416. package/src/runtime/migrations/vbundle-validator.ts +114 -0
  417. package/src/runtime/pending-interactions.ts +35 -9
  418. package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
  419. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  420. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  421. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  422. package/src/runtime/routes/approval-interception-types.ts +13 -0
  423. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  424. package/src/runtime/routes/backup-routes.ts +15 -38
  425. package/src/runtime/routes/btw-routes.ts +14 -37
  426. package/src/runtime/routes/client-routes.ts +1 -0
  427. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  428. package/src/runtime/routes/conversation-query-routes.ts +36 -1
  429. package/src/runtime/routes/conversation-routes.ts +30 -13
  430. package/src/runtime/routes/document-pdf-renderer.ts +165 -0
  431. package/src/runtime/routes/documents-routes.ts +30 -0
  432. package/src/runtime/routes/errors.ts +19 -4
  433. package/src/runtime/routes/events-routes.ts +12 -6
  434. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  435. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  436. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  437. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  438. package/src/runtime/routes/host-bash-routes.ts +36 -6
  439. package/src/runtime/routes/host-browser-routes.ts +108 -13
  440. package/src/runtime/routes/host-cu-routes.ts +44 -14
  441. package/src/runtime/routes/host-file-routes.ts +33 -10
  442. package/src/runtime/routes/host-transfer-routes.ts +64 -24
  443. package/src/runtime/routes/http-adapter.ts +1 -0
  444. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  445. package/src/runtime/routes/identity-routes.ts +15 -43
  446. package/src/runtime/routes/inbound-message-handler.ts +1 -9
  447. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
  448. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  449. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  450. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  451. package/src/runtime/routes/index.ts +8 -0
  452. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  453. package/src/runtime/routes/memory-item-routes.ts +10 -12
  454. package/src/runtime/routes/memory-v2-routes.ts +441 -1
  455. package/src/runtime/routes/migration-routes.ts +96 -0
  456. package/src/runtime/routes/schedule-routes.ts +7 -0
  457. package/src/runtime/verification-templates.ts +4 -7
  458. package/src/schedule/integration-status.ts +66 -2
  459. package/src/schedule/recurrence-engine.ts +4 -1
  460. package/src/schedule/retry-backoff.ts +18 -0
  461. package/src/schedule/retry-policy.ts +82 -0
  462. package/src/schedule/schedule-recovery.ts +64 -0
  463. package/src/schedule/schedule-store.ts +106 -2
  464. package/src/schedule/scheduler-types.ts +25 -0
  465. package/src/schedule/scheduler.ts +63 -38
  466. package/src/security/oauth-callback-registry.ts +8 -0
  467. package/src/sequence/analytics.ts +5 -5
  468. package/src/sequence/engine.ts +1 -1
  469. package/src/skills/catalog-files.ts +2 -8
  470. package/src/skills/include-graph.ts +5 -5
  471. package/src/skills/remote-skill-policy.ts +5 -5
  472. package/src/skills/skill-file-provider.ts +1 -1
  473. package/src/skills/skill-file-types.ts +13 -0
  474. package/src/skills/skillssh-audit-types.ts +28 -0
  475. package/src/skills/skillssh-registry.ts +8 -21
  476. package/src/telemetry/types.ts +2 -0
  477. package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
  478. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  479. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  480. package/src/tools/apps/executors.ts +56 -69
  481. package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
  482. package/src/tools/browser/browser-execution.ts +2 -2
  483. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
  484. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  485. package/src/tools/browser/cdp-client/factory.ts +23 -24
  486. package/src/tools/browser/cdp-client/index.ts +1 -14
  487. package/src/tools/computer-use/definitions.ts +42 -20
  488. package/src/tools/executor.ts +2 -0
  489. package/src/tools/host-filesystem/edit.ts +26 -0
  490. package/src/tools/host-filesystem/read.ts +26 -0
  491. package/src/tools/host-filesystem/transfer.ts +31 -1
  492. package/src/tools/host-filesystem/write.ts +26 -0
  493. package/src/tools/host-terminal/host-shell.ts +58 -0
  494. package/src/tools/schedule/create.ts +6 -0
  495. package/src/tools/schedule/list.ts +2 -0
  496. package/src/tools/schedule/update.ts +10 -0
  497. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  498. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  499. package/src/tools/skills/load.ts +0 -32
  500. package/src/tools/tool-approval-handler.ts +1 -5
  501. package/src/tools/types.ts +4 -0
  502. package/src/usage/pricing.ts +1 -1
  503. package/src/workspace/hatched-date.ts +86 -0
  504. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  505. package/src/workspace/migrations/006-services-config.ts +8 -5
  506. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  507. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  508. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  509. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  510. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  511. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  512. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
  513. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  514. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
  515. package/src/workspace/migrations/AGENTS.md +1 -1
  516. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  517. package/src/workspace/migrations/utils.ts +21 -0
  518. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
  519. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
  520. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
  521. package/src/__tests__/twilio-rest.test.ts +0 -34
  522. package/src/backup/__tests__/backup-key.test.ts +0 -152
  523. package/src/backup/__tests__/backup-worker.test.ts +0 -782
  524. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  525. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  526. package/src/backup/backup-key.ts +0 -137
  527. package/src/backup/backup-worker.ts +0 -472
  528. package/src/backup/offsite-writer.ts +0 -222
  529. package/src/backup/stream-crypt.ts +0 -263
  530. package/src/daemon/message-types/pairing.ts +0 -58
  531. package/src/outbound-proxy/config.ts +0 -20
  532. package/src/outbound-proxy/health.ts +0 -18
  533. package/src/outbound-proxy/types.ts +0 -150
  534. package/src/runtime/capability-tokens.ts +0 -190
  535. package/src/signals/mcp-reload.ts +0 -18
@@ -11,7 +11,8 @@ import {
11
11
  publishCdpEvent,
12
12
  } from "../../browser-session/events.js";
13
13
  import { HostBrowserProxy } from "../../daemon/host-browser-proxy.js";
14
- import { BadRequestError, NotFoundError } from "./errors.js";
14
+ import * as pendingInteractions from "../pending-interactions.js";
15
+ import { BadRequestError, ConflictError, NotFoundError } from "./errors.js";
15
16
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
16
17
 
17
18
  /**
@@ -29,21 +30,26 @@ export type HostBrowserResultResolution =
29
30
  | { ok: true }
30
31
  | {
31
32
  ok: false;
32
- code: "BAD_REQUEST" | "NOT_FOUND";
33
- status: 400 | 404;
33
+ code: "BAD_REQUEST" | "NOT_FOUND" | "CONFLICT";
34
+ status: 400 | 404 | 409;
34
35
  message: string;
35
36
  };
36
37
 
37
38
  /**
38
- * Shared resolver used by both the HTTP route handler and the WS
39
- * `host_browser_result` frame handler. Looks up the pending interaction
40
- * by requestId, validates its kind, and forwards the response to the
41
- * owning conversation.
39
+ * Resolver for the `POST /v1/host-browser-result` HTTP route. Looks up
40
+ * the pending interaction by requestId, validates its kind is
41
+ * `host_browser`, and forwards the response to the owning conversation.
42
+ *
43
+ * NOTE: The WebSocket `host_browser_result` frame path does NOT go
44
+ * through this function — it is handled by `HostBrowserProxy.resolveResult`
45
+ * directly, which only consults `pendingInteractions` and does not
46
+ * currently perform a kind check. That asymmetry is pre-existing; if
47
+ * the WS path is ever opened to less-trusted clients, it should adopt
48
+ * the same kind-check guard added here.
42
49
  *
43
50
  * This function does NOT perform auth — callers are expected to have
44
51
  * already authenticated the caller (the HTTP route uses
45
- * `requireBoundGuardian`, the WS path relies on the JWT check performed
46
- * at WebSocket upgrade time).
52
+ * `requireBoundGuardian`).
47
53
  */
48
54
  export function resolveHostBrowserResultByRequestId(frame: {
49
55
  requestId?: unknown;
@@ -61,20 +67,30 @@ export function resolveHostBrowserResultByRequestId(frame: {
61
67
  };
62
68
  }
63
69
 
64
- const proxy = HostBrowserProxy.instance;
65
- if (!proxy.hasPendingRequest(requestId)) {
70
+ const peeked = pendingInteractions.get(requestId);
71
+ if (!peeked) {
66
72
  return {
67
73
  ok: false,
68
74
  code: "NOT_FOUND",
69
75
  status: 404,
70
- message: "No pending interaction found for this requestId",
76
+ message: "No pending browser request for this requestId",
77
+ };
78
+ }
79
+
80
+ if (peeked.kind !== "host_browser") {
81
+ return {
82
+ ok: false,
83
+ code: "CONFLICT",
84
+ status: 409,
85
+ message: `Pending interaction is of kind "${peeked.kind}", expected "host_browser"`,
71
86
  };
72
87
  }
73
88
 
74
89
  const normalizedContent = typeof content === "string" ? content : "";
75
90
  const normalizedIsError = typeof isError === "boolean" ? isError : false;
76
91
 
77
- proxy.resolve(requestId, {
92
+ const proxy = HostBrowserProxy.instance;
93
+ proxy.resolveResult(requestId, {
78
94
  content: normalizedContent,
79
95
  isError: normalizedIsError,
80
96
  });
@@ -181,6 +197,42 @@ function handleHostBrowserResult({ body }: RouteHandlerArgs) {
181
197
  if (!resolution.ok) {
182
198
  if (resolution.code === "NOT_FOUND")
183
199
  throw new NotFoundError(resolution.message);
200
+ if (resolution.code === "CONFLICT")
201
+ throw new ConflictError(resolution.message);
202
+ throw new BadRequestError(resolution.message);
203
+ }
204
+
205
+ return { accepted: true };
206
+ }
207
+
208
+ // ---------------------------------------------------------------------------
209
+ // POST /v1/host-browser-event
210
+ // ---------------------------------------------------------------------------
211
+
212
+ function handleHostBrowserEvent({ body }: RouteHandlerArgs) {
213
+ if (!body || typeof body !== "object") {
214
+ throw new BadRequestError("Request body is required");
215
+ }
216
+
217
+ const resolution = resolveHostBrowserEvent(body);
218
+ if (!resolution.ok) {
219
+ throw new BadRequestError(resolution.message);
220
+ }
221
+
222
+ return { accepted: true };
223
+ }
224
+
225
+ // ---------------------------------------------------------------------------
226
+ // POST /v1/host-browser-session-invalidated
227
+ // ---------------------------------------------------------------------------
228
+
229
+ function handleHostBrowserSessionInvalidated({ body }: RouteHandlerArgs) {
230
+ if (!body || typeof body !== "object") {
231
+ throw new BadRequestError("Request body is required");
232
+ }
233
+
234
+ const resolution = resolveHostBrowserSessionInvalidated(body);
235
+ if (!resolution.ok) {
184
236
  throw new BadRequestError(resolution.message);
185
237
  }
186
238
 
@@ -210,4 +262,47 @@ export const ROUTES: RouteDefinition[] = [
210
262
  }),
211
263
  handler: handleHostBrowserResult,
212
264
  },
265
+ {
266
+ operationId: "host_browser_event",
267
+ endpoint: "host-browser-event",
268
+ method: "POST",
269
+ requireGuardian: true,
270
+ summary: "Forward a CDP event from the browser extension",
271
+ description:
272
+ "Publishes a chrome.debugger.onEvent firing into the runtime-side browser-session event bus.",
273
+ tags: ["host"],
274
+ requestBody: z.object({
275
+ method: z.string().describe("CDP event method name"),
276
+ params: z.unknown().optional().describe("CDP event parameters"),
277
+ cdpSessionId: z
278
+ .string()
279
+ .optional()
280
+ .describe("CDP session ID (if target-scoped)"),
281
+ }),
282
+ responseBody: z.object({
283
+ accepted: z.boolean(),
284
+ }),
285
+ handler: handleHostBrowserEvent,
286
+ },
287
+ {
288
+ operationId: "host_browser_session_invalidated",
289
+ endpoint: "host-browser-session-invalidated",
290
+ method: "POST",
291
+ requireGuardian: true,
292
+ summary: "Notify runtime that a CDP session was invalidated",
293
+ description:
294
+ "Marks the target as invalidated in the runtime-side browser session registry.",
295
+ tags: ["host"],
296
+ requestBody: z.object({
297
+ targetId: z
298
+ .string()
299
+ .optional()
300
+ .describe("CDP target that was detached"),
301
+ reason: z.string().optional().describe("Detach reason"),
302
+ }),
303
+ responseBody: z.object({
304
+ accepted: z.boolean(),
305
+ }),
306
+ handler: handleHostBrowserSessionInvalidated,
307
+ },
213
308
  ];
@@ -8,18 +8,14 @@ import { z } from "zod";
8
8
 
9
9
  import { findConversation } from "../../daemon/conversation-store.js";
10
10
  import * as pendingInteractions from "../pending-interactions.js";
11
- import {
12
- BadRequestError,
13
- ConflictError,
14
- NotFoundError,
15
- } from "./errors.js";
11
+ import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from "./errors.js";
16
12
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
17
13
 
18
14
  // ---------------------------------------------------------------------------
19
15
  // POST /v1/host-cu-result
20
16
  // ---------------------------------------------------------------------------
21
17
 
22
- function handleHostCuResult({ body }: RouteHandlerArgs) {
18
+ function handleHostCuResult({ body, headers }: RouteHandlerArgs) {
23
19
  if (!body || typeof body !== "object") {
24
20
  throw new BadRequestError("Request body is required");
25
21
  }
@@ -58,9 +54,7 @@ function handleHostCuResult({ body }: RouteHandlerArgs) {
58
54
 
59
55
  const peeked = pendingInteractions.get(requestId);
60
56
  if (!peeked) {
61
- throw new NotFoundError(
62
- "No pending interaction found for this requestId",
63
- );
57
+ throw new NotFoundError("No pending interaction found for this requestId");
64
58
  }
65
59
 
66
60
  if (peeked.kind !== "host_cu") {
@@ -69,13 +63,32 @@ function handleHostCuResult({ body }: RouteHandlerArgs) {
69
63
  );
70
64
  }
71
65
 
72
- const interaction = pendingInteractions.resolve(requestId)!;
73
- const conversation = findConversation(interaction.conversationId);
66
+ // Validate submitting client matches the targeted client (if any).
67
+ if (peeked.targetClientId != null) {
68
+ const rawClientId = (headers as Record<string, string | undefined>)?.["x-vellum-client-id"];
69
+ const submittingClientId = rawClientId?.trim() || undefined;
70
+ if (!submittingClientId) {
71
+ throw new BadRequestError("x-vellum-client-id header is missing for a targeted host CU request.");
72
+ }
73
+ if (submittingClientId !== peeked.targetClientId) {
74
+ throw new ForbiddenError(
75
+ `Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}"). The targeted client must submit the result.`,
76
+ );
77
+ }
78
+ }
79
+
80
+ const conversation = findConversation(peeked.conversationId);
74
81
  if (!conversation) {
82
+ pendingInteractions.resolve(requestId);
75
83
  throw new NotFoundError("Conversation not found for host CU result");
76
84
  }
77
85
 
78
- conversation.hostCuProxy?.resolve(requestId, {
86
+ if (!conversation.hostCuProxy) {
87
+ pendingInteractions.resolve(requestId);
88
+ throw new NotFoundError("No host CU proxy for conversation");
89
+ }
90
+
91
+ conversation.hostCuProxy.processObservation(requestId, {
79
92
  axTree,
80
93
  axDiff,
81
94
  screenshot,
@@ -103,8 +116,7 @@ export const ROUTES: RouteDefinition[] = [
103
116
  method: "POST",
104
117
  requireGuardian: true,
105
118
  summary: "Submit host CU result",
106
- description:
107
- "Resolve a pending host computer-use request by requestId.",
119
+ description: "Resolve a pending host computer-use request by requestId.",
108
120
  tags: ["host"],
109
121
  requestBody: z.object({
110
122
  requestId: z.string().describe("Pending CU request ID"),
@@ -123,6 +135,24 @@ export const ROUTES: RouteDefinition[] = [
123
135
  responseBody: z.object({
124
136
  accepted: z.boolean(),
125
137
  }),
138
+ additionalResponses: {
139
+ "400": {
140
+ description:
141
+ "x-vellum-client-id header is missing for a targeted host CU request.",
142
+ },
143
+ "403": {
144
+ description:
145
+ "Submitting client does not match the targeted client for this request.",
146
+ },
147
+ "404": {
148
+ description:
149
+ "No pending interaction found for the given requestId, or the conversation/proxy no longer exists.",
150
+ },
151
+ "409": {
152
+ description:
153
+ "Pending interaction exists but is of a different kind (e.g. host_bash, host_file).",
154
+ },
155
+ },
126
156
  handler: handleHostCuResult,
127
157
  },
128
158
  ];
@@ -8,18 +8,14 @@ import { z } from "zod";
8
8
 
9
9
  import { HostFileProxy } from "../../daemon/host-file-proxy.js";
10
10
  import * as pendingInteractions from "../pending-interactions.js";
11
- import {
12
- BadRequestError,
13
- ConflictError,
14
- NotFoundError,
15
- } from "./errors.js";
11
+ import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from "./errors.js";
16
12
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
17
13
 
18
14
  // ---------------------------------------------------------------------------
19
15
  // POST /v1/host-file-result
20
16
  // ---------------------------------------------------------------------------
21
17
 
22
- function handleHostFileResult({ body }: RouteHandlerArgs) {
18
+ function handleHostFileResult({ body, headers }: RouteHandlerArgs) {
23
19
  if (!body || typeof body !== "object") {
24
20
  throw new BadRequestError("Request body is required");
25
21
  }
@@ -37,9 +33,7 @@ function handleHostFileResult({ body }: RouteHandlerArgs) {
37
33
 
38
34
  const peeked = pendingInteractions.get(requestId);
39
35
  if (!peeked) {
40
- throw new NotFoundError(
41
- "No pending interaction found for this requestId",
42
- );
36
+ throw new NotFoundError("No pending interaction found for this requestId");
43
37
  }
44
38
 
45
39
  if (peeked.kind !== "host_file") {
@@ -48,7 +42,19 @@ function handleHostFileResult({ body }: RouteHandlerArgs) {
48
42
  );
49
43
  }
50
44
 
51
- pendingInteractions.resolve(requestId);
45
+ // Validate submitting client matches the targeted client (if any).
46
+ if (peeked.targetClientId != null) {
47
+ const rawClientId = (headers as Record<string, string | undefined>)?.["x-vellum-client-id"];
48
+ const submittingClientId = rawClientId?.trim() || undefined;
49
+ if (!submittingClientId) {
50
+ throw new BadRequestError("x-vellum-client-id header is missing for a targeted host file request.");
51
+ }
52
+ if (submittingClientId !== peeked.targetClientId) {
53
+ throw new ForbiddenError(
54
+ `Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}"). The targeted client must submit the result.`,
55
+ );
56
+ }
57
+ }
52
58
 
53
59
  HostFileProxy.instance.resolve(requestId, {
54
60
  content: content ?? "",
@@ -90,6 +96,23 @@ export const ROUTES: RouteDefinition[] = [
90
96
  responseBody: z.object({
91
97
  accepted: z.boolean(),
92
98
  }),
99
+ additionalResponses: {
100
+ "400": {
101
+ description:
102
+ "x-vellum-client-id header is missing for a targeted host file request.",
103
+ },
104
+ "403": {
105
+ description:
106
+ "Submitting client does not match the targeted client for this request.",
107
+ },
108
+ "404": {
109
+ description: "No pending interaction found for the given requestId.",
110
+ },
111
+ "409": {
112
+ description:
113
+ "Pending interaction exists but is of a different kind (e.g. host_bash, host_cu).",
114
+ },
115
+ },
93
116
  handler: handleHostFileResult,
94
117
  },
95
118
  ];
@@ -9,11 +9,7 @@ import { z } from "zod";
9
9
 
10
10
  import { HostTransferProxy } from "../../daemon/host-transfer-proxy.js";
11
11
  import * as pendingInteractions from "../pending-interactions.js";
12
- import {
13
- BadRequestError,
14
- ConflictError,
15
- NotFoundError,
16
- } from "./errors.js";
12
+ import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from "./errors.js";
17
13
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
18
14
 
19
15
  /**
@@ -34,6 +30,7 @@ function findProxyByTransferId(transferId: string) {
34
30
 
35
31
  function handleTransferContentGet({
36
32
  pathParams = {},
33
+ headers = {},
37
34
  }: RouteHandlerArgs): Uint8Array {
38
35
  const transferId = pathParams.transferId;
39
36
  if (!transferId) {
@@ -45,6 +42,13 @@ function handleTransferContentGet({
45
42
  throw new NotFoundError("Unknown or consumed transfer");
46
43
  }
47
44
 
45
+ const targetClientId = match.proxy.getTargetClientIdForTransfer(transferId);
46
+ if (targetClientId != null) {
47
+ const submittingClientId = (headers as Record<string, string>)["x-vellum-client-id"]?.trim() || undefined;
48
+ if (!submittingClientId) throw new BadRequestError("x-vellum-client-id header required for targeted transfer");
49
+ if (submittingClientId !== targetClientId) throw new ForbiddenError(`Client "${submittingClientId}" is not the owner of this transfer`);
50
+ }
51
+
48
52
  const content = match.proxy.getTransferContent(transferId);
49
53
  if (!content) {
50
54
  throw new NotFoundError("Unknown or consumed transfer");
@@ -55,8 +59,11 @@ function handleTransferContentGet({
55
59
 
56
60
  /**
57
61
  * Resolve Content-Length and X-Transfer-SHA256 response headers for the
58
- * GET transfer content endpoint. Called by the HTTP adapter before
59
- * sending the response.
62
+ * GET transfer content endpoint. Called by the HTTP adapter AFTER the handler
63
+ * runs (`http-adapter.ts:107-125`), so the entry has already been consumed by
64
+ * `getTransferContent`. We read the size/sha256 from
65
+ * `takeJustConsumedTransferMetadata`, which the proxy populates synchronously
66
+ * during the handler's `getTransferContent` call.
60
67
  */
61
68
  function resolveTransferContentGetHeaders({
62
69
  pathParams = {},
@@ -66,16 +73,14 @@ function resolveTransferContentGetHeaders({
66
73
  const transferId = pathParams?.transferId;
67
74
  if (!transferId) return { "Content-Type": "application/octet-stream" };
68
75
 
69
- const match = findProxyByTransferId(transferId);
70
- if (!match) return { "Content-Type": "application/octet-stream" };
71
-
72
- const content = match.proxy.getTransferContent(transferId);
73
- if (!content) return { "Content-Type": "application/octet-stream" };
76
+ const meta =
77
+ HostTransferProxy.instance.takeJustConsumedTransferMetadata(transferId);
78
+ if (!meta) return { "Content-Type": "application/octet-stream" };
74
79
 
75
80
  return {
76
81
  "Content-Type": "application/octet-stream",
77
- "Content-Length": content.sizeBytes.toString(),
78
- "X-Transfer-SHA256": content.sha256,
82
+ "Content-Length": meta.sizeBytes.toString(),
83
+ "X-Transfer-SHA256": meta.sha256,
79
84
  };
80
85
  }
81
86
 
@@ -98,6 +103,13 @@ async function handleTransferContentPut({
98
103
  throw new NotFoundError("Unknown or consumed transfer");
99
104
  }
100
105
 
106
+ const targetClientId = match.proxy.getTargetClientIdForTransfer(transferId);
107
+ if (targetClientId != null) {
108
+ const submittingClientId = (headers as Record<string, string>)["x-vellum-client-id"]?.trim() || undefined;
109
+ if (!submittingClientId) throw new BadRequestError("x-vellum-client-id header required for targeted transfer");
110
+ if (submittingClientId !== targetClientId) throw new ForbiddenError(`Client "${submittingClientId}" is not the owner of this transfer`);
111
+ }
112
+
101
113
  const data = rawBody ? Buffer.from(rawBody) : Buffer.alloc(0);
102
114
  const sha256 = headers["x-transfer-sha256"] ?? "";
103
115
 
@@ -107,11 +119,6 @@ async function handleTransferContentPut({
107
119
  sha256,
108
120
  );
109
121
 
110
- // For to_sandbox transfers there is no separate /v1/host-transfer-result
111
- // callback — the PUT handler is the terminal event. Always clean up the
112
- // pending interaction so it doesn't leak.
113
- pendingInteractions.resolve(match.requestId);
114
-
115
122
  if (!result.accepted) {
116
123
  throw new BadRequestError(result.error ?? "Transfer content rejected");
117
124
  }
@@ -123,7 +130,7 @@ async function handleTransferContentPut({
123
130
  // POST /v1/host-transfer-result
124
131
  // ---------------------------------------------------------------------------
125
132
 
126
- function handleTransferResult({ body }: RouteHandlerArgs) {
133
+ function handleTransferResult({ body, headers }: RouteHandlerArgs) {
127
134
  if (!body || typeof body !== "object") {
128
135
  throw new BadRequestError("Request body is required");
129
136
  }
@@ -141,9 +148,7 @@ function handleTransferResult({ body }: RouteHandlerArgs) {
141
148
 
142
149
  const peeked = pendingInteractions.get(requestId);
143
150
  if (!peeked) {
144
- throw new NotFoundError(
145
- "No pending interaction found for this requestId",
146
- );
151
+ throw new NotFoundError("No pending interaction found for this requestId");
147
152
  }
148
153
 
149
154
  if (peeked.kind !== "host_transfer") {
@@ -152,7 +157,12 @@ function handleTransferResult({ body }: RouteHandlerArgs) {
152
157
  );
153
158
  }
154
159
 
155
- pendingInteractions.resolve(requestId);
160
+ if (peeked.targetClientId != null) {
161
+ const rawClientId = (headers as Record<string, string | undefined>)?.["x-vellum-client-id"];
162
+ const submittingClientId = rawClientId?.trim() || undefined;
163
+ if (!submittingClientId) throw new BadRequestError("x-vellum-client-id header is missing for a targeted host transfer request.");
164
+ if (submittingClientId !== peeked.targetClientId) throw new ForbiddenError(`Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}").`);
165
+ }
156
166
 
157
167
  HostTransferProxy.instance.resolveTransferResult(requestId, {
158
168
  isError: isError ?? false,
@@ -179,6 +189,16 @@ export const ROUTES: RouteDefinition[] = [
179
189
  "Serve raw file bytes for a to_host transfer. Single-use: returns 404 after first consumption.",
180
190
  tags: ["host-transfer"],
181
191
  responseHeaders: resolveTransferContentGetHeaders,
192
+ additionalResponses: {
193
+ "400": {
194
+ description:
195
+ "x-vellum-client-id header is missing for a targeted transfer.",
196
+ },
197
+ "403": {
198
+ description:
199
+ "Submitting client does not match the targeted client for this transfer.",
200
+ },
201
+ },
182
202
  handler: handleTransferContentGet,
183
203
  },
184
204
  {
@@ -191,6 +211,16 @@ export const ROUTES: RouteDefinition[] = [
191
211
  description:
192
212
  "Receive raw file bytes for a to_sandbox transfer. Verifies SHA-256 integrity via the X-Transfer-SHA256 header.",
193
213
  tags: ["host-transfer"],
214
+ additionalResponses: {
215
+ "400": {
216
+ description:
217
+ "x-vellum-client-id header is missing for a targeted transfer.",
218
+ },
219
+ "403": {
220
+ description:
221
+ "Submitting client does not match the targeted client for this transfer.",
222
+ },
223
+ },
194
224
  handler: handleTransferContentPut,
195
225
  },
196
226
  {
@@ -211,6 +241,16 @@ export const ROUTES: RouteDefinition[] = [
211
241
  responseBody: z.object({
212
242
  accepted: z.boolean(),
213
243
  }),
244
+ additionalResponses: {
245
+ "400": {
246
+ description:
247
+ "x-vellum-client-id header is missing for a targeted host transfer request.",
248
+ },
249
+ "403": {
250
+ description:
251
+ "Submitting client does not match the targeted client for this transfer.",
252
+ },
253
+ },
214
254
  handler: handleTransferResult,
215
255
  },
216
256
  ];
@@ -163,6 +163,7 @@ export function routeDefinitionsToHTTPRoutes(
163
163
  err.code as HttpErrorCode,
164
164
  err.message,
165
165
  err.statusCode,
166
+ err.details,
166
167
  );
167
168
  }
168
169
  throw err;
@@ -48,6 +48,36 @@ function readWorkspaceFile(name: string): string {
48
48
  }
49
49
  }
50
50
 
51
+ function parseIdentityIntroSection(content: string): string | null {
52
+ let inSection = false;
53
+
54
+ for (const line of content.split("\n")) {
55
+ const trimmed = line.trim();
56
+ if (/^#+\s/.test(trimmed)) {
57
+ inSection = trimmed.toLowerCase().includes("identity intro");
58
+ continue;
59
+ }
60
+ if (inSection && trimmed.length > 0) {
61
+ return trimmed;
62
+ }
63
+ }
64
+
65
+ return null;
66
+ }
67
+
68
+ /**
69
+ * Read the explicit `## Identity Intro` section from workspace prompt files.
70
+ *
71
+ * BOOTSTRAP.md instructs the assistant to write this section in IDENTITY.md.
72
+ * SOUL.md remains a fallback for older workspaces that stored the intro there.
73
+ */
74
+ export function readWorkspaceIdentityIntro(): string | null {
75
+ return (
76
+ parseIdentityIntroSection(readWorkspaceFile("IDENTITY.md")) ??
77
+ parseIdentityIntroSection(readWorkspaceFile("SOUL.md"))
78
+ );
79
+ }
80
+
51
81
  /** Compute a SHA-256 hex hash of the concatenated identity file contents. */
52
82
  export function computeIdentityContentHash(): string {
53
83
  const staticFiles = IDENTITY_FILES.map(readWorkspaceFile).join("\n---\n");
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { spawnSync } from "node:child_process";
6
- import { existsSync, readFileSync, statfsSync, statSync } from "node:fs";
6
+ import { existsSync, readFileSync, statfsSync } from "node:fs";
7
7
  import { availableParallelism, cpus, totalmem } from "node:os";
8
8
 
9
9
  import { z } from "zod";
@@ -21,6 +21,7 @@ import {
21
21
  getWorkspacePromptPath,
22
22
  } from "../../util/platform.js";
23
23
  import { APP_VERSION } from "../../version.js";
24
+ import { resolveHatchedAtReadOnly } from "../../workspace/hatched-date.js";
24
25
  import { WORKSPACE_MIGRATIONS } from "../../workspace/migrations/registry.js";
25
26
  import { getLastWorkspaceMigrationId } from "../../workspace/migrations/runner.js";
26
27
  import { NotFoundError } from "./errors.js";
@@ -488,13 +489,7 @@ function getIdentity() {
488
489
 
489
490
  const version = APP_VERSION;
490
491
 
491
- let createdAt: string | undefined;
492
- try {
493
- const stats = statSync(identityPath);
494
- createdAt = stats.birthtime.toISOString();
495
- } catch {
496
- // ignore
497
- }
492
+ const createdAt = resolveIdentityCreatedAt(identityPath);
498
493
 
499
494
  return {
500
495
  name: fields.name ?? "",
@@ -507,10 +502,18 @@ function getIdentity() {
507
502
  };
508
503
  }
509
504
 
505
+ function resolveIdentityCreatedAt(identityPath: string): string | undefined {
506
+ return resolveHatchedAtReadOnly(identityPath);
507
+ }
508
+
510
509
  function getIdentityIntro() {
511
- const soulIntro = readSoulIdentityIntro();
512
- if (soulIntro) {
513
- return { text: soulIntro };
510
+ const identityPath = getWorkspacePromptPath("IDENTITY.md");
511
+ if (existsSync(identityPath)) {
512
+ const content = readFileSync(identityPath, "utf-8");
513
+ const fields = parseIdentityFields(content);
514
+ if (fields.name) {
515
+ return { text: `Hi, I'm ${fields.name}!` };
516
+ }
514
517
  }
515
518
 
516
519
  const cached = getCachedIntro();
@@ -520,37 +523,6 @@ function getIdentityIntro() {
520
523
  return { text: cached.text };
521
524
  }
522
525
 
523
- // ---------------------------------------------------------------------------
524
- // Identity intro cache
525
- // ---------------------------------------------------------------------------
526
-
527
- /**
528
- * Parse the `## Identity Intro` section from SOUL.md.
529
- * Returns the first non-empty line under that heading, or null.
530
- */
531
- function readSoulIdentityIntro(): string | null {
532
- try {
533
- const soulPath = getWorkspacePromptPath("SOUL.md");
534
- if (!existsSync(soulPath)) return null;
535
- const content = readFileSync(soulPath, "utf-8");
536
-
537
- let inSection = false;
538
- for (const line of content.split("\n")) {
539
- const trimmed = line.trim();
540
- if (/^#+\s/.test(trimmed)) {
541
- inSection = trimmed.toLowerCase().includes("identity intro");
542
- continue;
543
- }
544
- if (inSection && trimmed.length > 0) {
545
- return trimmed;
546
- }
547
- }
548
- } catch {
549
- // Fall through to cache/fallback
550
- }
551
- return null;
552
- }
553
-
554
526
  // ---------------------------------------------------------------------------
555
527
  // Zod schemas for profiler health metadata
556
528
  // ---------------------------------------------------------------------------
@@ -647,7 +619,7 @@ export const ROUTES: RouteDefinition[] = [
647
619
  handler: getIdentityIntro,
648
620
  summary: "Get identity intro text",
649
621
  description:
650
- "Returns the cached identity intro string, preferring SOUL.md over LLM-generated cache.",
622
+ "Returns a deterministic greeting derived from the assistant name in IDENTITY.md, falling back to LLM-generated cache.",
651
623
  tags: ["identity"],
652
624
  responseBody: z.object({
653
625
  text: z.string(),