@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
@@ -0,0 +1,183 @@
1
+ /**
2
+ * IPC route for the `contacts/prompt` CLI command.
3
+ *
4
+ * Flow:
5
+ * 1. CLI calls `contacts/prompt` IPC route with optional channel/role hints.
6
+ * 2. Daemon broadcasts a `contact_request` to all connected clients.
7
+ * 3. Client shows a contact address input form.
8
+ * 4. User enters an address; client POSTs to the gateway's
9
+ * `POST /v1/contacts/prompt` HTTP route.
10
+ * 5. Gateway upserts the contact + channel (gateway owns all contact writes).
11
+ * 6. Gateway calls daemon IPC `resolve_contact_prompt` with the new contact info.
12
+ * 7. Daemon resolves the pending promise; `contacts/prompt` IPC returns to CLI.
13
+ *
14
+ * The daemon only broadcasts the prompt and waits. It never writes contacts.
15
+ * All writes go through the gateway.
16
+ */
17
+
18
+ import { v4 as uuid } from "uuid";
19
+ import { z } from "zod";
20
+
21
+ import { getLogger } from "../../util/logger.js";
22
+ import { broadcastMessage } from "../assistant-event-hub.js";
23
+ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
24
+
25
+ const log = getLogger("contact-prompt-routes");
26
+
27
+ /** Timeout for waiting on the user to submit the contact form (5 min). */
28
+ const CONTACT_PROMPT_TIMEOUT_MS = 300_000;
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // Pending contact prompts
32
+ // ---------------------------------------------------------------------------
33
+
34
+ export interface ContactPromptResult {
35
+ ok: boolean;
36
+ error?: string;
37
+ contactId?: string;
38
+ channelId?: string;
39
+ channelType?: string;
40
+ address?: string;
41
+ }
42
+
43
+ interface PendingContactPrompt {
44
+ resolve: (result: ContactPromptResult) => void;
45
+ timer: ReturnType<typeof setTimeout>;
46
+ }
47
+
48
+ const pendingContactPrompts = new Map<string, PendingContactPrompt>();
49
+
50
+ /**
51
+ * Called by the gateway after it writes the contact and channel.
52
+ * Resolves the pending promise so the CLI's `contacts/prompt` IPC call returns.
53
+ */
54
+ function resolveContactPrompt({
55
+ body = {},
56
+ }: RouteHandlerArgs): { resolved: boolean } {
57
+ const { requestId, contactId, channelId, channelType, address, error } =
58
+ body as {
59
+ requestId: string;
60
+ contactId?: string;
61
+ channelId?: string;
62
+ channelType?: string;
63
+ address?: string;
64
+ error?: string;
65
+ };
66
+ const pending = pendingContactPrompts.get(requestId);
67
+ if (!pending) {
68
+ log.warn({ requestId }, "resolve_contact_prompt: no pending prompt found");
69
+ return { resolved: false };
70
+ }
71
+
72
+ clearTimeout(pending.timer);
73
+ pendingContactPrompts.delete(requestId);
74
+
75
+ if (error) {
76
+ pending.resolve({ ok: false, error });
77
+ } else {
78
+ pending.resolve({
79
+ ok: true,
80
+ contactId,
81
+ channelId,
82
+ channelType,
83
+ address,
84
+ });
85
+ }
86
+
87
+ log.info({ requestId, contactId }, "Contact prompt resolved");
88
+ return { resolved: true };
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Schema
93
+ // ---------------------------------------------------------------------------
94
+
95
+ const ContactPromptParams = z.object({
96
+ channel: z
97
+ .string()
98
+ .optional()
99
+ .describe(
100
+ "Suggested channel type hint (e.g. phone, email, telegram). Free text — not enforced.",
101
+ ),
102
+ placeholder: z
103
+ .string()
104
+ .optional()
105
+ .describe("Placeholder text for the address input field."),
106
+ label: z.string().optional().describe("Display label shown in the prompt UI."),
107
+ description: z.string().optional().describe("Longer description for the prompt UI."),
108
+ role: z
109
+ .enum(["guardian", "trusted-contact", "unknown"])
110
+ .default("unknown")
111
+ .describe("Intended role of the contact being registered."),
112
+ });
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // Handler
116
+ // ---------------------------------------------------------------------------
117
+
118
+ async function handleContactPrompt({
119
+ body = {},
120
+ }: RouteHandlerArgs): Promise<ContactPromptResult> {
121
+ const { channel, placeholder, label, description, role } =
122
+ ContactPromptParams.parse(body);
123
+
124
+ const requestId = uuid();
125
+
126
+ return new Promise((resolve) => {
127
+ const timer = setTimeout(() => {
128
+ pendingContactPrompts.delete(requestId);
129
+ log.warn({ requestId }, "Contact prompt timed out");
130
+ resolve({ ok: false, error: "Prompt timed out" });
131
+ }, CONTACT_PROMPT_TIMEOUT_MS);
132
+
133
+ pendingContactPrompts.set(requestId, { resolve, timer });
134
+
135
+ broadcastMessage({
136
+ type: "contact_request",
137
+ requestId,
138
+ channel,
139
+ placeholder,
140
+ label,
141
+ description,
142
+ role,
143
+ });
144
+
145
+ log.info({ requestId, channel, role }, "Contact prompt broadcast");
146
+ });
147
+ }
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Routes
151
+ // ---------------------------------------------------------------------------
152
+
153
+ export const CONTACT_PROMPT_ROUTES: RouteDefinition[] = [
154
+ {
155
+ operationId: "contacts_prompt",
156
+ endpoint: "contacts/prompt",
157
+ method: "POST",
158
+ handler: handleContactPrompt,
159
+ summary: "Prompt user to register a contact channel",
160
+ description:
161
+ "Broadcasts a contact_request to connected clients, waits for the user to submit an address via the gateway. The gateway owns the contact write and notifies the daemon via resolve_contact_prompt IPC.",
162
+ tags: ["contacts"],
163
+ requestBody: ContactPromptParams,
164
+ responseBody: z.object({
165
+ ok: z.boolean(),
166
+ error: z.string().optional(),
167
+ contactId: z.string().optional(),
168
+ channelId: z.string().optional(),
169
+ channelType: z.string().optional(),
170
+ address: z.string().optional(),
171
+ }),
172
+ },
173
+ {
174
+ operationId: "resolve_contact_prompt",
175
+ endpoint: "resolve_contact_prompt",
176
+ method: "POST",
177
+ handler: resolveContactPrompt,
178
+ summary: "Gateway callback: resolve a pending contact prompt",
179
+ description:
180
+ "Called by the gateway after it writes the contact and channel. Unblocks the waiting contacts/prompt IPC call.",
181
+ tags: ["contacts"],
182
+ },
183
+ ];
@@ -50,7 +50,11 @@ import {
50
50
  CONFIG_RELOAD_DEBOUNCE_MS,
51
51
  log,
52
52
  } from "../../daemon/handlers/shared.js";
53
- import { getAssistantMessageIdsInTurn } from "../../memory/conversation-crud.js";
53
+ import {
54
+ getAssistantMessageIdsInTurn,
55
+ getConversation,
56
+ getMessageById,
57
+ } from "../../memory/conversation-crud.js";
54
58
  import { clearEmbeddingBackendCache } from "../../memory/embedding-backend.js";
55
59
  import {
56
60
  getRequestLogById,
@@ -58,6 +62,7 @@ import {
58
62
  } from "../../memory/llm-request-log-store.js";
59
63
  import { getMemoryRecallLogByMessageIds } from "../../memory/memory-recall-log-store.js";
60
64
  import { getMemoryV2ActivationLogByMessageIds } from "../../memory/memory-v2-activation-log-store.js";
65
+ import { MEMORY_V2_CONSOLIDATION_SOURCE } from "../../memory/v2/constants.js";
61
66
  import { initializeProviders } from "../../providers/registry.js";
62
67
  import { resolvePricingForUsage } from "../../util/pricing.js";
63
68
  import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
@@ -459,6 +464,26 @@ function handleGetMessageContent({
459
464
  return result;
460
465
  }
461
466
 
467
+ const CONVERSATION_KINDS = [
468
+ "user",
469
+ "background",
470
+ "background_memory_consolidation",
471
+ "scheduled",
472
+ ] as const;
473
+ type ConversationKind = (typeof CONVERSATION_KINDS)[number];
474
+
475
+ function resolveConversationKind(
476
+ source: string,
477
+ conversationType: string,
478
+ ): ConversationKind {
479
+ if (source === MEMORY_V2_CONSOLIDATION_SOURCE) {
480
+ return "background_memory_consolidation";
481
+ }
482
+ if (conversationType === "background") return "background";
483
+ if (conversationType === "scheduled") return "scheduled";
484
+ return "user";
485
+ }
486
+
462
487
  function handleGetLlmContext({ pathParams = {} }: RouteHandlerArgs) {
463
488
  const messageId = pathParams.id;
464
489
  if (!messageId) {
@@ -469,8 +494,17 @@ function handleGetLlmContext({ pathParams = {} }: RouteHandlerArgs) {
469
494
  const memoryRecallLog = getMemoryRecallLogByMessageIds(turnMessageIds);
470
495
  const memoryV2Activation =
471
496
  getMemoryV2ActivationLogByMessageIds(turnMessageIds);
497
+ const message = getMessageById(messageId);
498
+ const conversation = message ? getConversation(message.conversationId) : null;
499
+ const conversationKind: ConversationKind = conversation
500
+ ? resolveConversationKind(
501
+ conversation.source,
502
+ conversation.conversationType,
503
+ )
504
+ : "user";
472
505
  return {
473
506
  messageId,
507
+ conversationKind,
474
508
  logs: logs.map((log) => {
475
509
  let requestPayload: unknown;
476
510
  try {
@@ -709,6 +743,7 @@ export const ROUTES: RouteDefinition[] = [
709
743
  tags: ["messages"],
710
744
  responseBody: z.object({
711
745
  messageId: z.string(),
746
+ conversationKind: z.enum(CONVERSATION_KINDS),
712
747
  logs: z.array(z.unknown()),
713
748
  memoryRecall: z.object({}).passthrough().nullable(),
714
749
  memoryV2Activation: z.object({}).passthrough().nullable(),
@@ -44,7 +44,9 @@ import {
44
44
  isWakeUpGreeting,
45
45
  } from "../../daemon/first-greeting.js";
46
46
  import { renderHistoryContent } from "../../daemon/handlers/shared.js";
47
+ import { HostAppControlProxy } from "../../daemon/host-app-control-proxy.js";
47
48
  import { HostCuProxy } from "../../daemon/host-cu-proxy.js";
49
+ import { preactivateHostProxySkills } from "../../daemon/host-proxy-preactivation.js";
48
50
  import type { ServerMessage } from "../../daemon/message-protocol.js";
49
51
  import type {
50
52
  HostProxyTransportMetadata,
@@ -1320,10 +1322,9 @@ export async function handleSendMessage(
1320
1322
 
1321
1323
  // Store pre-chat onboarding context on the conversation when this is the
1322
1324
  // very first message (no prior messages loaded). Artifact persistence
1323
- // (IDENTITY.md, USER.md, sidecar) is deferred: on the canned greeting
1324
- // path it runs inside the setTimeout right before warmPromptCache() so
1325
- // the warmed system prompt includes the identity; on the normal LLM
1326
- // path it runs immediately before inference starts.
1325
+ // (IDENTITY.md, USER.md, sidecar) runs before either the canned greeting
1326
+ // broadcast or normal LLM inference so client-side identity reads observe
1327
+ // the selected assistant name.
1327
1328
  const isFirstOnboarding =
1328
1329
  !!body.onboarding && conversation.messages.length === 0;
1329
1330
  if (isFirstOnboarding) {
@@ -1395,15 +1396,30 @@ export async function handleSendMessage(
1395
1396
  if (!conversation.isProcessing() || !conversation.hostCuProxy) {
1396
1397
  conversation.setHostCuProxy(new HostCuProxy());
1397
1398
  }
1398
- // Only preactivate CU when the conversation is idle — if the conversation is
1399
- // processing, this message will be queued and preactivation is deferred
1400
- // to dequeue time in drainQueueImpl to avoid mutating in-flight turn state.
1401
- if (!conversation.isProcessing()) {
1402
- conversation.addPreactivatedSkillId("computer-use");
1403
- }
1404
1399
  } else if (!conversation.isProcessing()) {
1405
1400
  conversation.setHostCuProxy(undefined);
1406
1401
  }
1402
+ // App-control mirrors CU's per-conversation lifecycle: the proxy owns a
1403
+ // singleton lock plus per-session loop tracking. Instantiation is
1404
+ // unconditional when the client supports the capability — feature-flag
1405
+ // gating lives in the skill-projection layer (which reads the
1406
+ // `feature-flag: app-control` declaration in SKILL.md frontmatter), so
1407
+ // an attached proxy is harmless when the flag resolves to off.
1408
+ if (supportsHostProxy(sourceInterface, "host_app_control")) {
1409
+ if (!conversation.isProcessing() || !conversation.hostAppControlProxy) {
1410
+ conversation.setHostAppControlProxy(
1411
+ new HostAppControlProxy(mapping.conversationId),
1412
+ );
1413
+ }
1414
+ } else if (!conversation.isProcessing()) {
1415
+ conversation.setHostAppControlProxy(undefined);
1416
+ }
1417
+ // Only preactivate when the conversation is idle — if it's processing,
1418
+ // this message will be queued and preactivation is deferred to dequeue
1419
+ // time in drainQueueImpl to avoid mutating in-flight turn state.
1420
+ if (!conversation.isProcessing()) {
1421
+ preactivateHostProxySkills(conversation, sourceInterface);
1422
+ }
1407
1423
  // Wire sendToClient to the SSE hub so all subsystems can reach the HTTP client.
1408
1424
  // hasNoClient must remain `!isInteractive` so downstream tool gating
1409
1425
  // (`isToolActiveForContext` for HOST_TOOL_NAMES, `createToolExecutor`'s
@@ -1473,6 +1489,10 @@ export async function handleSendMessage(
1473
1489
  conversationId,
1474
1490
  };
1475
1491
 
1492
+ if (isFirstOnboarding) {
1493
+ persistOnboardingArtifacts(body.onboarding!);
1494
+ }
1495
+
1476
1496
  setTimeout(() => {
1477
1497
  broadcastMessage({
1478
1498
  type: "user_message_echo",
@@ -1493,9 +1513,6 @@ export async function handleSendMessage(
1493
1513
  "canned-greeting queue drain",
1494
1514
  );
1495
1515
 
1496
- if (isFirstOnboarding) {
1497
- persistOnboardingArtifacts(body.onboarding!);
1498
- }
1499
1516
  conversation.warmPromptCache();
1500
1517
  }, 0);
1501
1518
 
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Markdown to PDF renderer for document export.
3
+ *
4
+ * Converts markdown content to styled HTML via `marked`, then renders
5
+ * the HTML to a PDF buffer using Playwright headless Chromium.
6
+ * The HTML template uses print-friendly styling that matches the
7
+ * document editor typography.
8
+ */
9
+
10
+ import { marked } from "marked";
11
+
12
+ import { importPlaywright } from "../../tools/browser/runtime-check.js";
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Print template
16
+ // ---------------------------------------------------------------------------
17
+
18
+ const FONT_STACK = `"DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif`;
19
+
20
+ function wrapInPrintTemplate(innerHtml: string): string {
21
+ return `<!DOCTYPE html>
22
+ <html>
23
+ <head>
24
+ <meta charset="utf-8">
25
+ <style>
26
+ * { margin: 0; padding: 0; box-sizing: border-box; }
27
+
28
+ body {
29
+ font-family: ${FONT_STACK};
30
+ font-size: 14px;
31
+ line-height: 1.7;
32
+ color: #1a1a1a;
33
+ background: #ffffff;
34
+ padding: 0;
35
+ }
36
+
37
+ h1 { font-size: 28px; font-weight: 600; margin-top: 32px; margin-bottom: 12px; }
38
+ h2 { font-size: 22px; font-weight: 600; margin-top: 28px; margin-bottom: 10px; }
39
+ h3 { font-size: 18px; font-weight: 600; margin-top: 24px; margin-bottom: 8px; }
40
+ h4, h5, h6 { font-size: 16px; font-weight: 600; margin-top: 20px; margin-bottom: 8px; }
41
+
42
+ p {
43
+ margin-bottom: 12px;
44
+ }
45
+
46
+ pre {
47
+ background: #f5f5f5;
48
+ border-radius: 8px;
49
+ padding: 12px 16px;
50
+ overflow-x: auto;
51
+ margin-bottom: 12px;
52
+ }
53
+
54
+ code {
55
+ font-family: "DM Mono", "SF Mono", monospace;
56
+ font-size: 13px;
57
+ background: #f5f5f5;
58
+ border-radius: 4px;
59
+ padding: 2px 5px;
60
+ }
61
+
62
+ pre code {
63
+ background: none;
64
+ padding: 0;
65
+ border-radius: 0;
66
+ }
67
+
68
+ blockquote {
69
+ border-left: 3px solid #6366f1;
70
+ padding-left: 16px;
71
+ margin: 12px 0;
72
+ color: #555555;
73
+ }
74
+
75
+ table {
76
+ width: 100%;
77
+ border-collapse: collapse;
78
+ margin: 12px 0;
79
+ }
80
+
81
+ th, td {
82
+ border: 1px solid #e0e0e0;
83
+ padding: 8px 12px;
84
+ text-align: left;
85
+ }
86
+
87
+ th {
88
+ background: #f5f5f5;
89
+ font-weight: 600;
90
+ }
91
+
92
+ ul, ol {
93
+ margin: 12px 0;
94
+ padding-left: 24px;
95
+ }
96
+
97
+ li {
98
+ margin-bottom: 4px;
99
+ }
100
+
101
+ a {
102
+ color: #6366f1;
103
+ text-decoration: none;
104
+ }
105
+
106
+ hr {
107
+ border: none;
108
+ border-top: 1px solid #e0e0e0;
109
+ margin: 24px 0;
110
+ }
111
+
112
+ img {
113
+ max-width: 100%;
114
+ height: auto;
115
+ }
116
+
117
+ </style>
118
+ </head>
119
+ <body>
120
+ ${innerHtml}
121
+ </body>
122
+ </html>`;
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // Public API
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /**
130
+ * Convert a markdown string to a PDF buffer.
131
+ *
132
+ * Parses markdown to HTML via `marked`, wraps it in a print-friendly
133
+ * template, then renders to PDF using Playwright headless Chromium.
134
+ * The browser is always closed in a `finally` block.
135
+ */
136
+ export async function renderMarkdownToPDF(
137
+ title: string,
138
+ markdown: string,
139
+ ): Promise<Buffer> {
140
+ const innerHtml = marked.parse(markdown, {
141
+ gfm: true,
142
+ breaks: true,
143
+ }) as string;
144
+ const fullHtml = wrapInPrintTemplate(innerHtml);
145
+
146
+ const pw = await importPlaywright();
147
+ const browser = await pw.chromium.launch({ headless: true });
148
+ try {
149
+ const page = await browser.newPage();
150
+ await page.setContent(fullHtml, { waitUntil: "networkidle" });
151
+ const pdfBuffer = await page.pdf({
152
+ format: "A4",
153
+ margin: {
154
+ top: "0.75in",
155
+ bottom: "0.75in",
156
+ left: "0.75in",
157
+ right: "0.75in",
158
+ },
159
+ printBackground: true,
160
+ });
161
+ return Buffer.from(pdfBuffer);
162
+ } finally {
163
+ await browser.close();
164
+ }
165
+ }
@@ -8,8 +8,10 @@ import { z } from "zod";
8
8
 
9
9
  import { rawAll, rawGet, rawRun } from "../../memory/raw-query.js";
10
10
  import { getLogger } from "../../util/logger.js";
11
+ import { renderMarkdownToPDF } from "./document-pdf-renderer.js";
11
12
  import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
12
13
  import type { RouteDefinition } from "./types.js";
14
+ import { RouteResponse } from "./types.js";
13
15
 
14
16
  const log = getLogger("documents-routes");
15
17
 
@@ -312,4 +314,32 @@ export const ROUTES: RouteDefinition[] = [
312
314
  return result;
313
315
  },
314
316
  },
317
+
318
+ {
319
+ operationId: "exportDocumentPDF",
320
+ endpoint: "documents/:id/pdf",
321
+ method: "GET",
322
+ policyKey: "documents",
323
+ requirePolicyEnforcement: true,
324
+ summary: "Export a document as PDF",
325
+ description: "Render a document to PDF and return the binary content.",
326
+ tags: ["documents"],
327
+ handler: async ({ pathParams }) => {
328
+ const result = loadDocument(pathParams!.id);
329
+ if (!result.success) {
330
+ throw new NotFoundError(result.error);
331
+ }
332
+ const pdfBuffer = await renderMarkdownToPDF(result.title, result.content);
333
+ const filename =
334
+ result.title
335
+ .replace(/[^a-zA-Z0-9_-]/g, "-")
336
+ .replace(/-+/g, "-")
337
+ .replace(/^-|-$/g, "") || "document";
338
+ return new RouteResponse(new Uint8Array(pdfBuffer), {
339
+ "Content-Type": "application/pdf",
340
+ "Content-Disposition": `attachment; filename="${filename}.pdf"`,
341
+ "Content-Length": String(pdfBuffer.length),
342
+ });
343
+ },
344
+ },
315
345
  ];
@@ -10,12 +10,27 @@
10
10
  export class RouteError extends Error {
11
11
  readonly code: string;
12
12
  readonly statusCode: number;
13
-
14
- constructor(message: string, code: string, statusCode: number) {
13
+ /**
14
+ * Optional structured payload surfaced to clients in the standard
15
+ * error envelope as `error.details`. Use sparingly — only when the
16
+ * client genuinely needs machine-readable context beyond `code` and
17
+ * `message` (e.g. mirroring a platform-side response shape).
18
+ */
19
+ readonly details?: unknown;
20
+
21
+ constructor(
22
+ message: string,
23
+ code: string,
24
+ statusCode: number,
25
+ details?: unknown,
26
+ ) {
15
27
  super(message);
16
28
  this.name = "RouteError";
17
29
  this.code = code;
18
30
  this.statusCode = statusCode;
31
+ if (details !== undefined) {
32
+ this.details = details;
33
+ }
19
34
  }
20
35
  }
21
36
 
@@ -55,8 +70,8 @@ export class NotFoundError extends RouteError {
55
70
  }
56
71
 
57
72
  export class UnprocessableEntityError extends RouteError {
58
- constructor(message: string) {
59
- super(message, "UNPROCESSABLE_ENTITY", 422);
73
+ constructor(message: string, details?: unknown) {
74
+ super(message, "UNPROCESSABLE_ENTITY", 422, details);
60
75
  this.name = "UnprocessableEntityError";
61
76
  }
62
77
  }
@@ -22,7 +22,10 @@ import type { HostProxyCapability } from "../../channels/types.js";
22
22
  import { parseInterfaceId, supportsHostProxy } from "../../channels/types.js";
23
23
  import { getOrCreateConversation } from "../../memory/conversation-key-store.js";
24
24
  import { getLogger } from "../../util/logger.js";
25
- import { formatSseFrame, formatSseHeartbeat } from "../assistant-event.js";
25
+ import {
26
+ formatSseFrame,
27
+ formatSseHeartbeatWithData,
28
+ } from "../assistant-event.js";
26
29
  import type {
27
30
  AssistantEventCallback,
28
31
  AssistantEventFilter,
@@ -37,8 +40,8 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
37
40
 
38
41
  const log = getLogger("events-routes");
39
42
 
40
- /** Keep-alive comment sent to idle clients every 7 s by default. */
41
- const DEFAULT_HEARTBEAT_INTERVAL_MS = 7_000;
43
+ /** Keep-alive comment sent to idle clients every 5 s by default. */
44
+ const DEFAULT_HEARTBEAT_INTERVAL_MS = 5_000;
42
45
 
43
46
  /**
44
47
  * Stream assistant events as Server-Sent Events.
@@ -58,7 +61,7 @@ const DEFAULT_HEARTBEAT_INTERVAL_MS = 7_000;
58
61
  *
59
62
  * Options (for testing):
60
63
  * hub -- override the event hub (defaults to process singleton).
61
- * heartbeatIntervalMs -- how often to emit keep-alive comments (default 7 s).
64
+ * heartbeatIntervalMs -- how often to emit keep-alive comments (default 5 s).
62
65
  */
63
66
  export function handleSubscribeAssistantEvents(
64
67
  args: RouteHandlerArgs,
@@ -77,6 +80,7 @@ export function handleSubscribeAssistantEvents(
77
80
  // ── Client identity from headers ──────────────────────────────────────
78
81
  const rawClientId = headers?.["x-vellum-client-id"];
79
82
  const rawInterfaceId = headers?.["x-vellum-interface-id"];
83
+ const rawMachineName = headers?.["x-vellum-machine-name"];
80
84
  const clientId = rawClientId?.trim() || null;
81
85
  const interfaceId = clientId
82
86
  ? parseInterfaceId(rawInterfaceId?.trim())
@@ -100,6 +104,7 @@ export function handleSubscribeAssistantEvents(
100
104
  "host_bash",
101
105
  "host_file",
102
106
  "host_cu",
107
+ "host_app_control",
103
108
  "host_browser",
104
109
  ];
105
110
 
@@ -165,6 +170,7 @@ export function handleSubscribeAssistantEvents(
165
170
  capabilities: ALL_CAPABILITIES.filter((cap) =>
166
171
  supportsHostProxy(interfaceId, cap),
167
172
  ),
173
+ machineName: rawMachineName?.trim() || undefined,
168
174
  })
169
175
  : hub.subscribe({
170
176
  ...subscriberBase,
@@ -188,7 +194,7 @@ export function handleSubscribeAssistantEvents(
188
194
  return;
189
195
  }
190
196
 
191
- controller.enqueue(encoder.encode(formatSseHeartbeat()));
197
+ controller.enqueue(encoder.encode(formatSseHeartbeatWithData()));
192
198
 
193
199
  heartbeatTimer = setInterval(() => {
194
200
  try {
@@ -200,7 +206,7 @@ export function handleSubscribeAssistantEvents(
200
206
  if (clientId) {
201
207
  hub.touchClient(clientId);
202
208
  }
203
- controller.enqueue(encoder.encode(formatSseHeartbeat()));
209
+ controller.enqueue(encoder.encode(formatSseHeartbeatWithData()));
204
210
  } catch {
205
211
  sub.dispose();
206
212
  cleanup();